Java tutorial
/* * Copyright 2012 The Clustermeister Team. * * 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 com.github.nethad.clustermeister.provisioning.ec2; import com.github.nethad.clustermeister.api.LogLevel; import com.github.nethad.clustermeister.node.common.NodeConfigurationUtils; import com.github.nethad.clustermeister.provisioning.FileResource; import com.github.nethad.clustermeister.provisioning.InputStreamResource; import com.github.nethad.clustermeister.provisioning.RemoteResourceManager; import com.github.nethad.clustermeister.provisioning.Resource; import com.github.nethad.clustermeister.provisioning.utils.JCloudsSshClientWrapper; import com.github.nethad.clustermeister.provisioning.utils.SSHClientException; import com.google.common.base.Charsets; import static com.google.common.base.Preconditions.*; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Monitor; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import java.util.Observable; import java.util.Properties; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.Processor; import org.jclouds.domain.LoginCredentials; import org.jclouds.io.Payloads; import org.jclouds.ssh.SshClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author daniel */ public abstract class AmazonEC2JPPFDeployer extends Observable { protected int getNumberOfProcessingThreads() { int numberOfCores = 0; for (Processor processor : metadata.getHardware().getProcessors()) { numberOfCores += (int) processor.getCores(); } return numberOfCores; } public static enum Event { DEPLOYMENT_PREPARED, RESOURCES_PRELOADED, JPPF_CONFIGURATED, DEPLOYMENT_FINISHED }; protected static final String UUID_PREFIX = "UUID="; protected final static Logger logger = LoggerFactory.getLogger(AmazonEC2JPPFDeployer.class); protected static final String INIT_LOG = "init.log"; protected static final String CLUSTERMEISTER_BIN = "clustermeister-bin"; private static final Monitor driverMM = new Monitor(false); private static final Monitor nodeMM = new Monitor(false); //TODO: make sure this does not cause a memory leak. protected static final Map<String, Monitor> instanceDriverMonitors = new HashMap<String, Monitor>(); protected static final Map<String, Monitor> instanceNodeMonitors = new HashMap<String, Monitor>(); protected final String zipFile; protected final String crc32File; protected final String jppfConfigFileName; protected final String log4jConfigFileName; protected final String startScript; protected final String startScriptArguments; protected final String jppfFolder; protected final LoginCredentials loginCredentials; protected final ComputeServiceContext context; protected final NodeMetadata metadata; protected final AmazonNodeConfiguration nodeConfiguration; private SshClient sshClient = null; private String directoryName = null; static protected Monitor getDriverMonitor(NodeMetadata metadata) { driverMM.enter(); try { Monitor driverMonitor = instanceDriverMonitors.get(metadata.getId()); if (driverMonitor == null) { driverMonitor = new Monitor(false); instanceDriverMonitors.put(metadata.getId(), driverMonitor); } return driverMonitor; } finally { driverMM.leave(); } } static protected Monitor getNodeMonitor(NodeMetadata metadata) { nodeMM.enter(); try { Monitor nodeMonitor = instanceNodeMonitors.get(metadata.getId()); if (nodeMonitor == null) { nodeMonitor = new Monitor(false); instanceNodeMonitors.put(metadata.getId(), nodeMonitor); } return nodeMonitor; } finally { nodeMM.leave(); } } static void removeDriverMonitor(String instanceId) { driverMM.enter(); try { instanceDriverMonitors.remove(instanceId); } finally { driverMM.leave(); } } static void removeNodeMonitor(String instanceId) { nodeMM.enter(); try { instanceNodeMonitors.remove(instanceId); } finally { nodeMM.leave(); } } public AmazonEC2JPPFDeployer(LoginCredentials loginCredentials, ComputeServiceContext context, NodeMetadata metadata, AmazonNodeConfiguration nodeConfiguration, String zipFile, String crc32File, String jppfConfigFileName, String log4jConfigFileName, String startScript, String startScriptArguments, String jppfFolder) { this.loginCredentials = loginCredentials; this.context = context; this.metadata = metadata; this.nodeConfiguration = nodeConfiguration; this.zipFile = zipFile; this.crc32File = crc32File; this.jppfConfigFileName = jppfConfigFileName; this.log4jConfigFileName = log4jConfigFileName; this.startScript = startScript; this.startScriptArguments = startScriptArguments; this.jppfFolder = jppfFolder; } protected abstract void checkPrecondition() throws Throwable; protected abstract Properties getSettings(); protected abstract Monitor getMonitor(); public String deploy() { String uuid = null; SshClient ssh = getSSHClient(); ssh.connect(); JCloudsSshClientWrapper jCloudsSshClientWrapper = new JCloudsSshClientWrapper(sshClient, metadata.getLoginPort()); RemoteResourceManager remoteResourceManager = new RemoteResourceManager(jCloudsSshClientWrapper); Resource jppfZipResource = new InputStreamResource(String.format("/%s", zipFile), this.getClass(), zipFile, getDirectoryName()); jppfZipResource.setUnzipContents(true); remoteResourceManager.addResource(jppfZipResource); try { checkPrecondition(); final String nodeTypeStr = nodeConfiguration.getType().toString(); logger.debug("Deploying JPPF-{} to {} ({}).", new Object[] { nodeTypeStr, metadata.getId(), getPublicIp() }); prepareJPPF(remoteResourceManager); sendEvent(Event.DEPLOYMENT_PREPARED); Monitor monitor = getMonitor(); monitor.enter(); try { remoteResourceManager.uploadResources(); for (File artifact : nodeConfiguration.getArtifactsToPreload()) { remoteResourceManager .addResource(new FileResource(artifact, getDirectoryName() + jppfFolder + "lib")); } remoteResourceManager.uploadResources(); remoteResourceManager.deployResources(); sendEvent(Event.RESOURCES_PRELOADED); } finally { monitor.leave(); } setupJPPF(); uploadConfiguration("JPPF Node Configuration generated by Clustermeister.", getSettings(), jppfConfigFileName); uploadConfiguration("LOG4J Configuration generated by Clustermeister.", NodeConfigurationUtils.getLog4JConfiguration( nodeConfiguration.getLogLevel().or(LogLevel.INFO).toString(), nodeConfiguration.isRemoteLoggingActivataed().or(Boolean.FALSE), nodeConfiguration.getDriverAddress(), //TODO: driver address may not be correct in a remote driver scenario! configurable port nodeConfiguration.getRemoteLoggingPort().or(52321)), log4jConfigFileName); sendEvent(Event.JPPF_CONFIGURATED); logger.debug("Starting JPPF-{} on {}...", nodeTypeStr, metadata.getId()); startJPPF(); uuid = getUUID(); sendEvent(Event.DEPLOYMENT_FINISHED); logger.debug("JPPF-{} deployed on {}.", nodeTypeStr, metadata.getId()); } catch (Throwable ex) { logger.debug("Deployment of JPPF-{} failed.", nodeConfiguration.getType().toString()); throw new IllegalStateException(ex); } finally { if (ssh != null) { ssh.disconnect(); } } return uuid; } protected void sendEvent(Event event) { setChanged(); notifyObservers(event); } protected void prepareJPPF(RemoteResourceManager remoteResourceManager) { execute(String.format("rm -rf %s", getDirectoryName())); try { remoteResourceManager.prepareResourceDirectory(); } catch (SSHClientException ex) { logger.error("Could not prepare resource manager.", ex); } } protected ExecResponse execute(String command) { logger.trace("Executing {}", command); ExecResponse response = getSSHClient().exec(command); return logExecResponse(response); } protected ExecResponse logExecResponse(ExecResponse response) { logger.trace("Exit Code: {}", response.getExitStatus()); if (response.getError() != null && !response.getError().isEmpty()) { logger.warn("Execution error: {}.", response.getError()); } return response; } protected String getStringResult(ExecResponse response) { return response.getOutput().trim(); } protected void setupJPPF() { execute("chmod +x " + getDirectoryName() + jppfFolder + startScript); } protected void upload(InputStream source, String to) { logger.debug("Uploading {}", to); getSSHClient().put(to, Payloads.newInputStreamPayload(source)); } protected Properties getPropertiesFromStream(InputStream properties) { Properties nodeProperties = new Properties(); try { nodeProperties.load(properties); } catch (IOException ex) { logger.warn("Can not read properties file.", ex); } return nodeProperties; } protected InputStream getRunningConfig(java.util.Properties properties, String comment) { StringWriter runningConfig = new StringWriter(); try { properties.store(runningConfig, comment); } catch (IOException ex) { logger.warn("Can not write configuration: {}.", comment, ex); } return new ByteArrayInputStream(runningConfig.toString().getBytes(Charsets.UTF_8)); } protected String getDirectoryName() { if (directoryName != null) { return directoryName; } directoryName = "jppf-" + nodeConfiguration.getType().toString().toLowerCase() + "-" + metadata.getId().replace("/", "_") + "_" + nodeConfiguration.getManagementPort(); return directoryName; } protected String getPrivateIp() { String privateIp = Iterables.getFirst(metadata.getPrivateAddresses(), null); checkState(privateIp != null, "No private IP set."); return privateIp; } protected String getPublicIp() { String publicIp = Iterables.getFirst(metadata.getPublicAddresses(), null); checkState(publicIp != null, "No public IP set."); return publicIp; } protected SshClient getSSHClient() { if (sshClient == null) { sshClient = context.utils().sshForNode() .apply(NodeMetadataBuilder.fromNodeMetadata(metadata).credentials(loginCredentials).build()); } return sshClient; } protected void uploadConfiguration(String comment, Properties configuration, String destination) { logger.debug("Uploading config {}.", destination); upload(getRunningConfig(configuration, comment), String.format("%s%s", getDirectoryName(), destination)); } protected String startJPPF() { final StringBuilder script = new StringBuilder("cd /home/ec2-user/").append(getDirectoryName()) .append(jppfFolder).append(" && nohup ./").append(startScript); if (startScriptArguments != null && !startScriptArguments.isEmpty()) { script.append(" ").append(startScriptArguments); if (nodeConfiguration.getJvmOptions().isPresent()) { script.append(nodeConfiguration.getJvmOptions().get()); } } script.append(" > ").append(INIT_LOG).append(" 2>&1"); return execute(script.toString()).getOutput(); } protected void closeInputstream(final InputStream in) { try { in.close(); } catch (IOException ex) { logger.warn("Could not close Inputstream.", ex); } } protected String getUUID() { logger.debug("Fetching UUID from {}", INIT_LOG); String output = getStringResult( execute("cat " + getDirectoryName() + jppfFolder + INIT_LOG + " | grep " + UUID_PREFIX)); checkNotNull(output); checkState(output.contains(UUID_PREFIX)); int beginIndex = output.indexOf(UUID_PREFIX) + UUID_PREFIX.length(); int endIndex = beginIndex + 32; String uuid = output.substring(beginIndex, endIndex); logger.debug("Got UUID {}.", uuid); return uuid; } }