/**
* Copyright (c) Members of the EGEE Collaboration. 2006-2009.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* 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.glite.authz.pap.server.standalone;
import java.security.Security;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.glite.authz.pap.common.PAPConfiguration;
import org.glite.authz.pap.common.exceptions.PAPException;
import org.glite.security.trustmanager.ContextWrapper;
import org.glite.security.util.CaseInsensitiveProperties;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.thread.concurrent.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The standalone PAP daemon
*/
public final class PAPServer {
/**
*
* Useful defaults for the standalone service
*
*/
final class PAPStandaloneServiceDefaults {
/**
* The service hostname
*/
static final String HOSTNAME = "localhost";
/**
* The service port on which the pap will listen to request
*/
static final int PORT = 8150;
/**
* The shutdown service port
*/
static final int SHUTDOWN_PORT = 8151;
/**
* Max request queue size. -1 means unbounded queue is used.
*/
static final int MAX_REQUEST_QUEUE_SIZE = -1;
/**
* Max concurrent connections.
*/
static final int MAX_CONNECTIONS = 64;
/**
* Default certificate path for the service
*/
static final String CERTIFICATE_PATH = "/etc/grid-security/hostcert.pem";
/**
* Default private key path for the service
*/
static final String PRIVATE_KEY_PATH = "/etc/grid-security/hostkey.pem";
/**
* Default path for CA certificates
*/
static final String CA_PATH = "/etc/grid-security/certificates";
/**
* Should CRLs be checked by trustmanager?
*/
static final boolean CRL_ENABLED = true;
/**
* How frequently the PAP should update CRLs, CAs and namespaces from the filesystem.
* The interval is defined as a string with the following format:
* <code>N{s,m,h,d}</code>
*
* where N in the number of either (s=seconds, m=minutes, h=hours, d=days).
*
* Example: 30m
*
* which means 30 minutes.
*
* The default is 30 minutes.
*/
static final String CRL_UPDATE_INTERVAL = "30m";
/**
* The directory containing all the CA certificates, CRLs and namespace definitions.
*/
static final String TRUST_STORE_DIR = "/etc/grid-security/certificates";
}
private static final String DEFAULT_WAR_LOCATION = System
.getProperty("PAP_HOME")
+ "/wars/pap.war";
private static final Logger log = LoggerFactory.getLogger(PAPServer.class);
/**
* The option name used by callers to specify where the pap server should
* look for the configuration
*/
private static final String CONF_DIR_OPTION_NAME = "conf-dir";
/**
* The pap configuration directory
*/
protected String papConfigurationDir;
/**
* The pap jetty http server
*/
protected Server papServer;
/**
* The jetty webapp context in which the pap wep application is configured
*/
private WebAppContext webappContext;
/**
* Constructor. Parses the configuration and starts the server.
*
* @param args
* . the command line arguments as passed by the
* {@link #main(String[])} method
*/
public PAPServer(String[] args) {
try {
parseOptions(args);
Security.addProvider(new BouncyCastleProvider());
PAPConfiguration.initialize(papConfigurationDir);
configurePAPServer();
papServer.start();
if (webappContext.getUnavailableException() != null)
throw webappContext.getUnavailableException();
papServer.join();
} catch (Throwable e) {
log
.error("PAP encountered an error that could not be dealt with, shutting down!");
log.error(e.getMessage());
// Also print error message to standard error
System.err
.println("PAP encountered an error that could not be dealt with, shutting down!");
System.err.println("Error: " + e.getMessage());
e.printStackTrace(System.err);
if (log.isDebugEnabled())
log.error(e.getMessage(), e);
try {
papServer.stop();
} catch (Exception e1) {
// Just ignore this
}
System.exit(1);
}
}
/**
* Utility method to map pap configuration property names to
* glite-security-trustmanager property names
*
* @return the trustmanager properties
*/
private Properties buildTrustmanagerConfiguration() {
Properties tmProps = new Properties();
tmProps.setProperty("sslCertFile", getStringFromSecurityConfiguration(
"certificate", PAPStandaloneServiceDefaults.CERTIFICATE_PATH));
tmProps.setProperty("sslKey", getStringFromSecurityConfiguration(
"private_key", PAPStandaloneServiceDefaults.PRIVATE_KEY_PATH));
tmProps.setProperty("crlEnabled", getStringFromSecurityConfiguration(
"crl_enabled", String
.valueOf(PAPStandaloneServiceDefaults.CRL_ENABLED)));
tmProps.setProperty("crlUpdateInterval", getStringFromSecurityConfiguration("crl_update_interval", PAPStandaloneServiceDefaults.CRL_UPDATE_INTERVAL));
tmProps.setProperty("trustStoreDir", getStringFromSecurityConfiguration("trust_store_dir", String
.valueOf(PAPStandaloneServiceDefaults.TRUST_STORE_DIR)));
return tmProps;
}
private Connector configureTMConnector(String host, int port){
CaseInsensitiveProperties props = new CaseInsensitiveProperties(buildTrustmanagerConfiguration());
try {
ContextWrapper context = new ContextWrapper(props, false);
JettySslSelectChannelConnector connector = new JettySslSelectChannelConnector(context.getKeyManager(),context.m_trustmanager);
connector.setPort(port);
connector.setHost(host);
connector.setWantClientAuth(true);
connector.setNeedClientAuth(true);
log.info("PAP service will listen on {}:{}",
new Object[] { host, port });
return connector;
} catch (Exception e) {
log.error("Error initializing trustmanager connector: "+e.getMessage());
if (log.isDebugEnabled())
log.error("Error initializing trustmanager connector: "+e.getMessage(),e);
throw new PAPException(e);
}
}
/**
* Performs the jetty server configuration
*/
private void configurePAPServer() {
log.info("Configuring jetty PAP server...");
papServer = new Server();
int maxRequestQueueSize = getIntFromStandaloneConfiguration(
"max_request_queue_size",
PAPStandaloneServiceDefaults.MAX_REQUEST_QUEUE_SIZE);
log.debug("maxRequestQueueSize = {}", maxRequestQueueSize);
int maxConnections = getIntFromStandaloneConfiguration(
"max_connections", PAPStandaloneServiceDefaults.MAX_CONNECTIONS);
if (maxConnections <= 0) {
log
.error("Please specify a positive value for the 'maxConnections' configuration parameter!");
log.error("Will use the hardcoded default '{}' instead...",
PAPStandaloneServiceDefaults.MAX_CONNECTIONS);
maxConnections = PAPStandaloneServiceDefaults.MAX_CONNECTIONS;
}
log.info("maxConnections = {}", maxConnections);
papServer.setSendServerVersion(false);
papServer.setSendDateHeader(false);
BlockingQueue<Runnable> requestQueue;
if (maxRequestQueueSize < 1) {
requestQueue = new LinkedBlockingQueue<Runnable>();
} else {
requestQueue = new ArrayBlockingQueue<Runnable>(maxRequestQueueSize);
}
ThreadPool threadPool = new ThreadPool(5, maxConnections, 60,
TimeUnit.SECONDS, requestQueue);
papServer.setThreadPool(threadPool);
// Connectors configuration
int port = getIntFromStandaloneConfiguration("port",
PAPStandaloneServiceDefaults.PORT);
String host = getStringFromStandaloneConfiguration("hostname",
PAPStandaloneServiceDefaults.HOSTNAME);
papServer.setConnectors(new Connector[] { configureTMConnector(host,port) });
JettyShutdownCommand papShutdownCommand = new JettyShutdownCommand(
papServer);
PapShutdownAndStatusService.startPAPShutdownAndStatusService(8151, Collections
.singletonList((Runnable) papShutdownCommand));
webappContext = new WebAppContext();
webappContext.setContextPath("/"
+ PAPConfiguration.DEFAULT_WEBAPP_CONTEXT);
webappContext.setWar(DEFAULT_WAR_LOCATION);
webappContext.setParentLoaderPriority(true);
HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers(new Handler[] { webappContext,
new DefaultHandler() });
papServer.setHandler(handlers);
}
/**
* Utility method to fetch an int configuration parameter out of the
* standalone-service configuration
*
* @param key
* , the configuration parameter key
* @param defaultValue
* , a default value in case the parameter is not defined
* @return the configuration parameter value
*/
private int getIntFromStandaloneConfiguration(String key, int defaultValue) {
PAPConfiguration conf = PAPConfiguration.instance();
return conf.getInt(PAPConfiguration.STANDALONE_SERVICE_STANZA + "."
+ key, defaultValue);
}
/**
* Utility method to fetch a string configuration parameter out of the
* security configuration
*
* @param key
* , the configuration parameter key
* @param defaultValue
* , a default value in case the parameter is not defined
* @return the configuration parameter value
*
*/
private String getStringFromSecurityConfiguration(String key,
String defaultValue) {
PAPConfiguration conf = PAPConfiguration.instance();
return conf.getString(PAPConfiguration.SECURITY_STANZA + "." + key,
defaultValue);
}
/**
* Utility method to fetch a string configuration parameter out of the
* security configuration
*
* @param key
* , the configuration parameter key
* @param defaultValue
* , a default value in case the parameter is not defined
* @return the configuration parameter value
*/
private String getStringFromStandaloneConfiguration(String key,
String defaultValue) {
PAPConfiguration conf = PAPConfiguration.instance();
return conf.getString(PAPConfiguration.STANDALONE_SERVICE_STANZA + "."
+ key, defaultValue);
}
/**
* Parses command line options
*
* @param args
* , the command line options
*/
protected void parseOptions(String[] args) {
if (args.length > 0) {
int currentArg = 0;
while (currentArg < args.length) {
if (!args[currentArg].startsWith("--")) {
usage();
} else if (args[currentArg].equalsIgnoreCase("--"
+ CONF_DIR_OPTION_NAME)) {
papConfigurationDir = args[currentArg + 1];
log.info("Starting PAP with configuration dir: {}",
papConfigurationDir);
currentArg = currentArg + 2;
} else
usage();
}
}
}
/**
* Prints a usage message and exits with status 1
*/
private void usage() {
String usage = "PAPServer [--" + CONF_DIR_OPTION_NAME + " <confDir>]";
System.out.println(usage);
System.exit(1);
}
/**
* Runs the service
*
* @param args
* , the command line arguments
*/
public static void main(String[] args) {
new PAPServer(args);
}
}