| /* |
| * |
| * 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); |
| } |
| |
| } |