/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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.apache.geode.management.internal;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import org.apache.logging.log4j.Logger;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;
import org.apache.geode.GemFireConfigException;
import org.apache.geode.internal.admin.SSLConfig;
import org.apache.geode.internal.lang.StringUtils;
import org.apache.geode.internal.logging.LogService;
/**
* @since GemFire 8.1
*/
@SuppressWarnings("unused")
public class JettyHelper {
private static final Logger logger = LogService.getLogger();
private static final String FILE_PATH_SEPARATOR = System.getProperty("file.separator");
private static final String USER_DIR = System.getProperty("user.dir");
private static final String USER_NAME = System.getProperty("user.name");
private static final String HTTPS = "https";
private static String bindAddress = "0.0.0.0";
private static int port = 0;
public static Server initJetty(final String bindAddress, final int port, SSLConfig sslConfig) {
final Server jettyServer = new Server();
// Add a handler collection here, so that each new context adds itself
// to this collection.
jettyServer.setHandler(new HandlerCollection());
ServerConnector connector = null;
HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.setSecureScheme(HTTPS);
httpConfig.setSecurePort(port);
if (sslConfig.isEnabled()) {
SslContextFactory sslContextFactory = new SslContextFactory();
if (!StringUtils.isBlank(sslConfig.getAlias())) {
sslContextFactory.setCertAlias(sslConfig.getAlias());
}
sslContextFactory.setNeedClientAuth(sslConfig.isRequireAuth());
if (!StringUtils.isBlank(sslConfig.getCiphers())
&& !"any".equalsIgnoreCase(sslConfig.getCiphers())) {
// If use has mentioned "any" let the SSL layer decide on the ciphers
sslContextFactory.setIncludeCipherSuites(SSLUtil.readArray(sslConfig.getCiphers()));
}
String protocol = SSLUtil.getSSLAlgo(SSLUtil.readArray(sslConfig.getProtocols()));
if (protocol != null) {
sslContextFactory.setProtocol(protocol);
} else {
logger.warn(ManagementStrings.SSL_PROTOCOAL_COULD_NOT_BE_DETERMINED);
}
if (StringUtils.isBlank(sslConfig.getKeystore())) {
throw new GemFireConfigException(
"Key store can't be empty if SSL is enabled for HttpService");
}
sslContextFactory.setKeyStorePath(sslConfig.getKeystore());
if (!StringUtils.isBlank(sslConfig.getKeystoreType())) {
sslContextFactory.setKeyStoreType(sslConfig.getKeystoreType());
}
if (!StringUtils.isBlank(sslConfig.getKeystorePassword())) {
sslContextFactory.setKeyStorePassword(sslConfig.getKeystorePassword());
}
if (!StringUtils.isBlank(sslConfig.getTruststore())) {
sslContextFactory.setTrustStorePath(sslConfig.getTruststore());
}
if (!StringUtils.isBlank(sslConfig.getTruststorePassword())) {
sslContextFactory.setTrustStorePassword(sslConfig.getTruststorePassword());
}
httpConfig.addCustomizer(new SecureRequestCustomizer());
// Somehow With HTTP_2.0 Jetty throwing NPE. Need to investigate further whether all GemFire
// web application(Pulse, REST) can do with HTTP_1.1
connector = new ServerConnector(jettyServer,
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(httpConfig));
connector.setPort(port);
} else {
connector = new ServerConnector(jettyServer, new HttpConnectionFactory(httpConfig));
connector.setPort(port);
}
jettyServer.setConnectors(new Connector[] {connector});
if (!StringUtils.isBlank(bindAddress)) {
connector.setHost(bindAddress);
}
if (bindAddress != null && !bindAddress.isEmpty()) {
JettyHelper.bindAddress = bindAddress;
}
JettyHelper.port = port;
return jettyServer;
}
public static Server startJetty(final Server jetty) throws Exception {
jetty.start();
return jetty;
}
public static Server addWebApplication(final Server jetty, final String webAppContext,
final String warFilePath) {
WebAppContext webapp = new WebAppContext();
webapp.setContextPath(webAppContext);
webapp.setWar(warFilePath);
webapp.setParentLoaderPriority(false);
webapp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
File tmpPath = new File(getWebAppBaseDirectory(webAppContext));
tmpPath.mkdirs();
webapp.setTempDirectory(tmpPath);
((HandlerCollection) jetty.getHandler()).addHandler(webapp);
return jetty;
}
private static String getWebAppBaseDirectory(final String context) {
String underscoredContext = context.replace("/", "_");
final String workingDirectory = USER_DIR.concat(FILE_PATH_SEPARATOR)
.concat("GemFire_" + USER_NAME).concat(FILE_PATH_SEPARATOR).concat("services")
.concat(FILE_PATH_SEPARATOR).concat("http").concat(FILE_PATH_SEPARATOR)
.concat((StringUtils.isBlank(bindAddress)) ? "0.0.0.0" : bindAddress).concat("_")
.concat(String.valueOf(port).concat(underscoredContext));
return workingDirectory;
}
private static final CountDownLatch latch = new CountDownLatch(1);
private static String normalizeWebAppArchivePath(final String webAppArchivePath) {
return (webAppArchivePath.startsWith(File.separator) ? new File(webAppArchivePath)
: new File(".", webAppArchivePath)).getAbsolutePath();
}
private static String normalizeWebAppContext(final String webAppContext) {
return (webAppContext.startsWith("/") ? webAppContext : "/" + webAppContext);
}
public static void main(final String... args) throws Exception {
if (args.length > 1) {
System.out.printf("Temporary Directory @ ($1%s)%n", USER_DIR);
final Server jetty = JettyHelper.initJetty(null, 8090, new SSLConfig());
for (int index = 0; index < args.length; index += 2) {
final String webAppContext = args[index];
final String webAppArchivePath = args[index + 1];
JettyHelper.addWebApplication(jetty, normalizeWebAppContext(webAppContext),
normalizeWebAppArchivePath(webAppArchivePath));
}
JettyHelper.startJetty(jetty);
latch.await();
} else {
System.out.printf(
"usage:%n>java org.apache.geode.management.internal.TomcatHelper <web-app-context> <war-file-path> [<web-app-context> <war-file-path>]*");
}
}
}