blob: 23ca46930037d53fd5d0dc0907d279d0f4b010f6 [file] [log] [blame]
/*
*
* 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.qpid.server;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.qpid.server.configuration.CommonProperties;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.logging.logback.LogbackLoggingSystemLauncherListener;
import org.apache.qpid.server.model.AbstractSystemConfig;
import org.apache.qpid.server.model.JsonSystemConfigImpl;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.SystemConfig;
import org.apache.qpid.server.plugin.ProtocolEngineCreator;
import org.apache.qpid.server.plugin.QpidServiceLoader;
import org.apache.qpid.server.util.StringUtil;
import org.apache.qpid.server.util.FileUtils;
/**
* Main entry point for Apache Qpid Broker-J.
*
*/
public class Main
{
public static final String PROPERTY_QPID_HOME = "QPID_HOME";
/**
* Configuration property name for the absolute path to use for the broker home directory.
*
* If not otherwise set, the value for this configuration property defaults to the location
* set in the "QPID_HOME" system property if that was set, or remains unset if it was not.
*/
private static final String QPID_HOME_DIR = "qpid.home_dir";
private static final int MANAGEMENT_MODE_PASSWORD_LENGTH = 10;
private static final Option OPTION_HELP = new Option("h", "help", false, "print this message");
private static final Option OPTION_VERSION = new Option("v", "version", false, "print the version information and exit");
private static final Option OPTION_CONFIGURATION_STORE_PATH = OptionBuilder.withArgName("path").hasArg()
.withDescription("use given configuration store location").withLongOpt("store-path").create("sp");
private static final Option OPTION_CONFIGURATION_STORE_TYPE = OptionBuilder.withArgName("type").hasArg()
.withDescription("use given broker configuration store type").withLongOpt("store-type").create("st");
private static final Option OPTION_INITIAL_CONFIGURATION_PATH = OptionBuilder.withArgName("path").hasArg()
.withDescription("set the location of initial JSON config to use when creating/overwriting a broker configuration store").withLongOpt("initial-config-path").create("icp");
private static final Option OPTION_CREATE_INITIAL_CONFIG = OptionBuilder.withArgName("path").hasOptionalArg().withDescription("create a copy of the initial config file, either to an" +
" optionally specified file path, or as " + SystemConfig.DEFAULT_INITIAL_CONFIG_NAME + " in the current directory")
.withLongOpt("create-initial-config").create("cic");
private static final Option OPTION_CONFIGURATION_PROPERTY = OptionBuilder.withArgName("name=value").hasArg()
.withDescription("set a configuration property to use when resolving variables in the broker configuration store, with format \"name=value\"")
.withLongOpt("config-property").create("prop");
private static final Option OPTION_MANAGEMENT_MODE = OptionBuilder.withDescription("start broker in management mode, disabling the AMQP ports")
.withLongOpt("management-mode").create("mm");
private static final Option OPTION_MM_QUIESCE_VHOST = OptionBuilder.withDescription("make virtualhosts stay in the quiesced state during management mode.")
.withLongOpt("management-mode-quiesce-virtualhosts").create("mmqv");
private static final Option OPTION_MM_HTTP_PORT = OptionBuilder.withArgName("port").hasArg()
.withDescription("override http management port in management mode").withLongOpt("management-mode-http-port").create("mmhttp");
private static final Option OPTION_MM_PASSWORD = OptionBuilder.withArgName("password").hasArg()
.withDescription("Set the password for the management mode user " + SystemConfig.MANAGEMENT_MODE_USER_NAME).withLongOpt("management-mode-password").create("mmpass");
private static final Option OPTION_INITIAL_SYSTEM_PROPERTIES = OptionBuilder.withArgName("path").hasArg()
.withDescription("set the location of initial properties file to set otherwise unset system properties").withLongOpt("system-properties-file").create("props");
private static final Options OPTIONS = new Options();
static
{
OPTIONS.addOption(OPTION_HELP);
OPTIONS.addOption(OPTION_VERSION);
OPTIONS.addOption(OPTION_CONFIGURATION_STORE_PATH);
OPTIONS.addOption(OPTION_CONFIGURATION_STORE_TYPE);
OPTIONS.addOption(OPTION_CREATE_INITIAL_CONFIG);
OPTIONS.addOption(OPTION_INITIAL_CONFIGURATION_PATH);
OPTIONS.addOption(OPTION_MANAGEMENT_MODE);
OPTIONS.addOption(OPTION_MM_QUIESCE_VHOST);
OPTIONS.addOption(OPTION_MM_HTTP_PORT);
OPTIONS.addOption(OPTION_MM_PASSWORD);
OPTIONS.addOption(OPTION_CONFIGURATION_PROPERTY);
OPTIONS.addOption(OPTION_INITIAL_SYSTEM_PROPERTIES);
CommonProperties.ensureIsLoaded();
}
protected CommandLine _commandLine;
public static void main(String[] args)
{
new Main(args);
}
public Main(final String[] args)
{
if (parseCommandline(args))
{
try
{
execute();
}
catch(Exception e)
{
System.err.println("Exception during startup: " + e);
e.printStackTrace();
shutdown(1);
}
}
}
protected boolean parseCommandline(final String[] args)
{
try
{
_commandLine = new PosixParser().parse(OPTIONS, args);
return true;
}
catch (ParseException e)
{
System.err.println("Error: " + e.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("Qpid", OPTIONS, true);
return false;
}
}
protected void execute() throws Exception
{
Map<String,Object> attributes = new HashMap<>();
String initialProperties = _commandLine.getOptionValue(OPTION_INITIAL_SYSTEM_PROPERTIES.getOpt());
SystemLauncher.populateSystemPropertiesFromDefaults(initialProperties);
String initialConfigLocation = _commandLine.getOptionValue(OPTION_INITIAL_CONFIGURATION_PATH.getOpt());
if(initialConfigLocation != null)
{
attributes.put(SystemConfig.INITIAL_CONFIGURATION_LOCATION, initialConfigLocation);
}
//process the remaining options
if (_commandLine.hasOption(OPTION_HELP.getOpt()))
{
final HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("Qpid", OPTIONS, true);
}
else if (_commandLine.hasOption(OPTION_CREATE_INITIAL_CONFIG.getOpt()))
{
createInitialConfigCopy(initialConfigLocation);
}
else if (_commandLine.hasOption(OPTION_VERSION.getOpt()))
{
printVersion();
}
else
{
String[] configPropPairs = _commandLine.getOptionValues(OPTION_CONFIGURATION_PROPERTY.getOpt());
Map<String, String> context = calculateConfigContext(configPropPairs);
if(!context.isEmpty())
{
attributes.put(SystemConfig.CONTEXT, context);
}
String configurationStore = _commandLine.getOptionValue(OPTION_CONFIGURATION_STORE_PATH.getOpt());
if(configurationStore != null)
{
attributes.put("storePath", configurationStore);
}
String configurationStoreType = _commandLine.getOptionValue(OPTION_CONFIGURATION_STORE_TYPE.getOpt());
attributes.put(SystemConfig.TYPE, configurationStoreType == null ? JsonSystemConfigImpl.SYSTEM_CONFIG_TYPE : configurationStoreType);
boolean managementMode = _commandLine.hasOption(OPTION_MANAGEMENT_MODE.getOpt());
if (managementMode)
{
attributes.put(SystemConfig.MANAGEMENT_MODE, true);
String httpPort = _commandLine.getOptionValue(OPTION_MM_HTTP_PORT.getOpt());
if(httpPort != null)
{
attributes.put(SystemConfig.MANAGEMENT_MODE_HTTP_PORT_OVERRIDE, httpPort);
}
boolean quiesceVhosts = _commandLine.hasOption(OPTION_MM_QUIESCE_VHOST.getOpt());
attributes.put(SystemConfig.MANAGEMENT_MODE_QUIESCE_VIRTUAL_HOSTS, quiesceVhosts);
String password = _commandLine.getOptionValue(OPTION_MM_PASSWORD.getOpt());
if (password == null)
{
password = new StringUtil().randomAlphaNumericString(MANAGEMENT_MODE_PASSWORD_LENGTH);
}
attributes.put(SystemConfig.MANAGEMENT_MODE_PASSWORD, password);
}
setExceptionHandler();
startBroker(attributes);
}
}
private Map<String, String> calculateConfigContext(final String[] configPropPairs)
{
Map<String,String> context = new HashMap<>();
if(configPropPairs != null && configPropPairs.length > 0)
{
for(String s : configPropPairs)
{
int firstEquals = s.indexOf("=");
if(firstEquals == -1)
{
throw new IllegalArgumentException("Configuration property argument is not of the format name=value: " + s);
}
String name = s.substring(0, firstEquals);
String value = s.substring(firstEquals + 1);
if(name.equals(""))
{
throw new IllegalArgumentException("Configuration property argument is not of the format name=value: " + s);
}
context.put(name, value);
}
}
if(!context.containsKey(QPID_HOME_DIR))
{
Properties systemProperties = System.getProperties();
final Map<String, String> environment = System.getenv();
if(systemProperties.containsKey(QPID_HOME_DIR))
{
context.put(QPID_HOME_DIR, systemProperties.getProperty(QPID_HOME_DIR));
}
else if(environment.containsKey(QPID_HOME_DIR))
{
context.put(QPID_HOME_DIR, environment.get(QPID_HOME_DIR));
}
else if(context.containsKey(PROPERTY_QPID_HOME))
{
context.put(QPID_HOME_DIR, context.get(PROPERTY_QPID_HOME));
}
else if(systemProperties.containsKey(PROPERTY_QPID_HOME))
{
context.put(QPID_HOME_DIR, systemProperties.getProperty(PROPERTY_QPID_HOME));
}
else if(environment.containsKey(PROPERTY_QPID_HOME))
{
context.put(QPID_HOME_DIR, environment.get(PROPERTY_QPID_HOME));
}
}
return context;
}
private void printVersion()
{
final StringBuilder protocol = new StringBuilder("AMQP version(s) [major.minor]: ");
boolean first = true;
Set<Protocol> protocols = new TreeSet<>();
for(ProtocolEngineCreator installedEngine : (new QpidServiceLoader()).instancesOf(ProtocolEngineCreator.class))
{
protocols.add(installedEngine.getVersion());
}
for(Protocol supportedProtocol : protocols)
{
if (first)
{
first = false;
}
else
{
protocol.append(", ");
}
protocol.append(supportedProtocol.getProtocolVersion());
}
System.out.println(CommonProperties.getVersionString() + " (" + protocol + ")");
}
private void createInitialConfigCopy(String initialConfigLocation)
{
File destinationFile = null;
String destinationOption = _commandLine.getOptionValue(OPTION_CREATE_INITIAL_CONFIG.getOpt());
if (destinationOption != null)
{
destinationFile = new File(destinationOption);
}
else
{
destinationFile = new File(System.getProperty("user.dir"), SystemConfig.DEFAULT_INITIAL_CONFIG_NAME);
}
if(initialConfigLocation == null)
{
initialConfigLocation = AbstractSystemConfig.getDefaultValue(SystemConfig.INITIAL_CONFIGURATION_LOCATION);
}
copyInitialConfigFile(initialConfigLocation, destinationFile);
System.out.println("Initial config written to: " + destinationFile.getAbsolutePath());
}
private void copyInitialConfigFile(final String initialConfigLocation, final File destinationFile)
{
URL url = null;
try
{
url = new URL(initialConfigLocation);
}
catch (MalformedURLException e)
{
File locationFile = new File(initialConfigLocation);
try
{
url = locationFile.toURI().toURL();
}
catch (MalformedURLException e1)
{
throw new IllegalConfigurationException("Cannot create URL for file " + locationFile, e1);
}
}
InputStream in = null;
try
{
in = url.openStream();
FileUtils.copy(in, destinationFile);
}
catch (IOException e)
{
throw new IllegalConfigurationException("Cannot create file " + destinationFile
+ " by copying initial config from " + initialConfigLocation, e);
}
finally
{
if (in != null)
{
try
{
in.close();
}
catch (IOException e)
{
throw new IllegalConfigurationException("Cannot close initial config input stream: " + initialConfigLocation, e);
}
}
}
}
protected void setExceptionHandler()
{
Thread.UncaughtExceptionHandler handler = null;
String handlerClass = System.getProperty("qpid.broker.exceptionHandler");
if(handlerClass != null)
{
try
{
handler = (Thread.UncaughtExceptionHandler) Class.forName(handlerClass).newInstance();
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | ClassCastException e)
{
}
}
if(handler == null)
{
handler =
new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException(final Thread t, final Throwable e)
{
boolean continueOnError = Boolean.getBoolean("qpid.broker.exceptionHandler.continue");
try
{
System.err.println("########################################################################");
System.err.println("#");
System.err.print("# Unhandled Exception ");
System.err.print(e.toString());
System.err.print(" in Thread ");
System.err.println(t.getName());
System.err.println("#");
System.err.println(continueOnError ? "# Forced to continue by JVM setting 'qpid.broker.exceptionHandler.continue'" : "# Exiting");
System.err.println("#");
System.err.println("########################################################################");
e.printStackTrace(System.err);
Logger logger = LoggerFactory.getLogger("org.apache.qpid.server.Main");
logger.error("Uncaught exception, " + (continueOnError ? "continuing." : "shutting down."), e);
}
finally
{
if (!continueOnError)
{
Runtime.getRuntime().halt(1);
}
}
}
};
Thread.setDefaultUncaughtExceptionHandler(handler);
}
}
protected void startBroker(Map<String,Object> attributes) throws Exception
{
SystemLauncher systemLauncher = new SystemLauncher(new LogbackLoggingSystemLauncherListener(),
new SystemLauncherListener.DefaultSystemLauncherListener()
{
@Override
public void onShutdown(final int exitCode)
{
if (exitCode != 0)
{
shutdown(exitCode);
}
}
});
systemLauncher.startup(attributes);
}
protected void shutdown(final int status)
{
System.exit(status);
}
}