RestServiceInitializer.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.protocol.http.restv2;
import io.cdap.http.ChannelPipelineModifier;
import io.cdap.http.NettyHttpService;
import io.cdap.http.SSLConfig;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.cors.CorsConfig;
import io.netty.handler.codec.http.cors.CorsConfigBuilder;
import io.netty.handler.codec.http.cors.CorsHandler;
import org.waarp.common.crypto.ssl.WaarpSecureKeyStore;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.gateway.kernel.rest.RestConfiguration;
import org.waarp.gateway.kernel.rest.RestConfiguration.CRUD;
import org.waarp.openr66.protocol.configuration.Configuration;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.AbstractRestDbHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.HostConfigHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.HostIdHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.HostsHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.LimitsHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.RuleIdHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.RulesHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.ServerHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.SpooledHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.TransferIdHandler;
import org.waarp.openr66.protocol.http.restv2.dbhandlers.TransfersHandler;
import org.waarp.openr66.protocol.http.restv2.resthandlers.RestExceptionHandler;
import org.waarp.openr66.protocol.http.restv2.resthandlers.RestHandlerHook;
import org.waarp.openr66.protocol.http.restv2.resthandlers.RestSignatureHandler;
import org.waarp.openr66.protocol.http.restv2.resthandlers.RestVersionHandler;
import org.waarp.openr66.protocol.networkhandler.ssl.NetworkSslServerInitializer;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
import static io.netty.handler.codec.http.HttpMethod.*;
import static org.waarp.openr66.protocol.http.rest.HttpRestR66Handler.RESTHANDLERS.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
/**
* This class is called to initialize the RESTv2 API.
*/
public final class RestServiceInitializer {
private static final String ROUTER = "router";
/**
* The logger for all unexpected errors during the service initialization.
*/
private static final WaarpLogger logger =
WaarpLoggerFactory.getLogger(RestServiceInitializer.class);
/**
* This is the {@link NettyHttpService} in charge of handling the RESTv2
* API.
*/
private static NettyHttpService restService;
/**
* This is a static class that should never be instantiated with a
* constructor.
*/
private RestServiceInitializer() {
throw new UnsupportedOperationException(
getClass().getName() + " cannot be instantiated.");
}
/**
* The list of all {@link AbstractRestDbHandler} used by the API.
*/
public static final Collection<AbstractRestDbHandler> handlers =
new ArrayList<AbstractRestDbHandler>();
/**
* Fills the list of {@link AbstractRestDbHandler} with all handlers
* activated in the API configuration.
*
* @param config The REST API configuration object.
*/
private static void initHandlers(final RestConfiguration config) {
final byte hostsCRUD = config.getResthandlersCrud()[DbHostAuth.ordinal()];
final byte rulesCRUD = config.getResthandlersCrud()[DbRule.ordinal()];
final byte transferCRUD =
config.getResthandlersCrud()[DbTaskRunner.ordinal()];
final byte spooledCRUD = CRUD.READ.mask;
final byte configCRUD =
config.getResthandlersCrud()[DbHostConfiguration.ordinal()];
final byte limitCRUD = config.getResthandlersCrud()[Bandwidth.ordinal()];
final int serverCRUD = config.getResthandlersCrud()[Business.ordinal()] +
config.getResthandlersCrud()[Config.ordinal()] +
config.getResthandlersCrud()[Information.ordinal()] +
config.getResthandlersCrud()[Log.ordinal()] +
config.getResthandlersCrud()[Server.ordinal()] +
config.getResthandlersCrud()[Control.ordinal()];
if (hostsCRUD != 0) {
handlers.add(new HostsHandler(hostsCRUD));
handlers.add(new HostIdHandler(hostsCRUD));
}
if (rulesCRUD != 0) {
handlers.add(new RulesHandler(rulesCRUD));
handlers.add(new RuleIdHandler(rulesCRUD));
}
if (transferCRUD != 0) {
handlers.add(new TransfersHandler(transferCRUD));
handlers.add(new TransferIdHandler(transferCRUD));
}
if (configCRUD != 0) {
handlers.add(new HostConfigHandler(configCRUD));
}
if (limitCRUD != 0) {
handlers.add(new LimitsHandler(limitCRUD));
}
if (serverCRUD != 0) {
handlers.add(new ServerHandler(config.getResthandlersCrud()));
}
if (spooledCRUD != 0) {
handlers.add(new SpooledHandler(spooledCRUD));
}
}
/**
* Builds and returns a {@link CorsConfig} to be used by the {@link
* CorsHandler} to allow the REST API to
* support CORS.
*
* @return The configuration used for dealing with CORS requests.
*/
private static CorsConfig corsConfig() {
final CorsConfigBuilder builder = CorsConfigBuilder.forAnyOrigin();
builder.exposeHeaders(ALLOW, "transferURI", "hostURI", "ruleURI");
builder.allowedRequestHeaders(AUTHORIZATION, AUTH_USER, AUTH_TIMESTAMP,
AUTH_SIGNATURE, CONTENT_TYPE);
builder.allowedRequestMethods(GET, POST, PUT, DELETE, OPTIONS);
builder.maxAge(600);
return builder.build();
}
/**
* Initializes the RESTv2 service with the given {@link RestConfiguration}.
*
* @param config The REST API configuration object.
*/
public static void initRestService(final RestConfiguration config) {
initHandlers(config);
final NettyHttpService.Builder restServiceBuilder =
NettyHttpService.builder("R66_RESTv2").setPort(config.getRestPort())
.setHost(config.getRestAddress())
.setExecThreadPoolSize(20).setHttpChunkLimit(
Configuration.configuration.getMaxGlobalMemory())
.setChannelConfig(ChannelOption.SO_REUSEADDR, true)
.setChildChannelConfig(ChannelOption.TCP_NODELAY, false)
.setChildChannelConfig(ChannelOption.SO_REUSEADDR, true)
.setHttpHandlers(handlers).setHandlerHooks(
Collections.singleton(
new RestHandlerHook(config.isRestAuthenticated(),
config.getHmacSha256(),
config.getRestTimeLimit())))
.setExceptionHandler(new RestExceptionHandler())
.setExecThreadKeepAliveSeconds(-1L)
.setChannelPipelineModifier(
new ChannelPipelineModifier() {
@Override
public final void modify(
final ChannelPipeline channelPipeline) {
channelPipeline.addBefore(ROUTER, "aggregator",
new HttpObjectAggregator(
Configuration.configuration.getMaxGlobalMemory()));
channelPipeline.addBefore(ROUTER,
RestVersionHandler.HANDLER_NAME,
new RestVersionHandler(
config));
channelPipeline.addBefore(
RestVersionHandler.HANDLER_NAME, "cors",
new CorsHandler(corsConfig()));
if (config.isRestAuthenticated() &&
config.isRestSignature()) {
channelPipeline.addAfter(ROUTER, "signature",
new RestSignatureHandler(
config.getHmacSha256()));
}
// Removes the HTTP compressor which causes problems
// on systems running java6 or earlier
final double JRE_version = Double.parseDouble(
System.getProperty(
"java.specification.version"));
if (JRE_version <= 1.6) {
logger.info(
"Removed REST HTTP compressor due to incompatibility " +
"with the Java Runtime version");
channelPipeline.remove("compressor");
}
}
});
if (config.isRestSsl()) {
final WaarpSecureKeyStore keyStore =
NetworkSslServerInitializer.getWaarpSecureKeyStore();
final String keyStoreFilename = keyStore.getKeyStoreFilename();
final String keyStorePass = new String(keyStore.getKeyStorePassword());
final String certificatePassword =
new String(keyStore.getCertificatePassword());
restServiceBuilder.enableSSL(
SSLConfig.builder(new File(keyStoreFilename), keyStorePass)
.setCertificatePassword(certificatePassword).build());
}
restService = restServiceBuilder.build();
try {
restService.start();
} catch (final Throwable t) {
logger.error(t);
throw new ExceptionInInitializerError(
"FATAL ERROR_TASK : Failed to initialize RESTv2 service");
}
}
/**
* Stops the REST service.
*/
public static void stopRestService() {
if (restService != null) {
try {
restService.stop();
} catch (final Throwable e) {
logger.error("Exception caught during RESTv2 service shutdown", e);
}
} else {
logger.warn("Error RESTv2 service is not running, cannot stop");
}
}
}