/*
* 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.activemq.artemis.cli.commands;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.airlift.airline.Arguments;
import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.activemq.artemis.cli.CLIException;
import org.apache.activemq.artemis.cli.commands.util.HashUtil;
import org.apache.activemq.artemis.cli.commands.util.SyncCalculation;
import org.apache.activemq.artemis.core.server.JournalType;
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
import org.apache.activemq.artemis.jlibaio.LibaioContext;
import org.apache.activemq.artemis.jlibaio.LibaioFile;
import org.apache.activemq.artemis.utils.FileUtil;
/**
* CLI action that creates a broker instance directory.
*/
@Command(name = "create", description = "creates a new broker instance")
public class Create extends InputAbstract {
private static final Integer DEFAULT_PORT = 61616;
private static final Integer AMQP_PORT = 5672;
private static final Integer STOMP_PORT = 61613;
private static final Integer HQ_PORT = 5445;
public static final String HTTP_HOST = "localhost";
public static final Integer HTTP_PORT = 8161;
private static final Integer MQTT_PORT = 1883;
/* **********************************************************************************
* Note for developers: These are tested at StreamClassPathTest on the unit test.
* This is to make sure maven or something else is not hiding these resources.
* ********************************************************************************** */
public static final String BIN_ARTEMIS_CMD = "bin/artemis.cmd";
public static final String BIN_ARTEMIS_SERVICE_EXE = "bin/artemis-service.exe";
public static final String BIN_ARTEMIS_SERVICE_XML = "bin/artemis-service.xml";
public static final String ETC_ARTEMIS_PROFILE_CMD = "etc/artemis.profile.cmd";
public static final String BIN_ARTEMIS = "bin/artemis";
public static final String BIN_ARTEMIS_SERVICE = "bin/artemis-service";
public static final String ETC_ARTEMIS_PROFILE = "etc/artemis.profile";
public static final String ETC_LOGGING_PROPERTIES = "etc/logging.properties";
public static final String ETC_BOOTSTRAP_XML = "etc/bootstrap.xml";
public static final String ETC_BROKER_XML = "etc/broker.xml";
public static final String ETC_ARTEMIS_ROLES_PROPERTIES = "etc/artemis-roles.properties";
public static final String ETC_ARTEMIS_USERS_PROPERTIES = "etc/artemis-users.properties";
private static final String ETC_LOGIN_CONFIG = "etc/login.config";
private static final String ETC_LOGIN_CONFIG_WITH_GUEST = "etc/login-with-guest.config";
private static final String ETC_LOGIN_CONFIG_WITHOUT_GUEST = "etc/login-without-guest.config";
public static final String ETC_REPLICATED_SETTINGS_TXT = "etc/replicated-settings.txt";
public static final String ETC_SHARED_STORE_SETTINGS_TXT = "etc/shared-store-settings.txt";
public static final String ETC_CLUSTER_SECURITY_SETTINGS_TXT = "etc/cluster-security-settings.txt";
public static final String ETC_CLUSTER_SETTINGS_TXT = "etc/cluster-settings.txt";
public static final String ETC_CONNECTOR_SETTINGS_TXT = "etc/connector-settings.txt";
public static final String ETC_BOOTSTRAP_WEB_SETTINGS_TXT = "etc/bootstrap-web-settings.txt";
public static final String ETC_JOURNAL_BUFFER_SETTINGS = "etc/journal-buffer-settings.txt";
public static final String ETC_AMQP_ACCEPTOR_TXT = "etc/amqp-acceptor.txt";
public static final String ETC_HORNETQ_ACCEPTOR_TXT = "etc/hornetq-acceptor.txt";
public static final String ETC_MQTT_ACCEPTOR_TXT = "etc/mqtt-acceptor.txt";
public static final String ETC_STOMP_ACCEPTOR_TXT = "etc/stomp-acceptor.txt";
public static final String ETC_PING_TXT = "etc/ping-settings.txt";
public static final String ETC_COMMENTED_PING_TXT = "etc/commented-ping-settings.txt";
public static final String ETC_GLOBAL_MAX_SPECIFIED_TXT = "etc/global-max-specified.txt";
public static final String ETC_GLOBAL_MAX_DEFAULT_TXT = "etc/global-max-default.txt";
@Arguments(description = "The instance directory to hold the broker's configuration and data. Path must be writable.", required = true)
private File directory;
@Option(name = "--host", description = "The host name of the broker (Default: 0.0.0.0 or input if clustered)")
private String host;
@Option(name = "--http-host", description = "The host name to use for embedded web server (Default: localhost)")
private String httpHost = HTTP_HOST;
@Option(name = "--ping", description = "A comma separated string to be passed on to the broker config as network-check-list. The broker will shutdown when all these addresses are unreachable.")
private String ping;
@Option(name = "--default-port", description = "The port number to use for the main 'artemis' acceptor (Default: 61616)")
private int defaultPort = DEFAULT_PORT;
@Option(name = "--http-port", description = "The port number to use for embedded web server (Default: 8161)")
private int httpPort = HTTP_PORT;
@Option(name = "--ssl-key", description = "The key store path for embedded web server")
private String sslKey;
@Option(name = "--ssl-key-password", description = "The key store password")
private String sslKeyPassword;
@Option(name = "--use-client-auth", description = "If the embedded server requires client authentication")
private boolean useClientAuth;
@Option(name = "--ssl-trust", description = "The trust store path in case of client authentication")
private String sslTrust;
@Option(name = "--ssl-trust-password", description = "The trust store password")
private String sslTrustPassword;
@Option(name = "--name", description = "The name of the broker (Default: same as host)")
private String name;
@Option(name = "--port-offset", description = "Off sets the ports of every acceptor")
private int portOffset;
@Option(name = "--force", description = "Overwrite configuration at destination directory")
private boolean force;
@Option(name = "--home", description = "Directory where ActiveMQ Artemis is installed")
private File home;
@Option(name = "--data", description = "Directory where ActiveMQ Data is used. Paths are relative to artemis.instance")
private String data = "./data";
@Option(name = "--clustered", description = "Enable clustering")
private boolean clustered = false;
@Option(name = "--max-hops", description = "Number of hops on the cluster configuration")
private int maxHops = 0;
@Option(name = "--message-load-balancing", description = "Load balancing policy on cluster. [ON_DEMAND (default) | STRICT | OFF]")
private MessageLoadBalancingType messageLoadBalancing = MessageLoadBalancingType.ON_DEMAND;
@Option(name = "--replicated", description = "Enable broker replication")
private boolean replicated = false;
@Option(name = "--shared-store", description = "Enable broker shared store")
private boolean sharedStore = false;
@Option(name = "--slave", description = "Valid for shared store or replication: this is a slave server?")
private boolean slave;
@Option(name = "--failover-on-shutdown", description = "Valid for shared store: will shutdown trigger a failover? (Default: false)")
private boolean failoverOnShutodwn;
@Option(name = "--cluster-user", description = "The cluster user to use for clustering. (Default: input)")
private String clusterUser = null;
@Option(name = "--cluster-password", description = "The cluster password to use for clustering. (Default: input)")
private String clusterPassword = null;
@Option(name = "--encoding", description = "The encoding that text files should use")
private String encoding = "UTF-8";
@Option(name = "--java-options", description = "Extra java options to be passed to the profile")
private String javaOptions = "";
@Option(name = "--allow-anonymous", description = "Enables anonymous configuration on security, opposite of --require-login (Default: input)")
private Boolean allowAnonymous = null;
@Option(name = "--require-login", description = "This will configure security to require user / password, opposite of --allow-anonymous")
private Boolean requireLogin = null;
@Option(name = "--paging", description = "Page messages to disk when address becomes full, opposite of --blocking (Default: true)")
private Boolean paging;
@Option(name = "--blocking", description = "Block producers when address becomes full, opposite of --paging (Default: false)")
private Boolean blocking;
@Option(name = "--no-autotune", description = "Disable auto tuning on the journal.")
private boolean noAutoTune;
@Option(name = "--no-autocreate", description = "Disable Auto create addresses.")
private Boolean noAutoCreate;
@Option(name = "--autocreate", description = "Auto create addresses. (default: true)")
private Boolean autoCreate;
@Option(name = "--user", description = "The username (Default: input)")
private String user;
@Option(name = "--password", description = "The user's password (Default: input)")
private String password;
@Option(name = "--role", description = "The name for the role created (Default: amq)")
private String role = "amq";
@Option(name = "--no-web", description = "Remove the web-server definition from bootstrap.xml")
private boolean noWeb;
@Option(name = "--queues", description = "Comma separated list of queues.")
private String queues;
@Option(name = "--addresses", description = "Comma separated list of addresses ")
private String addresses;
@Option(name = "--aio", description = "Sets the journal as asyncio.")
private boolean aio;
@Option(name = "--nio", description = "Sets the journal as nio.")
private boolean nio;
@Option(name = "--mapped", description = "Sets the journal as mapped.")
private boolean mapped;
// this is used by the setupJournalType method
private JournalType journalType;
@Option(name = "--disable-persistence", description = "Disable message persistence to the journal")
private boolean disablePersistence;
@Option(name = "--no-amqp-acceptor", description = "Disable the AMQP specific acceptor.")
private boolean noAmqpAcceptor;
@Option(name = "--no-mqtt-acceptor", description = "Disable the MQTT specific acceptor.")
private boolean noMqttAcceptor;
@Option(name = "--no-stomp-acceptor", description = "Disable the STOMP specific acceptor.")
private boolean noStompAcceptor;
@Option(name = "--no-hornetq-acceptor", description = "Disable the HornetQ specific acceptor.")
private boolean noHornetQAcceptor;
@Option(name = "--no-fsync", description = "Disable usage of fdatasync (channel.force(false) from java nio) on the journal")
private boolean noJournalSync;
@Option(name = "--global-max-size", description = "Maximum amount of memory which message data may consume (Default: Undefined, half of the system's memory)")
private String globalMaxSize;
private boolean IS_WINDOWS;
private boolean IS_CYGWIN;
private boolean isAutoCreate() {
if (autoCreate == null) {
if (noAutoCreate != null) {
autoCreate = !noAutoCreate.booleanValue();
}
}
if (autoCreate == null) {
autoCreate = true;
}
return autoCreate;
}
public File getInstance() {
return directory;
}
public void setInstance(File directory) {
this.directory = directory;
}
public String getHost() {
if (host == null) {
host = "0.0.0.0";
}
return host;
}
private String getHostForClustered() {
if (getHost().equals("0.0.0.0")) {
host = input("--host", "Host " + host + " is not valid for clustering, please provide a valid IP or hostname", "localhost");
}
return host;
}
public void setHost(String host) {
this.host = host;
}
public boolean isForce() {
return force;
}
public void setForce(boolean force) {
this.force = force;
}
public File getHome() {
if (home == null) {
home = new File(getBrokerHome());
}
return home;
}
public void setHome(File home) {
this.home = home;
}
public boolean isReplicated() {
return replicated;
}
public void setReplicated(boolean replicated) {
this.replicated = replicated;
}
public boolean isSharedStore() {
return sharedStore;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
private String getClusterUser() {
if (clusterUser == null) {
clusterUser = input("--cluster-user", "Please provide the username:", "cluster-admin");
}
return clusterUser;
}
private String getClusterPassword() {
if (clusterPassword == null) {
clusterPassword = inputPassword("--cluster-password", "Please enter the password:", "password-admin");
}
return clusterPassword;
}
private String getSslKeyPassword() {
if (sslKeyPassword == null) {
sslKeyPassword = inputPassword("--ssl-key-password", "Please enter the keystore password:", "password");
}
return sslKeyPassword;
}
private String getSslTrust() {
if (sslTrust == null) {
sslTrust = input("--ssl-trust", "Please enter the trust store path:", "/etc/truststore.jks");
}
return sslTrust;
}
private String getSslTrustPassword() {
if (sslTrustPassword == null) {
sslTrustPassword = inputPassword("--ssl-key-password", "Please enter the keystore password:", "password");
}
return sslTrustPassword;
}
private boolean isAllowAnonymous() {
if (allowAnonymous == null) {
allowAnonymous = inputBoolean("--allow-anonymous | --require-login", "Allow anonymous access?", true);
}
return allowAnonymous;
}
public boolean isPaging() {
return paging;
}
public String getPassword() {
if (password == null) {
password = inputPassword("--password", "Please provide the default password:", "admin");
}
password = HashUtil.tryHash(context, password);
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUser() {
if (user == null) {
user = input("--user", "Please provide the default username:", "admin");
}
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
private boolean isSlave() {
return slave;
}
private boolean isFailoverOnShutodwn() {
return failoverOnShutodwn;
}
private boolean isDisablePersistence() {
return disablePersistence;
}
@Override
public Object execute(ActionContext context) throws Exception {
this.checkDirectory();
super.execute(context);
return run(context);
}
/**
* This method is made public for the testsuite
*/
public InputStream openStream(String source) {
return this.getClass().getResourceAsStream(source);
}
/**
* Checks that the directory provided either exists and is writable or doesn't exist but can be created.
*/
private void checkDirectory() {
if (!directory.exists()) {
boolean created = directory.mkdirs();
if (!created) {
throw new RuntimeException(String.format("Unable to create the path '%s'.", directory));
}
} else if (!directory.canWrite()) {
throw new RuntimeException(String.format("The path '%s' is not writable.", directory));
}
}
public Object run(ActionContext context) throws Exception {
IS_WINDOWS = System.getProperty("os.name").toLowerCase().trim().startsWith("win");
IS_CYGWIN = IS_WINDOWS && "cygwin".equals(System.getenv("OSTYPE"));
setupJournalType();
// requireLogin should set allowAnonymous=false, to avoid user's questions
if (requireLogin != null && requireLogin.booleanValue()) {
allowAnonymous = Boolean.FALSE;
}
context.out.println(String.format("Creating ActiveMQ Artemis instance at: %s", directory.getCanonicalPath()));
HashMap<String, String> filters = new HashMap<>();
filters.put("${master-slave}", isSlave() ? "slave" : "master");
filters.put("${failover-on-shutdown}", isFailoverOnShutodwn() ? "true" : "false");
filters.put("${persistence-enabled}", isDisablePersistence() ? "false" : "true");
if (ping != null && !ping.isEmpty()) {
filters.put("${ping}", ping);
filters.put("${ping-config.settings}", applyFilters(readTextFile(ETC_PING_TXT), filters));
} else {
filters.put("${ping-config.settings}", readTextFile(ETC_COMMENTED_PING_TXT));
}
if (replicated) {
clustered = true;
filters.put("${replicated.settings}", applyFilters(readTextFile(ETC_REPLICATED_SETTINGS_TXT), filters));
} else {
filters.put("${replicated.settings}", "");
}
if (sharedStore) {
clustered = true;
filters.put("${shared-store.settings}", applyFilters(readTextFile(ETC_SHARED_STORE_SETTINGS_TXT), filters));
} else {
filters.put("${shared-store.settings}", "");
}
filters.put("${journal.settings}", journalType.name());
if (sslKey != null) {
filters.put("${web.protocol}", "https");
getSslKeyPassword();
String extraWebAttr = " keyStorePath=\"" + sslKey + "\" keyStorePassword=\"" + sslKeyPassword + "\"";
if (useClientAuth) {
getSslTrust();
getSslTrustPassword();
extraWebAttr += " clientAuth=\"true\" trustStorePath=\"" + sslTrust + "\" trustStorePassword=\"" + sslTrustPassword + "\"";
}
filters.put("${extra.web.attributes}", extraWebAttr);
} else {
filters.put("${web.protocol}", "http");
filters.put("${extra.web.attributes}", "");
}
filters.put("${fsync}", String.valueOf(!noJournalSync));
filters.put("${user}", System.getProperty("user.name", ""));
filters.put("${default.port}", String.valueOf(defaultPort + portOffset));
filters.put("${amqp.port}", String.valueOf(AMQP_PORT + portOffset));
filters.put("${stomp.port}", String.valueOf(STOMP_PORT + portOffset));
filters.put("${hq.port}", String.valueOf(HQ_PORT + portOffset));
filters.put("${mqtt.port}", String.valueOf(MQTT_PORT + portOffset));
filters.put("${http.host}", httpHost);
filters.put("${http.port}", String.valueOf(httpPort + portOffset));
filters.put("${data.dir}", data);
filters.put("${max-hops}", String.valueOf(maxHops));
filters.put("${message-load-balancing}", messageLoadBalancing.toString());
filters.put("${user}", getUser());
filters.put("${password}", getPassword());
filters.put("${role}", role);
if (globalMaxSize == null || globalMaxSize.trim().equals("")) {
filters.put("${global-max-section}", readTextFile(ETC_GLOBAL_MAX_DEFAULT_TXT));
} else {
filters.put("${global-max-size}", globalMaxSize);
filters.put("${global-max-section}", applyFilters(readTextFile(ETC_GLOBAL_MAX_SPECIFIED_TXT), filters));
}
if (clustered) {
filters.put("${host}", getHostForClustered());
if (name == null) {
name = getHostForClustered();
}
String connectorSettings = readTextFile(ETC_CONNECTOR_SETTINGS_TXT);
connectorSettings = applyFilters(connectorSettings, filters);
filters.put("${name}", name);
filters.put("${connector-config.settings}", connectorSettings);
filters.put("${cluster-security.settings}", readTextFile(ETC_CLUSTER_SECURITY_SETTINGS_TXT));
filters.put("${cluster.settings}", applyFilters(readTextFile(ETC_CLUSTER_SETTINGS_TXT), filters));
filters.put("${cluster-user}", getClusterUser());
filters.put("${cluster-password}", getClusterPassword());
} else {
if (name == null) {
name = getHost();
}
filters.put("${name}", name);
filters.put("${host}", getHost());
filters.put("${connector-config.settings}", "");
filters.put("${cluster-security.settings}", "");
filters.put("${cluster.settings}", "");
filters.put("${cluster-user}", "");
filters.put("${cluster-password}", "");
}
applyAddressesAndQueues(filters);
if (home != null) {
filters.put("${home}", path(home));
}
filters.put("${artemis.home}", path(getHome().toString()));
filters.put("${artemis.instance}", path(directory));
filters.put("${artemis.instance.uri}", directory.toURI().toString());
filters.put("${artemis.instance.uri.windows}", directory.toURI().toString().replaceAll("%", "%%"));
filters.put("${artemis.instance.name}", directory.getName());
filters.put("${java.home}", path(System.getProperty("java.home")));
new File(directory, "bin").mkdirs();
new File(directory, "etc").mkdirs();
new File(directory, "log").mkdirs();
new File(directory, "tmp").mkdirs();
new File(directory, "lib").mkdirs();
File dataFolder = new File(directory, "data");
dataFolder.mkdirs();
filters.put("${logmanager}", getLogManager());
if (javaOptions == null || javaOptions.length() == 0) {
javaOptions = "";
}
filters.put("${java-opts}", javaOptions);
if (isAllowAnonymous()) {
write(ETC_LOGIN_CONFIG_WITH_GUEST, filters, false);
new File(directory, ETC_LOGIN_CONFIG_WITH_GUEST).renameTo(new File(directory, ETC_LOGIN_CONFIG));
} else {
write(ETC_LOGIN_CONFIG_WITHOUT_GUEST, filters, false);
new File(directory, ETC_LOGIN_CONFIG_WITHOUT_GUEST).renameTo(new File(directory, ETC_LOGIN_CONFIG));
}
write(ETC_ARTEMIS_ROLES_PROPERTIES, filters, false);
if (IS_WINDOWS) {
write(BIN_ARTEMIS_CMD, null, false);
write(BIN_ARTEMIS_SERVICE_EXE);
write(BIN_ARTEMIS_SERVICE_XML, filters, false);
write(ETC_ARTEMIS_PROFILE_CMD, filters, false);
}
if (!IS_WINDOWS || IS_CYGWIN) {
write(BIN_ARTEMIS, filters, true);
makeExec(BIN_ARTEMIS);
write(BIN_ARTEMIS_SERVICE, filters, true);
makeExec(BIN_ARTEMIS_SERVICE);
write(ETC_ARTEMIS_PROFILE, filters, true);
}
write(ETC_LOGGING_PROPERTIES, null, false);
if (noWeb) {
filters.put("${bootstrap-web-settings}", "");
} else {
filters.put("${bootstrap-web-settings}", applyFilters(readTextFile(ETC_BOOTSTRAP_WEB_SETTINGS_TXT), filters));
}
if (noAmqpAcceptor) {
filters.put("${amqp-acceptor}", "");
} else {
filters.put("${amqp-acceptor}", applyFilters(readTextFile(ETC_AMQP_ACCEPTOR_TXT), filters));
}
if (noMqttAcceptor) {
filters.put("${mqtt-acceptor}", "");
} else {
filters.put("${mqtt-acceptor}", applyFilters(readTextFile(ETC_MQTT_ACCEPTOR_TXT), filters));
}
if (noStompAcceptor) {
filters.put("${stomp-acceptor}", "");
} else {
filters.put("${stomp-acceptor}", applyFilters(readTextFile(ETC_STOMP_ACCEPTOR_TXT), filters));
}
if (noHornetQAcceptor) {
filters.put("${hornetq-acceptor}", "");
} else {
filters.put("${hornetq-acceptor}", applyFilters(readTextFile(ETC_HORNETQ_ACCEPTOR_TXT), filters));
}
if (paging == null && blocking == null) {
filters.put("${full-policy}", "PAGE");
} else if (paging != null && paging) {
filters.put("${full-policy}", "PAGE");
} else if (blocking != null && blocking) {
filters.put("${full-policy}", "BLOCK");
} else {
filters.put("${full-policy}", "PAGE");
}
filters.put("${auto-create}", isAutoCreate() ? "true" : "false");
performAutoTune(filters, journalType, dataFolder);
write(ETC_BROKER_XML, filters, false);
write(ETC_ARTEMIS_USERS_PROPERTIES, filters, false);
// we want this variable to remain unchanged so that it will use the value set in the profile
filters.remove("${artemis.instance}");
write(ETC_BOOTSTRAP_XML, filters, false);
context.out.println("");
context.out.println("You can now start the broker by executing: ");
context.out.println("");
context.out.println(String.format(" \"%s\" run", path(new File(directory, "bin/artemis"))));
File service = new File(directory, BIN_ARTEMIS_SERVICE);
context.out.println("");
if (!IS_WINDOWS || IS_CYGWIN) {
context.out.println("Or you can run the broker in the background using:");
context.out.println("");
context.out.println(String.format(" \"%s\" start", path(service)));
context.out.println("");
}
if (IS_WINDOWS) {
service = new File(directory, BIN_ARTEMIS_SERVICE_EXE);
context.out.println("Or you can setup the broker as Windows service and run it in the background:");
context.out.println("");
context.out.println(String.format(" \"%s\" install", path(service)));
context.out.println(String.format(" \"%s\" start", path(service)));
context.out.println("");
context.out.println(" To stop the windows service:");
context.out.println(String.format(" \"%s\" stop", path(service)));
context.out.println("");
context.out.println(" To uninstall the windows service");
context.out.println(String.format(" \"%s\" uninstall", path(service)));
}
return null;
}
private void setupJournalType() {
int countJournalTypes = countBoolean(aio, nio, mapped);
if (countJournalTypes > 1) {
throw new RuntimeException("You can only select one journal type (--nio | --aio | --mapped).");
}
if (countJournalTypes == 0) {
if (supportsLibaio()) {
aio = true;
} else {
nio = true;
}
}
if (aio) {
journalType = JournalType.ASYNCIO;
} else if (nio) {
journalType = JournalType.NIO;
} else if (mapped) {
journalType = JournalType.MAPPED;
}
}
private static int countBoolean(boolean...b) {
int count = 0;
for (boolean itemB : b) {
if (itemB) count++;
}
return count;
}
private String getLogManager() throws IOException {
String logManager = "";
File dir = new File(path(getHome().toString()) + "/lib");
File[] matches = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.startsWith("jboss-logmanager") && name.endsWith(".jar");
}
});
if (matches != null && matches.length > 0) {
logManager = matches[0].getName();
}
return logManager;
}
/**
* It will create the address and queue configurations
*/
private void applyAddressesAndQueues(HashMap<String, String> filters) {
StringWriter writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
printWriter.println();
for (String str : getQueueList()) {
printWriter.println(" <address name=\"" + str + "\">");
printWriter.println(" <anycast>");
printWriter.println(" <queue name=\"" + str + "\" />");
printWriter.println(" </anycast>");
printWriter.println(" </address>");
}
for (String str : getAddressList()) {
printWriter.println(" <address name=\"" + str + "\"/>");
}
filters.put("${address-queue.settings}", writer.toString());
}
private void performAutoTune(HashMap<String, String> filters, JournalType journalType, File dataFolder) {
if (noAutoTune) {
filters.put("${journal-buffer.settings}", "");
} else {
try {
int writes = 250;
System.out.println("");
System.out.println("Auto tuning journal ...");
long time = SyncCalculation.syncTest(dataFolder, 4096, writes, 5, verbose, !noJournalSync, journalType);
long nanoseconds = SyncCalculation.toNanos(time, writes, verbose);
double writesPerMillisecond = (double) writes / (double) time;
String writesPerMillisecondStr = new DecimalFormat("###.##").format(writesPerMillisecond);
HashMap<String, String> syncFilter = new HashMap<>();
syncFilter.put("${nanoseconds}", Long.toString(nanoseconds));
syncFilter.put("${writesPerMillisecond}", writesPerMillisecondStr);
System.out.println("done! Your system can make " + writesPerMillisecondStr +
" writes per millisecond, your journal-buffer-timeout will be " + nanoseconds);
filters.put("${journal-buffer.settings}", applyFilters(readTextFile(ETC_JOURNAL_BUFFER_SETTINGS), syncFilter));
} catch (Exception e) {
filters.put("${journal-buffer.settings}", "");
e.printStackTrace();
System.err.println("Couldn't perform sync calculation, using default values");
}
}
}
public boolean supportsLibaio() {
if (IS_WINDOWS) {
return false;
}
if (LibaioContext.isLoaded()) {
try (LibaioContext context = new LibaioContext(1, true, true)) {
File tmpFile = new File(directory, "validateAIO.bin");
boolean supportsLibaio = true;
try {
LibaioFile file = context.openFile(tmpFile, true);
file.close();
} catch (Exception e) {
supportsLibaio = false;
}
tmpFile.delete();
if (!supportsLibaio) {
System.err.println("The filesystem used on " + directory + " doesn't support libAIO and O_DIRECT files, switching journal-type to NIO");
}
return supportsLibaio;
}
} else {
return false;
}
}
private void makeExec(String path) throws IOException {
FileUtil.makeExec(new File(directory, path));
}
private String[] getQueueList() {
if (queues == null) {
return new String[0];
} else {
return queues.split(",");
}
}
private String[] getAddressList() {
if (addresses == null) {
return new String[0];
} else {
return addresses.split(",");
}
}
private String path(String value) throws IOException {
return path(new File(value));
}
private String path(File value) throws IOException {
return value.getCanonicalPath();
}
private void write(String source, HashMap<String, String> filters, boolean unixTarget) throws Exception {
write(source, new File(directory, source), filters, unixTarget);
}
private void write(String source,
File target,
HashMap<String, String> filters,
boolean unixTarget) throws Exception {
if (target.exists() && !force) {
throw new CLIException(String.format("The file '%s' already exists. Use --force to overwrite.", target));
}
String content = applyFilters(readTextFile(source), filters);
// and then writing out in the new target encoding.. Let's also replace \n with the values
// that is correct for the current platform.
String separator = unixTarget && IS_CYGWIN ? "\n" : System.getProperty("line.separator");
content = content.replaceAll("\\r?\\n", Matcher.quoteReplacement(separator));
ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes(encoding));
try (FileOutputStream fout = new FileOutputStream(target)) {
copy(in, fout);
}
}
private String applyFilters(String content, HashMap<String, String> filters) throws IOException {
if (filters != null) {
for (Map.Entry<String, String> entry : filters.entrySet()) {
content = replace(content, entry.getKey(), entry.getValue());
}
}
return content;
}
private String readTextFile(String source) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (InputStream in = openStream(source)) {
copy(in, out);
}
return new String(out.toByteArray(), StandardCharsets.UTF_8);
}
private void write(String source) throws IOException {
File target = new File(directory, source);
if (target.exists() && !force) {
throw new RuntimeException(String.format("The file '%s' already exists. Use --force to overwrite.", target));
}
try (FileOutputStream fout = new FileOutputStream(target)) {
try (InputStream in = openStream(source)) {
copy(in, fout);
}
}
}
private String replace(String content, String key, String value) {
return content.replaceAll(Pattern.quote(key), Matcher.quoteReplacement(value));
}
private void copy(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024 * 4];
int c = is.read(buffer);
while (c >= 0) {
os.write(buffer, 0, c);
c = is.read(buffer);
}
}
}