Java tutorial
/* * Copyright (C) 2008 Universidade Federal de Campina Grande * * This file is part of OurGrid. * * OurGrid is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.ourgrid.common.executor.vmachine; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.io.FileUtils; import org.ourgrid.common.executor.Executor; import org.ourgrid.common.executor.ExecutorException; import org.ourgrid.common.executor.ExecutorHandle; import org.ourgrid.common.executor.ExecutorResult; import org.ourgrid.common.executor.FolderBasedSandboxedUnixEnvironmentUtil; import org.ourgrid.common.executor.IntegerExecutorHandle; import org.ourgrid.common.executor.config.ExecutorConfiguration; import org.ourgrid.common.executor.config.VirtualMachineExecutorConfiguration; import org.ourgrid.worker.WorkerConstants; import br.edu.ufcg.lsd.commune.container.logging.CommuneLogger; /** * Executor that performs application executions on a sandboxed environment. The virtual environment * is started using predetermined scripts. Currently the only virtual machine supported by this * executor is <i>VirtualBox (http://www.virtualbox.org)</i>. */ public class VirtualMachineExecutor implements Executor { private static final long serialVersionUID = 40L; /* * Constants used to build properties file that will be transfered to the virtual machine. The * file has the following syntax. * * APP_SCRIPT="value" * APP_EXIT="value" * APP_STDOUT="value" * APP_STDERR="value" * TERMINATION_FILE="value" * STORAGE_NAME="value" */ private static final String VIRTUAL_ENV_APP_SCRIPT_PROP = "APP_SCRIPT"; private static final String VIRTUAL_ENV_APP_EXIT_PROP = "APP_EXIT"; private static final String VIRTUAL_ENV_APP_STDOUT_PROP = "APP_STDOUT"; private static final String VIRTUAL_ENV_APP_STDERR_PROP = "APP_STDERR"; private static final String VIRTUAL_ENV_TERMINATION_FILE_PROP = "TERMINATION_FILE"; private static final String VIRTUAL_ENV_STORAGE_NAME_PROP = "STORAGE_NAME"; private static final String VIRTUAL_ENV_PROPS_FILES = "OG_OPTS"; /* * Constants that determine default names for output files. */ private static final String APP_EXIT = "app.exit"; private static final String APP_STDERR = "app.stderr"; private static final String APP_STDOUT = "app.stdout"; private static final String APP_SCRIPT_SH = "app.script.sh"; private static final String TERMINATED = ".terminated"; private static final String VBOX_VM_STORAGE = "storage"; private transient final CommuneLogger logger; /* * Location of the virtual environment properties file. */ private File virtualEnvPropertiesFile; /* * Locks that guarantee sequential executions. */ private final Lock EXEC_LOCK = new ReentrantLock(); private final Lock KILL_LOCK = new ReentrantLock(); private static final IntegerExecutorHandle HANDLE = new IntegerExecutorHandle(1); /* * Executor to perform operations. */ private final Executor executor; /* * Configured commands. */ private String startvmCmd; private String stopvmCmd; private String waitForExecutionCmd; private String machineName; private String vBoxLocation; /* * Class parameters to manipulate executions. */ private File terminationFile; private File execFile; private File stdOut; private File stdErr; private File exitStatus; private File vmStorage; private File playpen; /* * Other */ private final FolderBasedSandboxedUnixEnvironmentUtil unixFolderUtil; /** * Creates a new <code>VirtualMachineExecutor</code> using the * given <code>Executor</code> to perform tasks. * * @param osExecutor <code>Executor</code> that will execute scripts. * @param logger */ public VirtualMachineExecutor(Executor osExecutor, CommuneLogger logger) { this.executor = osExecutor; this.unixFolderUtil = new FolderBasedSandboxedUnixEnvironmentUtil(); this.logger = logger; } /* * (non-Javadoc) * @see org.ourgrid.common.executor.Executor#setConfiguration(org.ourgrid.common.executor.config.ExecutorConfiguration) */ public void setConfiguration(ExecutorConfiguration executorConfiguratrion) { this.startvmCmd = ("\"" + executorConfiguratrion.getProperty(VirtualMachineExecutorConfiguration.PROPERTY_START_VM_COMMAND) + "\""); this.stopvmCmd = ("\"" + executorConfiguratrion.getProperty(VirtualMachineExecutorConfiguration.PROPERTY_STOP_VM_COMMAND) + "\""); this.waitForExecutionCmd = ("\"" + executorConfiguratrion .getProperty(VirtualMachineExecutorConfiguration.PROPERTY_WAIT_FOR_VM_COMMAND) + "\""); this.vBoxLocation = ("\"" + executorConfiguratrion.getProperty(VirtualMachineExecutorConfiguration.PROPERTY_VBOX_LOCATION) + "\""); this.machineName = executorConfiguratrion .getProperty(VirtualMachineExecutorConfiguration.PROPERTY_MACHINE_NAME); } public void prepareAllocation() throws ExecutorException { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see org.ourgrid.common.executor.Executor#execute(java.lang.String, java.lang.String) */ public synchronized ExecutorHandle execute(String dirName, String command) throws ExecutorException { return execute(dirName, command, new HashMap<String, String>()); } /* * (non-Javadoc) * @see org.ourgrid.common.executor.Executor#execute(java.lang.String, java.lang.String, java.util.Map) */ public synchronized ExecutorHandle execute(String dirName, String command, Map<String, String> envVars) throws ExecutorException { EXEC_LOCK.lock(); try { if (isExecutionInProcess()) { throw new ExecutorException("An execution is in process"); } initEnvironmentVariables(envVars); executeRemoteCommand(dirName, command, envVars); } finally { EXEC_LOCK.unlock(); } return HANDLE; } private void initEnvironmentVariables(Map<String, String> envVars) throws ExecutorException { String uniquifier = Integer.toString((int) (Math.random() * Integer.MAX_VALUE)); this.playpen = new File(envVars.get(WorkerConstants.ENV_PLAYPEN)); this.vmStorage = new File(playpen.getAbsolutePath() + File.separator + uniquifier + VBOX_VM_STORAGE); this.terminationFile = new File(playpen.getAbsolutePath() + File.separator + uniquifier + TERMINATED); this.execFile = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_SCRIPT_SH); this.stdOut = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_STDOUT); this.stdErr = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_STDERR); this.exitStatus = new File(playpen.getAbsolutePath() + File.separator + uniquifier + APP_EXIT); this.virtualEnvPropertiesFile = new File( playpen.getAbsolutePath() + File.separator + VIRTUAL_ENV_PROPS_FILES); } private void executeRemoteCommand(String dirName, String command, Map<String, String> envVars) throws ExecutorException { try { String env_storage = envVars.get(WorkerConstants.ENV_STORAGE); command = command.replace(env_storage, vmStorage.getName()); logger.info("Asked to run command " + command); //Defining new environment variables Map<String, String> clone = new HashMap<String, String>(); clone.putAll(envVars); clone.remove(WorkerConstants.ENV_PLAYPEN); clone.remove(WorkerConstants.ENV_STORAGE); //Creating application script File script = unixFolderUtil.createScript(command, dirName, clone); //Writing virtual environment variables PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(virtualEnvPropertiesFile))); writer.println(VIRTUAL_ENV_APP_SCRIPT_PROP + "=" + execFile.getName()); writer.println(VIRTUAL_ENV_APP_EXIT_PROP + "=" + exitStatus.getName()); writer.println(VIRTUAL_ENV_APP_STDOUT_PROP + "=" + stdOut.getName()); writer.println(VIRTUAL_ENV_APP_STDERR_PROP + "=" + stdErr.getName()); writer.println(VIRTUAL_ENV_TERMINATION_FILE_PROP + "=" + terminationFile.getName()); writer.println(VIRTUAL_ENV_STORAGE_NAME_PROP + "=" + vmStorage.getName()); try { if (writer.checkError()) { throw new IOException("Unable to create Virtual environment"); } } finally { writer.close(); } //Copying files to needed location vmStorage.mkdirs(); FileUtils.copyFile(script, execFile); unixFolderUtil.copyStorageFiles(envVars, vmStorage); } catch (IOException e) { throw new ExecutorException("Unable to create remote execution script", e); } logger.debug("About to start secure environment"); //Executing command that will initiate virtual environment ExecutorHandle internalHandle = this.executor.execute(dirName, startvmCmd + " " + vBoxLocation + " " + machineName + " " + ("\"" + playpen.getAbsolutePath() + "\"")); ExecutorResult result = this.executor.getResult(internalHandle); logger.debug("Result: " + result); if (result.getExitValue() != 0) { cleanup(); throw new ExecutorException("Unable to start virtual environment \n" + result); } } /* * (non-Javadoc) * @see org.ourgrid.common.executor.Executor#getResult(org.ourgrid.common.executor.ExecutorHandle) */ public synchronized ExecutorResult getResult(ExecutorHandle handle) throws ExecutorException { try { logger.debug("About to wait for secure environment to exit"); String command = waitForExecutionCmd + " " + vBoxLocation + " " + machineName + " " + ("\"" + terminationFile.getAbsolutePath() + "\""); ExecutorHandle waitHandle = this.executor.execute(playpen.getAbsolutePath(), command); ExecutorResult result = this.executor.getResult(waitHandle); logger.debug("Result: " + result); if (result.getExitValue() != 0) { throw new ExecutorException("Wait command has finished with errors\n" + result); } return getResult(); } catch (ExecutorException e) { throw new ExecutorException("Unable to wait for application", e); } finally { KILL_LOCK.lock(); try { cleanup(); } finally { KILL_LOCK.unlock(); } } } private ExecutorResult getResult() throws ExecutorException { KILL_LOCK.lock(); try { if (!isExecutionInProcess()) { throw new ExecutorException("No execution is in process, probably it was killed."); } //Acquiring results ExecutorResult result = new ExecutorResult(); try { unixFolderUtil.catchOutputFromFile(result, stdOut, stdErr, exitStatus); } catch (Throwable e) { throw new ExecutorException("Unable to catch output ", e); } finally { stopVm(); } return result; } finally { KILL_LOCK.unlock(); } } public void kill(ExecutorHandle handle) throws ExecutorException { try { KILL_LOCK.lock(); EXEC_LOCK.lock(); if (!isExecutionInProcess()) { throw new ExecutorException("No execution is in process"); } stopVm(); } finally { cleanup(); KILL_LOCK.unlock(); EXEC_LOCK.unlock(); } } private void stopVm() throws ExecutorException { logger.debug("About to kill secure environment"); ExecutorHandle internalHandle = executor.execute(playpen.getAbsolutePath(), stopvmCmd + " " + vBoxLocation + " " + machineName); ExecutorResult result = executor.getResult(internalHandle); logger.debug("Result: " + result); if (result.getExitValue() != 0) { throw new ExecutorException("Unable to kill virtual environment \n" + result); } } private void cleanup() { this.playpen = null; this.vmStorage = null; this.terminationFile = null; this.execFile = null; this.stdOut = null; this.stdErr = null; this.exitStatus = null; } protected boolean isExecutionInProcess() { return playpen != null; } public void chmod(File file, String perm) throws ExecutorException { executor.chmod(file, perm); } public void finishExecution() throws ExecutorException { // TODO Auto-generated method stub } public void killCommand(ExecutorHandle handle) throws ExecutorException { // TODO Auto-generated method stub } public void killPreparingAllocation() throws ExecutorException { // TODO Auto-generated method stub } public void finishCommandExecution(ExecutorHandle handle) throws ExecutorException { // TODO Auto-generated method stub } public void finishPrepareAllocation() throws ExecutorException { // TODO Auto-generated method stub } }