/*
* Copyright 2016 The Simple File Server Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sfs;
import com.fasterxml.jackson.core.JsonFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.google.common.io.BaseEncoding;
import com.google.common.net.HostAndPort;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.shareddata.Shareable;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import org.sfs.auth.AuthProviderService;
import org.sfs.elasticsearch.Elasticsearch;
import org.sfs.encryption.AwsKms;
import org.sfs.encryption.AzureKms;
import org.sfs.encryption.ContainerKeys;
import org.sfs.encryption.MasterKeys;
import org.sfs.filesystem.temp.TempDirectoryCleaner;
import org.sfs.jobs.Jobs;
import org.sfs.nodes.ClusterInfo;
import org.sfs.nodes.NodeStats;
import org.sfs.nodes.Nodes;
import org.sfs.nodes.all.elasticsearch.RefreshIndex;
import org.sfs.nodes.all.stats.GetClusterStats;
import org.sfs.nodes.all.stats.GetNodeStats;
import org.sfs.nodes.compute.account.DeleteAccount;
import org.sfs.nodes.compute.account.GetAccount;
import org.sfs.nodes.compute.account.GetAccountMeta;
import org.sfs.nodes.compute.account.HeadAccount;
import org.sfs.nodes.compute.account.PostAccount;
import org.sfs.nodes.compute.container.DeleteOrDestroyContainer;
import org.sfs.nodes.compute.container.ExportContainer;
import org.sfs.nodes.compute.container.GetContainer;
import org.sfs.nodes.compute.container.GetContainerMeta;
import org.sfs.nodes.compute.container.HeadContainer;
import org.sfs.nodes.compute.container.ImportContainer;
import org.sfs.nodes.compute.container.PostContainer;
import org.sfs.nodes.compute.container.PutContainer;
import org.sfs.nodes.compute.container.VerifyRepairAllContainersExecute;
import org.sfs.nodes.compute.container.VerifyRepairAllContainersStop;
import org.sfs.nodes.compute.container.VerifyRepairAllContainersWait;
import org.sfs.nodes.compute.container.VerifyRepairContainerExecute;
import org.sfs.nodes.compute.container.VerifyRepairContainerStop;
import org.sfs.nodes.compute.container.VerifyRepairContainerWait;
import org.sfs.nodes.compute.containerkeys.ReEncryptContainerKeys;
import org.sfs.nodes.compute.identity.PostTokens;
import org.sfs.nodes.compute.masterkey.ReEncryptMasterKeys;
import org.sfs.nodes.compute.masterkey.VerifyRepairMasterKeys;
import org.sfs.nodes.compute.object.DeleteObject;
import org.sfs.nodes.compute.object.GetObject;
import org.sfs.nodes.compute.object.GetObjectMeta;
import org.sfs.nodes.compute.object.HeadObject;
import org.sfs.nodes.compute.object.PostObject;
import org.sfs.nodes.compute.object.PutObject;
import org.sfs.nodes.compute.object.VerifyRepairObjectExecute;
import org.sfs.nodes.compute.object.VerifyRepairObjectStop;
import org.sfs.nodes.data.AckBlob;
import org.sfs.nodes.data.CanReadVolume;
import org.sfs.nodes.data.CanWriteVolume;
import org.sfs.nodes.data.ChecksumBlob;
import org.sfs.nodes.data.DeleteBlob;
import org.sfs.nodes.data.GetBlob;
import org.sfs.nodes.data.PutBlob;
import org.sfs.nodes.master.MasterNodeExecuteJob;
import org.sfs.nodes.master.MasterNodeStopJob;
import org.sfs.nodes.master.MasterNodeWaitForJob;
import org.sfs.rx.Defer;
import org.sfs.rx.ObservableFuture;
import org.sfs.rx.RxHelper;
import org.sfs.rx.Terminus;
import org.sfs.rx.ToVoid;
import org.sfs.thread.NamedCapacityFixedThreadPool;
import org.sfs.util.ConfigHelper;
import org.sfs.util.FileSystemLock;
import rx.Observable;
import rx.plugins.RxJavaHooks;
import rx.plugins.RxJavaSchedulersHook;
import java.net.HttpURLConnection;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class SfsSingletonServer extends Server implements Shareable {
private static final AtomicBoolean STARTED = new AtomicBoolean(false);
private VertxContext<Server> vertxContext;
private ExecutorService ioPool;
private ExecutorService backgroundPool;
private TempDirectoryCleaner tempDirectoryCleaner = new TempDirectoryCleaner();
private AwsKms awsKms = new AwsKms();
private AzureKms azureKms = new AzureKms();
private MasterKeys masterKeys = new MasterKeys();
private ContainerKeys containerKeys = new ContainerKeys();
private Elasticsearch elasticsearch = new Elasticsearch();
private ClusterInfo clusterInfo = new ClusterInfo();
private NodeStats nodeStats = new NodeStats();
private Nodes nodes;
private Jobs jobs = new Jobs();
private SfsFileSystem sfsFileSystem = new SfsFileSystem();
private JsonFactory jsonFactory = new JsonFactory();
private Set<HostAndPort> parsedListenAddresses = new LinkedHashSet<>();
private Set<HostAndPort> parsedPublishAddresses = new LinkedHashSet<>();
private Set<HostAndPort> clusterHosts = new LinkedHashSet<>();
private List<HttpServer> httpServers = new ArrayList<>();
private int httpServerMaxHeaderSize;
private int httpServerIdleConnectionTimeout;
private int remoteNodeIdleConnectionTimeout;
private boolean started = false;
private Throwable startException = null;
private int remoteNodeMaxPoolSize;
private int remoteNodeConnectTimeout;
private HttpClient httpsClient;
private HttpClient httpClient;
private AuthProviderService authProviderService = new AuthProviderService();
private boolean testMode;
private byte[] remoteNodeSecret;
private static final Logger LOGGER = LoggerFactory.getLogger(SfsSingletonServer.class);
@Override
public void start(final Future<Void> startedResult) {
Preconditions.checkState(STARTED.compareAndSet(false, true), "Only one instance is allowed.");
final Server _this = this;
LOGGER.info("Starting verticle " + _this);
initRxSchedulers(vertx);
JsonObject config = config();
String fsHome = ConfigHelper.getFieldOrEnv(config, "fs.home");
if (fsHome == null) {
fsHome = System.getProperty("user.home");
if (fsHome != null) {
fsHome = Paths.get(fsHome, "data", "sfs").toString();
}
}
config.put("fs.home", fsHome);
LOGGER.info(String.format("Config: %s", config.encodePrettily()));
Path fileSystem = Paths.get(fsHome);
testMode = Boolean.valueOf(ConfigHelper.getFieldOrEnv(config, "test_mode", "false"));
FileSystemLock fileSystemLock = new FileSystemLock(Paths.get(fileSystem.toString(), ".lock"), 60, TimeUnit.SECONDS);
for (String jsonListenAddress : ConfigHelper.getArrayFieldOrEnv(config, "http.listen.addresses", new String[]{})) {
parsedListenAddresses.add(HostAndPort.fromString(jsonListenAddress));
}
for (String jsonPublishAddress : ConfigHelper.getArrayFieldOrEnv(config, "http.publish.addresses", Iterables.transform(parsedListenAddresses, input -> input.toString()))) {
parsedPublishAddresses.add(HostAndPort.fromString(jsonPublishAddress));
}
if (parsedPublishAddresses.isEmpty()) {
parsedPublishAddresses.addAll(parsedListenAddresses);
}
for (String jsonClusterHost : ConfigHelper.getArrayFieldOrEnv(config, "cluster.hosts", Iterables.transform(parsedPublishAddresses, input -> input.toString()))) {
clusterHosts.add(HostAndPort.fromString(jsonClusterHost));
}
// adds itself to the list if the list is empty
if (clusterHosts.isEmpty()) {
clusterHosts.addAll(parsedPublishAddresses);
}
int threadPoolIoQueueSize = new Integer(ConfigHelper.getFieldOrEnv(config, "threadpool.io.queuesize", "10000"));
Preconditions.checkArgument(threadPoolIoQueueSize > 0, "threadpool.io.queuesize must be greater than 0");
int threadPoolIoSize = new Integer(ConfigHelper.getFieldOrEnv(config, "threadpool.io.size", String.valueOf(Runtime.getRuntime().availableProcessors() * 2)));
Preconditions.checkArgument(threadPoolIoSize > 0, "threadpool.io.size must be greater than 0");
int threadPoolBackgroundSize = new Integer(ConfigHelper.getFieldOrEnv(config, "threadpool.background.size", String.valueOf(Runtime.getRuntime().availableProcessors() * 2)));
Preconditions.checkArgument(threadPoolBackgroundSize > 0, "threadpool.background.size must be greater than 0");
int threadPoolBackgroundQueueSize = new Integer(ConfigHelper.getFieldOrEnv(config, "threadpool.background.queuesize", "10000"));
Preconditions.checkArgument(threadPoolBackgroundQueueSize > 0, "threadpool.background.queuesize must be greater than 0");
ioPool = NamedCapacityFixedThreadPool.newInstance(threadPoolIoSize, threadPoolIoQueueSize, "sfs-io-pool");
backgroundPool = NamedCapacityFixedThreadPool.newInstance(threadPoolBackgroundSize, threadPoolBackgroundQueueSize, "sfs-blocking-action-pool");
this.vertxContext = new VertxContext<>(this);
nodes = new Nodes();
httpServerMaxHeaderSize = new Integer(ConfigHelper.getFieldOrEnv(config, "http.maxheadersize", "8192"));
Preconditions.checkArgument(httpServerMaxHeaderSize > 0, "http.maxheadersize must be greater than 0");
httpServerIdleConnectionTimeout = new Integer(ConfigHelper.getFieldOrEnv(config, "http.idleconnectiontimeout", String.valueOf(TimeUnit.MINUTES.toMillis(20))));
Preconditions.checkArgument(httpServerIdleConnectionTimeout > 0, "http.idleconnectiontimeout must be greater than 0");
int nodeStatsRefreshInterval = new Integer(ConfigHelper.getFieldOrEnv(config, "node_stats_refresh_interval", String.valueOf(TimeUnit.SECONDS.toMillis(1))));
Preconditions.checkArgument(nodeStatsRefreshInterval > 0, "node_stats_refresh_interval must be greater than 0");
remoteNodeIdleConnectionTimeout = new Integer(ConfigHelper.getFieldOrEnv(config, "remotenode.idleconnectiontimeout", "30"));
Preconditions.checkArgument(remoteNodeIdleConnectionTimeout > 0, "remotenode.idleconnectiontimeout must be greater than 0");
remoteNodeMaxPoolSize = new Integer(ConfigHelper.getFieldOrEnv(config, "remotenode.maxpoolsize", "25"));
Preconditions.checkArgument(remoteNodeMaxPoolSize > 0, "remotenode.maxpoolsize must be greater than 0");
remoteNodeConnectTimeout = new Integer(ConfigHelper.getFieldOrEnv(config, "remotenode.connectimeout", "30000"));
Preconditions.checkArgument(remoteNodeConnectTimeout > 0, "remotenode.connectimeout must be greater than 0");
int remoteNodeResponseTimeout = new Integer(ConfigHelper.getFieldOrEnv(config, "remotenode.responsetimeout", "30000"));
Preconditions.checkArgument(remoteNodeResponseTimeout > 0, "remotenode.responsetimeout must be greater than 0");
String strRemoteNodeSecret = ConfigHelper.getFieldOrEnv(config, "remotenode.secret");
Preconditions.checkArgument(strRemoteNodeSecret != null, "remotenode.secret is required");
remoteNodeSecret = BaseEncoding.base64().decode(strRemoteNodeSecret);
int numberOfObjectReplicas = new Integer(ConfigHelper.getFieldOrEnv(config, "number_of_object_replicas", "0"));
Preconditions.checkArgument(numberOfObjectReplicas >= 0, "number_of_object_replicas must be greater or equal to 0");
int tempFileTtl = new Integer(ConfigHelper.getFieldOrEnv(config, "temp_file_ttl", "86400000"));
Preconditions.checkArgument(tempFileTtl >= 0, "temp_file_ttl must be greater or equal to 0");
final boolean dataNode = Boolean.valueOf(ConfigHelper.getFieldOrEnv(config, "node.data", "true"));
final boolean masterNode = Boolean.valueOf(ConfigHelper.getFieldOrEnv(config, "node.master", "true"));
this.httpsClient = createHttpClient(vertx, true);
this.httpClient = createHttpClient(vertx, false);
Defer.aVoid()
.flatMap(aVoid -> sfsFileSystem.open(vertxContext, fileSystem))
.flatMap(aVoid -> fileSystemLock.lock(vertxContext))
.flatMap(aVoid -> authProviderService.open(vertxContext))
.flatMap(aVoid -> awsKms.start(vertxContext, config))
.flatMap(aVoid -> azureKms.start(vertxContext, config))
.flatMap(aVoid -> tempDirectoryCleaner.start(vertxContext, tempFileTtl))
.flatMap(aVoid -> elasticsearch.start(vertxContext, config, masterNode))
.flatMap(aVoid ->
nodes.open(vertxContext,
parsedPublishAddresses,
clusterHosts,
remoteNodeMaxPoolSize,
remoteNodeConnectTimeout,
remoteNodeResponseTimeout,
numberOfObjectReplicas,
nodeStatsRefreshInterval,
dataNode,
masterNode))
.flatMap(aVoid -> nodeStats.open(vertxContext))
.flatMap(aVoid -> clusterInfo.open(vertxContext))
.flatMap(aVoid -> masterKeys.start(vertxContext))
.flatMap(aVoid -> containerKeys.start(vertxContext))
.flatMap(aVoid -> jobs.open(vertxContext, config))
.flatMap(aVoid -> initHttpListeners(vertxContext, true))
.doOnNext(httpServers1 -> httpServers.addAll(httpServers))
.subscribe(
o -> {
// do nothing
}, throwable -> {
LOGGER.error("Failed to start verticle " + _this, throwable);
startException = throwable;
started = true;
startedResult.fail(throwable);
}, () -> {
LOGGER.info("Started verticle " + _this);
started = true;
startedResult.complete();
});
}
@Override
public void stop(final Future<Void> stoppedResult) {
final Server _this = this;
LOGGER.info("Stopping verticle " + _this);
Defer.aVoid()
.flatMap(aVoid ->
RxHelper.iterate(vertx, httpServers, httpServer -> {
ObservableFuture<Void> handler = RxHelper.observableFuture();
httpServer.close(handler.toHandler());
return handler
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
})
.map(aVoid1 -> Boolean.TRUE);
}).map(new ToVoid<>()))
.flatMap(aVoid -> {
if (jobs != null) {
return jobs
.close(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
}
return Defer.aVoid();
})
.flatMap(aVoid -> {
if (tempDirectoryCleaner != null) {
return tempDirectoryCleaner
.stop()
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
}
return Defer.aVoid();
})
.flatMap(aVoid -> {
if (containerKeys != null) {
return containerKeys
.stop(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
} else {
return Defer.aVoid();
}
})
.flatMap(aVoid -> {
if (masterKeys != null) {
return masterKeys
.stop(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
} else {
return Defer.aVoid();
}
})
.flatMap(aVoid -> {
if (clusterInfo != null) {
return clusterInfo.close(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
} else {
return Defer.aVoid();
}
})
.flatMap(aVoid -> {
if (nodeStats != null) {
return nodeStats.close(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
} else {
return Defer.aVoid();
}
})
.flatMap(aVoid -> {
if (nodes != null) {
return nodes
.close(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
} else {
return Defer.aVoid();
}
})
.flatMap(aVoid -> {
if (elasticsearch != null) {
return elasticsearch
.stop(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
} else {
return Defer.aVoid();
}
})
.flatMap(aVoid -> {
if (azureKms != null) {
return azureKms
.stop(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
}
return Defer.aVoid();
})
.flatMap(aVoid -> {
if (awsKms != null) {
return awsKms
.stop(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
}
return Defer.aVoid();
})
.flatMap(aVoid -> {
if (authProviderService != null) {
return authProviderService
.close(vertxContext)
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
});
}
return Defer.aVoid();
})
.doOnNext(aVoid -> {
if (ioPool != null) {
ioPool.shutdown();
}
})
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
})
.doOnNext(aVoid -> {
if (backgroundPool != null) {
backgroundPool.shutdown();
}
})
.onErrorResumeNext(throwable -> {
LOGGER.error("Unhandled Exception", throwable);
return Defer.aVoid();
})
.doOnNext(aVoid -> {
for (String envVar : ConfigHelper.getEnvVars()) {
System.out.println("ENV " + envVar);
}
})
.subscribe(
aVoid -> {
// do nothing
},
throwable -> {
STARTED.set(false);
LOGGER.info("Failed to stop verticle " + _this, throwable);
stoppedResult.fail(throwable);
},
() -> {
STARTED.set(false);
LOGGER.info("Stopped verticle " + _this);
stoppedResult.complete();
});
}
@Override
public ExecutorService getIoPool() {
return ioPool;
}
@Override
public ExecutorService getBackgroundPool() {
return backgroundPool;
}
@Override
public VertxContext<Server> vertxContext() {
return vertxContext;
}
public boolean isStarted() {
return started;
}
public Throwable getStartException() {
return startException;
}
@Override
public AuthProviderService authProviderService() {
return authProviderService;
}
@Override
public TempDirectoryCleaner tempFileFactory() {
return tempDirectoryCleaner;
}
@Override
public Elasticsearch elasticsearch() {
return elasticsearch;
}
@Override
public Nodes nodes() {
return nodes;
}
@Override
public Jobs jobs() {
return jobs;
}
@Override
public SfsFileSystem sfsFileSystem() {
return sfsFileSystem;
}
@Override
public JsonFactory jsonFactory() {
return jsonFactory;
}
@Override
public AwsKms awsKms() {
return awsKms;
}
@Override
public AzureKms azureKms() {
return azureKms;
}
@Override
public MasterKeys masterKeys() {
return masterKeys;
}
@Override
public ContainerKeys containerKeys() {
return containerKeys;
}
@Override
public HttpClient httpClient(boolean https) {
return https ? httpsClient : httpClient;
}
@Override
public ClusterInfo getClusterInfo() {
return clusterInfo;
}
@Override
public NodeStats getNodeStats() {
return nodeStats;
}
@Override
public byte[] getRemoteNodeSecret() {
return remoteNodeSecret;
}
public Observable<List<HttpServer>> initHttpListeners(VertxContext<Server> vertxContext, boolean createHttpServer) {
// for each listen address attempt to listen on the supplied port range
// and if successful add to the listener list which will be used
// to initialize the rest of the application
Set<HostAndPort> listeningHostAddresses = new HashSet<>();
List<HttpServer> httpServers = new ArrayList<>();
return RxHelper.iterate(vertx, parsedListenAddresses, hostAndPort -> {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed listen ports " + parsedListenAddresses);
}
if (!listeningHostAddresses.contains(hostAndPort)) {
if (createHttpServer) {
LOGGER.info("Creating http listener on " + hostAndPort);
return createHttpServer(vertxContext, hostAndPort, createRouter(vertxContext))
.onErrorResumeNext(throwable -> {
LOGGER.warn("Failed to start listener " + hostAndPort.toString(), throwable);
return Defer.just(null);
})
.map(httpServer -> {
if (httpServer != null) {
listeningHostAddresses.add(hostAndPort);
httpServers.add(httpServer);
}
return Boolean.TRUE;
});
} else {
LOGGER.debug("Skip creating http listener");
listeningHostAddresses.add(hostAndPort);
return Observable.just(true);
}
}
return Defer.just(true);
})
.map(_continue -> httpServers);
}
protected Observable<HttpServer> createHttpServer(VertxContext<Server> vertxContext, HostAndPort hostAndPort, Router router) {
ObservableFuture<HttpServer> handler = RxHelper.observableFuture();
HttpServerOptions httpServerOptions = new HttpServerOptions()
.setMaxHeaderSize(httpServerMaxHeaderSize)
.setCompressionSupported(false)
.setUsePooledBuffers(true)
.setAcceptBacklog(10000)
.setReuseAddress(true)
.setIdleTimeout(httpServerIdleConnectionTimeout)
.setHandle100ContinueAutomatically(true);
vertxContext.vertx().createHttpServer(httpServerOptions)
.requestHandler(router::accept)
.listen(hostAndPort.getPort(), hostAndPort.getHostText(), handler.toHandler());
return handler;
}
protected HttpClient createHttpClient(Vertx v, boolean https) {
HttpClientOptions httpClientOptions = new HttpClientOptions()
.setConnectTimeout(remoteNodeConnectTimeout)
.setMaxPoolSize(remoteNodeMaxPoolSize)
.setKeepAlive(true)
.setUsePooledBuffers(true)
.setPipelining(false)
.setMaxWaitQueueSize(200)
.setReuseAddress(true)
.setIdleTimeout(remoteNodeIdleConnectionTimeout)
.setSsl(https);
return v.createHttpClient(httpClientOptions);
}
protected Router createRouter(VertxContext<Server> vertxContext) {
Vertx vertx = vertxContext.vertx();
Router router = Router.router(vertx);
router.get("/admin/001/healthcheck").handler(httpServerRequest -> httpServerRequest
.response()
.setStatusCode(HttpURLConnection.HTTP_OK)
.end());
// cluster stats
router.get("/stats").handler(new SfsRequestHandler(vertxContext, new GetClusterStats()));
// object admin method
router.get("/metadata_accounts/*").handler(new SfsRequestHandler(vertxContext, new GetAccountMeta()));
router.get("/metadata_containers/*").handler(new SfsRequestHandler(vertxContext, new GetContainerMeta()));
router.get("/metadata_objects/*").handler(new SfsRequestHandler(vertxContext, new GetObjectMeta()));
//re encrypt
router.post("/reencrypt_masterkeys").handler(new SfsRequestHandler(vertxContext, new ReEncryptMasterKeys()));
router.post("/reencrypt_containerkeys").handler(new SfsRequestHandler(vertxContext, new ReEncryptContainerKeys()));
//repair
router.post("/verify_repair_masterkeys").handler(new SfsRequestHandler(vertxContext, new VerifyRepairMasterKeys()));
router.postWithRegex("\\/verify_repair_objects\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new VerifyRepairObjectExecute()));
router.deleteWithRegex("\\/verify_repair_objects\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new VerifyRepairObjectStop()));
router.getWithRegex("\\/verify_repair_objects\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new VerifyRepairObjectStop()));
router.postWithRegex("\\/verify_repair_containers\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new VerifyRepairContainerExecute()));
router.deleteWithRegex("\\/verify_repair_containers\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new VerifyRepairContainerStop()));
router.getWithRegex("\\/verify_repair_containers\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new VerifyRepairContainerWait()));
router.post("/verify_repair_containers").handler(new SfsRequestHandler(vertxContext, new VerifyRepairAllContainersExecute()));
router.delete("/verify_repair_containers").handler(new SfsRequestHandler(vertxContext, new VerifyRepairAllContainersStop()));
router.get("/verify_repair_containers").handler(new SfsRequestHandler(vertxContext, new VerifyRepairAllContainersWait()));
// container admin method
router.post("/export_container/*").handler(new SfsRequestHandler(vertxContext, new ExportContainer()));
router.post("/import_container/*").handler(new SfsRequestHandler(vertxContext, new ImportContainer()));
// openstack keystone
router.post("/v2.0/tokens").handler(new SfsRequestHandler(vertxContext, new PostTokens()));
// openstack swift object methods
router.getWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new GetObject()));
router.headWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new HeadObject()));
router.deleteWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new DeleteObject()));
router.putWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new PutObject()));
router.postWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+\\/.+").handler(new SfsRequestHandler(vertxContext, new PostObject()));
// openstack swift container methods
router.getWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new GetContainer()));
router.headWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new HeadContainer()));
router.putWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new PutContainer()));
router.postWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new PostContainer()));
router.deleteWithRegex("\\/openstackswift001\\/[^\\/]+\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new DeleteOrDestroyContainer()));
// openstack swift account methods
router.getWithRegex("\\/openstackswift001\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new GetAccount()));
router.headWithRegex("\\/openstackswift001\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new HeadAccount()));
router.postWithRegex("\\/openstackswift001\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new PostAccount()));
router.deleteWithRegex("\\/openstackswift001\\/[^\\/]+").handler(new SfsRequestHandler(vertxContext, new DeleteAccount()));
// node specific calls below. ie. A call to any node that requires a master will get routed to the master node
// commands that must run on the master node
router.post("/_internal_node_master_execute_job").handler(new SfsRequestHandler(vertxContext, new MasterNodeExecuteJob()));
router.post("/_internal_node_master_wait_for_job").handler(new SfsRequestHandler(vertxContext, new MasterNodeWaitForJob()));
router.post("/_internal_node_master_stop_job").handler(new SfsRequestHandler(vertxContext, new MasterNodeStopJob()));
// node stats methods
router.get("/_internal_node/stats").handler(new SfsRequestHandler(vertxContext, new GetNodeStats()));
// data node blob store methods
router.delete("/_internal_node_data/blob").handler(new SfsRequestHandler(vertxContext, new DeleteBlob()));
router.get("/_internal_node_data/blob").handler(new SfsRequestHandler(vertxContext, new GetBlob()));
router.put("/_internal_node_data/blob").handler(new SfsRequestHandler(vertxContext, new PutBlob()));
router.get("/_internal_node_data/blob/canwrite").handler(new SfsRequestHandler(vertxContext, new CanWriteVolume()));
router.get("/_internal_node_data/blob/canread").handler(new SfsRequestHandler(vertxContext, new CanReadVolume()));
router.put("/_internal_node_data/blob/ack").handler(new SfsRequestHandler(vertxContext, new AckBlob()));
router.get("/_internal_node_data/blob/checksum").handler(new SfsRequestHandler(vertxContext, new ChecksumBlob()));
if (testMode) {
router.post("/admin/001/refresh_index").handler(new SfsRequestHandler(vertxContext, new RefreshIndex()));
router.get("/admin/001/is_online").handler(new SfsRequestHandler(vertxContext,
sfsRequest ->
Defer.aVoid()
.flatMap(aVoid1 -> sfsRequest.vertxContext().verticle().getClusterInfo().isOnline())
.doOnNext(isOnline -> {
if (Boolean.TRUE.equals(isOnline)) {
sfsRequest.response().setStatusCode(HttpURLConnection.HTTP_OK);
} else {
sfsRequest.response().setStatusCode(HttpURLConnection.HTTP_NO_CONTENT);
}
})
.subscribe(new Terminus<Boolean>(sfsRequest) {
@Override
public void onNext(Boolean aBoolean) {
}
})));
}
return router;
}
protected static class SfsRequestHandler implements Handler<RoutingContext> {
private final VertxContext<Server> vertxContext;
private final Handler<SfsRequest> delegate;
public SfsRequestHandler(VertxContext<Server> vertxContext, Handler<SfsRequest> delegate) {
this.vertxContext = vertxContext;
this.delegate = delegate;
}
@Override
public void handle(RoutingContext routingContext) {
routingContext.addBodyEndHandler(event -> routingContext.request().resume());
SfsRequest sfsRequest = new SfsRequest(vertxContext, routingContext.request());
delegate.handle(sfsRequest);
}
}
public void initRxSchedulers(Vertx vertx) {
RxJavaSchedulersHook hook = RxHelper.schedulerHook(vertx);
RxJavaHooks.setOnIOScheduler(f -> hook.getIOScheduler());
RxJavaHooks.setOnNewThreadScheduler(f -> hook.getNewThreadScheduler());
RxJavaHooks.setOnComputationScheduler(f -> hook.getComputationScheduler());
}
}