| /* |
| * 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.brooklyn.entity.webapp.jboss; |
| |
| import static java.lang.String.format; |
| |
| import java.io.InputStream; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.brooklyn.core.entity.Entities; |
| import org.apache.brooklyn.entity.software.base.SoftwareProcess; |
| import org.apache.brooklyn.entity.webapp.JavaWebAppSshDriver; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.apache.brooklyn.location.ssh.SshMachineLocation; |
| import org.apache.brooklyn.util.collections.MutableList; |
| import org.apache.brooklyn.util.collections.MutableMap; |
| import org.apache.brooklyn.util.net.Networking; |
| import org.apache.brooklyn.util.os.Os; |
| import org.apache.brooklyn.util.ssh.BashCommands; |
| import org.apache.brooklyn.util.text.Strings; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Preconditions; |
| import com.google.common.hash.Hashing; |
| import com.google.common.io.BaseEncoding; |
| |
| public class JBoss7SshDriver extends JavaWebAppSshDriver implements JBoss7Driver { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(JBoss7SshDriver.class); |
| |
| // TODO more configurability of config files, java memory, etc |
| |
| public static final String SERVER_TYPE = "standalone"; |
| public static final String CONFIG_FILE = "standalone-brooklyn.xml"; |
| public static final String KEYSTORE_FILE = ".keystore"; |
| public static final String MANAGEMENT_REALM = "ManagementRealm"; |
| |
| public JBoss7SshDriver(JBoss7ServerImpl entity, SshMachineLocation machine) { |
| super(entity, machine); |
| } |
| |
| @Override |
| public JBoss7ServerImpl getEntity() { |
| return (JBoss7ServerImpl) super.getEntity(); |
| } |
| |
| @Override |
| public String getSslKeystoreFile() { |
| return Os.mergePathsUnix(getRunDir(), SERVER_TYPE, "configuration", KEYSTORE_FILE); |
| } |
| |
| protected String getTemplateConfigurationUrl() { |
| return entity.getConfig(JBoss7Server.TEMPLATE_CONFIGURATION_URL); |
| } |
| |
| @Override |
| protected String getLogFileLocation() { |
| return Os.mergePathsUnix(getRunDir(), SERVER_TYPE, "log/server.log"); |
| } |
| |
| @Override |
| protected String getDeploySubdir() { |
| return Os.mergePathsUnix(SERVER_TYPE, "deployments"); |
| } |
| |
| private Integer getManagementHttpPort() { |
| return entity.getAttribute(JBoss7Server.MANAGEMENT_HTTP_PORT); |
| } |
| |
| private Integer getManagementHttpsPort() { |
| return entity.getAttribute(JBoss7Server.MANAGEMENT_HTTPS_PORT); |
| } |
| |
| private Integer getManagementNativePort() { |
| return entity.getAttribute(JBoss7Server.MANAGEMENT_NATIVE_PORT); |
| } |
| |
| private String getManagementUsername() { |
| return entity.getConfig(JBoss7Server.MANAGEMENT_USER); |
| } |
| |
| private String getManagementPassword() { |
| return entity.getConfig(JBoss7Server.MANAGEMENT_PASSWORD); |
| } |
| |
| @Override |
| public void prepare() { |
| resolver = Entities.newDownloader(this); |
| setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(format("jboss-as-%s", getVersion())))); |
| } |
| |
| @Override |
| public void install() { |
| List<String> urls = resolver.getTargets(); |
| String saveAs = resolver.getFilename(); |
| |
| List<String> commands = new LinkedList<String>(); |
| commands.addAll(BashCommands.commandsToDownloadUrlsAs(urls, saveAs)); |
| commands.add(BashCommands.INSTALL_TAR); |
| commands.add("tar xzfv " + saveAs); |
| |
| newScript(INSTALLING) |
| // don't set vars yet -- it resolves dependencies (e.g. DB) which we don't want until we start |
| .environmentVariablesReset() |
| .body.append(commands) |
| .execute(); |
| } |
| |
| /** |
| * AS7 config notes and TODOs: |
| * We're using the http management interface on port managementPort |
| * We're not using any JMX. |
| * - AS 7 simply doesn't boot with Sun JMX enabled (https://issues.jboss.org/browse/JBAS-7427) |
| * - 7.1 onwards uses Remoting 3, which we haven't configured |
| * - We have generic support for jmxmp, which one could configure |
| * We're completely disabling security on the management interface. |
| * - In the future we probably want to use the as7/bin/add-user.sh script using config keys for user and password |
| * - Or we could create our own security realm and use that. |
| * We disable the root welcome page, since we can't deploy our own root otherwise |
| * We bind all interfaces to entity.hostname, rather than 127.0.0.1. |
| */ |
| @Override |
| public void customize() { |
| // Check that a password was set for the management user |
| Preconditions.checkState(Strings.isNonBlank(getManagementUsername()), "User for management realm required"); |
| String managementPassword = getManagementPassword(); |
| if (Strings.isBlank(managementPassword)) { |
| LOG.debug(this+" has no password specified for "+JBoss7Server.MANAGEMENT_PASSWORD.getName()+"; using a random string"); |
| entity.config().set(JBoss7Server.MANAGEMENT_PASSWORD, Strings.makeRandomId(8)); |
| } |
| String hashedPassword = hashPassword(getManagementUsername(), getManagementPassword(), MANAGEMENT_REALM); |
| |
| // Check that ports are all configured |
| Map<String,Integer> ports = MutableMap.<String,Integer>builder() |
| .put("managementHttpPort", getManagementHttpPort()) |
| .put("managementHttpsPort", getManagementHttpsPort()) |
| .put("managementNativePort", getManagementNativePort()) |
| .build(); |
| if (isProtocolEnabled("HTTP")) { |
| ports.put("httpPort", getHttpPort()); |
| } |
| if (isProtocolEnabled("HTTPS")) { |
| ports.put("httpsPort", getHttpsPort()); |
| } |
| Networking.checkPortsValid(ports); |
| |
| // Check hostname is defined |
| String hostname = entity.getAttribute(SoftwareProcess.HOSTNAME); |
| Preconditions.checkNotNull(hostname, "AS 7 entity must set hostname otherwise server will only be visible on localhost"); |
| |
| // Copy the install files to the run-dir and add the management user |
| newScript(CUSTOMIZING) |
| // don't set vars yet -- it resolves dependencies (e.g. DB) which we don't want until we start |
| .environmentVariablesReset() |
| .body.append( |
| format("cp -r %s/%s . || exit $!", getExpandedInstallDir(), SERVER_TYPE), |
| format("echo -e '\n%s=%s' >> %s/%s/configuration/mgmt-users.properties", |
| getManagementUsername(), hashedPassword, getRunDir(), SERVER_TYPE) |
| ) |
| .execute(); |
| |
| // Copy the keystore across, if there is one |
| if (isProtocolEnabled("HTTPS")) { |
| String keystoreUrl = Preconditions.checkNotNull(getSslKeystoreUrl(), "keystore URL must be specified if using HTTPS for "+entity); |
| String destinationSslKeystoreFile = getSslKeystoreFile(); |
| InputStream keystoreStream = resource.getResourceFromUrl(keystoreUrl); |
| getMachine().copyTo(keystoreStream, destinationSslKeystoreFile); |
| } |
| |
| // Copy the configuration file across |
| String destinationConfigFile = Os.mergePathsUnix(getRunDir(), SERVER_TYPE, "configuration", CONFIG_FILE); |
| copyTemplate(getTemplateConfigurationUrl(), destinationConfigFile); |
| |
| // Copy the initial wars to the deploys directory |
| getEntity().deployInitialWars(); |
| } |
| |
| @Override |
| public void launch() { |
| entity.sensors().set(JBoss7Server.PID_FILE, Os.mergePathsUnix(getRunDir(), PID_FILENAME)); |
| |
| // We wait for evidence of JBoss running because, using SshCliTool, |
| // we saw the ssh session return before the JBoss process was fully running |
| // so the process failed to start. |
| newScript(MutableMap.of(USE_PID_FILE, false), LAUNCHING) |
| .body.append( |
| "export LAUNCH_JBOSS_IN_BACKGROUND=true", |
| format("export JBOSS_HOME=%s", getExpandedInstallDir()), |
| format("export JBOSS_PIDFILE=%s/%s", getRunDir(), PID_FILENAME), |
| format("%s/bin/%s.sh ", getExpandedInstallDir(), SERVER_TYPE) + |
| format("--server-config %s ", CONFIG_FILE) + |
| format("-Djboss.server.base.dir=%s/%s ", getRunDir(), SERVER_TYPE) + |
| format("\"-Djboss.server.base.url=file://%s/%s\" ", getRunDir(), SERVER_TYPE) + |
| "-Djava.net.preferIPv4Stack=true " + |
| "-Djava.net.preferIPv6Addresses=false " + |
| format(" >> %s/console 2>&1 </dev/null &", getRunDir()), |
| "for i in {1..10}\n" + |
| "do\n" + |
| " grep -i 'starting' "+getRunDir()+"/console && exit\n" + |
| " sleep 1\n" + |
| "done\n" + |
| "echo \"Couldn't determine if process is running (console output does not contain 'starting'); continuing but may subsequently fail\"" |
| ) |
| .execute(); |
| } |
| |
| @Override |
| public boolean isRunning() { |
| return newScript(MutableMap.of(USE_PID_FILE, true), CHECK_RUNNING).execute() == 0; |
| } |
| |
| @Override |
| public void stop() { |
| newScript(MutableMap.of(USE_PID_FILE, true), STOPPING).environmentVariablesReset().execute(); |
| } |
| |
| @Override |
| public void kill() { |
| newScript(MutableMap.of(USE_PID_FILE, true), KILLING).execute(); |
| } |
| |
| @Override |
| protected List<String> getCustomJavaConfigOptions() { |
| return MutableList.<String>builder() |
| .addAll(super.getCustomJavaConfigOptions()) |
| .add("-Xms200m") |
| .add("-Xmx800m") |
| .add("-XX:MaxPermSize=400m") |
| .build(); |
| } |
| |
| /** |
| * Creates a hash of a username, password and security realm that is suitable for use |
| * with AS7 and Wildfire. |
| * <p/> |
| * Although AS7 has an <code>add-user.sh</code> script it is unsuitable for use in |
| * non-interactive modes. (See AS7-5061 for details.) Versions 7.1.2+ (EAP) accept |
| * a <code>--silent</code> flag. When this entity is updated past 7.1.1 we should |
| * probably use that instead. |
| * <p/> |
| * This method mirrors AS7 and Wildfire's method of hashing user's passwords. Refer |
| * to its class <code>UsernamePasswordHashUtil.generateHashedURP</code> for their |
| * implementation. |
| * |
| * @see <a href="https://issues.jboss.org/browse/AS7-5061">AS7-5061</a> |
| * @see <a href="https://github.com/jboss-remoting/jboss-sasl/blob/master/src/main/java/org/jboss/sasl/util/UsernamePasswordHashUtil.java"> |
| * UsernamePasswordHashUtil.generateHashedURP</a> |
| * @return <code>HEX(MD5(username ':' realm ':' password))</code> |
| */ |
| public static String hashPassword(String username, String password, String realm) { |
| String concat = username + ":" + realm + ":" + password; |
| byte[] hashed = Hashing.md5().hashString(concat, Charsets.UTF_8).asBytes(); |
| return BaseEncoding.base16().lowerCase().encode(hashed); |
| } |
| } |