package org.robotninjas.barge.jaxrs;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.Binder;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.robotninjas.barge.ClusterConfig;
import org.robotninjas.barge.StateMachine;
import org.robotninjas.barge.state.Raft;
import org.robotninjas.barge.state.RaftProtocolListener;
import org.robotninjas.barge.state.StateTransitionListener;
import org.robotninjas.barge.utils.Files;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
/**
*/
public class RaftApplication {
private static final Logger logger = LoggerFactory.getLogger(RaftJdkServer.class);
private final int serverIndex;
private final URI[] uris;
private final File logDir;
private final List<StateTransitionListener> transitionListeners;
private final List<RaftProtocolListener> protocolListeners;
private Optional<Injector> injector = Optional.absent();
public RaftApplication(int serverIndex, URI[] uris, File logDir, Iterable<StateTransitionListener> transitionListener, Iterable<RaftProtocolListener> protocolListener) {
this.serverIndex = serverIndex;
this.uris = uris;
this.logDir = logDir;
this.transitionListeners = Lists.newArrayList(transitionListener);
this.protocolListeners = Lists.newArrayList(protocolListener);
}
public RaftApplication(int serverIndex, URI[] uris, File logDir) {
this(serverIndex,uris,logDir, Collections.<StateTransitionListener>emptyList(),Collections.<RaftProtocolListener>emptyList());
}
public ResourceConfig makeResourceConfig() {
ClusterConfig clusterConfig = HttpClusterConfig.from(new HttpReplica(uris[serverIndex]),
remotes());
if (!logDir.exists() && !logDir.mkdirs())
logger.warn("failed to create directories for storing logs, bad things will happen");
StateMachine stateMachine = new StateMachine() {
int i = 0;
@Override
public Object applyOperation(@Nonnull ByteBuffer entry) {
return i++;
}
};
final JaxRsRaftModule raftModule = new JaxRsRaftModule(clusterConfig, logDir, stateMachine, 1500, transitionListeners, protocolListeners);
injector = Optional.of(Guice.createInjector(raftModule));
ResourceConfig resourceConfig = new ResourceConfig();
Binder binder = new AbstractBinder() {
@Override
protected void configure() {
bindFactory(new Factory<Raft>() {
@Override
public Raft provide() {
return injector.get().getInstance(Raft.class);
}
@Override
public void dispose(Raft raft) {
}
}).to(Raft.class);
bindFactory(new Factory<ClusterConfig>() {
@Override public ClusterConfig provide() {
return injector.get().getInstance(ClusterConfig.class);
}
@Override public void dispose(ClusterConfig instance) {
}
}).to(ClusterConfig.class);
}
};
resourceConfig.register(BargeResource.class);
resourceConfig.register(Jackson.customJacksonProvider());
resourceConfig.register(binder);
return resourceConfig;
}
private HttpReplica[] remotes() {
HttpReplica[] remoteReplicas = new HttpReplica[uris.length - 1];
for (int i = 0; i < remoteReplicas.length; i++) {
remoteReplicas[i] = new HttpReplica(uris[(serverIndex + i + 1) % uris.length]);
}
return remoteReplicas;
}
public void clean() throws IOException {
Files.delete(logDir);
}
public void stop() {
injector.transform(new Function<Injector, Object>() {
@Nullable
@Override
public Object apply(@Nullable Injector input) {
Raft instance = null;
if (input != null) {
instance = input.getInstance(Raft.class);
instance.stop();
}
return instance;
}
});
}
}