/************************************************************************************** * Copyright (C) 2009 Progress Software, Inc. All rights reserved. * * http://fusesource.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the AGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.fusesource.cloudmix.agent.mop; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import com.google.common.collect.Lists; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.fusesource.cloudmix.common.GridClient; import org.fusesource.cloudmix.common.dto.ProvisioningAction; import org.fusesource.cloudmix.common.util.FileUtils; import org.fusesource.cloudmix.common.util.Strings; import org.fusesource.mop.MOP; import org.fusesource.mop.ProcessRunner; /** * @version $Revision: 1.1 $ */ public class MopProcess { private static final transient Log LOG = LogFactory.getLog(MopProcess.class); private MopAgent mopAgent; private ProvisioningAction action; private String credentials; private String commandLine; private ClassLoader mopClassLoader; private MOP mop = new MOP(); private int statusCode = -1; private Thread thread; private AtomicBoolean completed = new AtomicBoolean(false); private File workDirectory; private String featureId; public MopProcess(MopAgent mopAgent, ProvisioningAction action, String credentials, String commandLine, ClassLoader mopClassLoader) { this.mopAgent = mopAgent; this.action = action; this.featureId = action.getFeature(); this.credentials = credentials; this.commandLine = commandLine; this.mopClassLoader = mopClassLoader; this.workDirectory = mopAgent.createProcessDirectory(action); } public String getFeatureId() { return featureId; } public String getId() { return action.getFeature(); } public ProvisioningAction getAction() { return action; } public String getCommandLine() { return commandLine; } public String getCredentials() { return credentials; } public File getWorkDirectory() { return workDirectory; } public void start() throws Exception { final List<String> argList = Lists.newArrayList(); if (commandLine != null) { StringTokenizer iter = new StringTokenizer(commandLine); while (iter.hasMoreTokens()) { argList.add(iter.nextToken()); } } if (argList.isEmpty()) { throw new IllegalArgumentException("No arguments specified"); } // Propagate the profile properties to the forked process... String profile = mopAgent.getProfileFor(action); if (profile == null) { LOG.warn("Could not find profile ID for action " + action); } else { GridClient gridClient = mopAgent.getClient(); Properties properties = gridClient.getProperties(profile); if (properties == null) { LOG.warn("No properties available for profile: " + profile); } else { for (Entry<Object, Object> entry : properties.entrySet()) { mop.setSystemProperty(Strings.asString(entry.getKey()), Strings.asString(entry.getValue())); } } } // Propagate repository props to the forked process: for (Entry<String, String> entry : mop.getRepository().getRepositorySystemProps().entrySet()) { mop.setSystemProperty(entry.getKey(), entry.getValue()); } // lets ensure the command will spin off to another process: if (!(argList.contains("fork") || argList.contains("exec"))) { LOG.info("Adding Fork To MopProcess: " + argList); argList.add(0, "fork"); } // lets transform the class loader to exclude the parent (to avoid maven & jetty plugin dependencies) // lets run in a background thread thread = new Thread("Feature: " + getId() + "MOP " + argList) { @Override public void run() { LOG.debug("Using class loader: " + mopClassLoader + " of type: " + mopClassLoader.getClass()); dumpClassLoader(mopClassLoader); File dir = getWorkDirectory(); FileUtils.createDirectory(dir); try { LOG.info("Created working directory " + dir.getCanonicalPath() + " for feature " + getId()); } catch (IOException e1) { LOG.error("Problem with working directory for " + dir + " for feature " + getId()); } mop.setWorkingDirectory(dir); Thread.currentThread().setContextClassLoader(mopClassLoader); LOG.info("Starting feature: " + getId() + " via MOP: " + argList); String[] args = argList.toArray(new String[argList.size()]); try { statusCode = mop.executeAndWait(args); LOG.info("Stopped feature: " + getId() + " with status code: " + statusCode); } catch (Exception e) { LOG.error("Failed running feature: " + getId() + ". Reason: " + e, e); } finally { clear(); } } }; thread.setContextClassLoader(mopClassLoader); thread.start(); } void dumpClassLoader(ClassLoader cl) { if (LOG.isDebugEnabled()) { if (cl instanceof URLClassLoader) { URLClassLoader urlClassLoader = (URLClassLoader) cl; URL[] urls = urlClassLoader.getURLs(); for (URL url : urls) { LOG.debug("ClassLoader URL: " + url); } } ClassLoader parent = cl.getParent(); if (parent != null) { LOG.debug("Parent Class Loader: " + parent); dumpClassLoader(parent); } } } public void stop() throws Exception { if (mop != null) { ProcessRunner processRunner = mop.getProcessRunner(); if (processRunner != null) { processRunner.kill(); } } clear(); } public boolean isCompleted() { return completed.get(); } private void clear() { completed.set(true); mop = null; thread = null; } }