You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/05/29 19:22:00 UTC

[07/27] incubator-brooklyn git commit: Adds (WIP) outline for VanillaWindowsProcess

Adds (WIP) outline for VanillaWindowsProcess


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/2b07bed4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/2b07bed4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/2b07bed4

Branch: refs/heads/master
Commit: 2b07bed4f2ed5e00b0422161455c2baa91ddea60
Parents: ce2a7c2
Author: Martin Harris <gi...@nakomis.com>
Authored: Mon Apr 13 10:48:58 2015 +0100
Committer: Richard Downer <ri...@apache.org>
Committed: Thu May 28 17:27:34 2015 +0100

----------------------------------------------------------------------
 .../location/basic/WinRmMachineLocation.java    |   8 +
 .../basic/AbstractSoftwareProcessDriver.java    | 243 +++++++++++++++++--
 .../basic/AbstractSoftwareProcessSshDriver.java | 197 +--------------
 .../AbstractSoftwareProcessWinRmDriver.java     |  94 ++++++-
 .../basic/AbstractVanillaSoftwareProcess.java   |  32 +++
 .../AbstractVanillaSoftwareProcessDriver.java   |  22 ++
 .../entity/basic/VanillaSoftwareProcess.java    |  12 +-
 .../basic/VanillaSoftwareProcessDriver.java     |   3 +-
 .../entity/basic/VanillaWindowsProcess.java     |  27 +++
 .../basic/VanillaWindowsProcessDriver.java      |  23 ++
 .../entity/basic/VanillaWindowsProcessImpl.java |  38 +++
 .../basic/VanillaWindowsProcessWinRmDriver.java |  54 +++++
 12 files changed, 527 insertions(+), 226 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java b/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
index 54e3829..186fc4f 100644
--- a/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
+++ b/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java
@@ -91,12 +91,20 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc
         return null;
     }
 
+    public int executeScript(String script) {
+        return executeScript(ImmutableList.of(script));
+    }
+
     public int executeScript(List<String> script) {
         WinRmTool winRmTool = WinRmTool.connect(getHostname(), getUsername(), getPassword());
         WinRmToolResponse response = winRmTool.executeScript(script);
         return response.getStatusCode();
     }
 
+    public int executePsScript(String psScript) {
+        return executePsScript(ImmutableList.of(psScript));
+    }
+
     public int executePsScript(List<String> psScript) {
         WinRmTool winRmTool = WinRmTool.connect(getHostname(), getUsername(), getPassword());
         WinRmToolResponse response = winRmTool.executePs(psScript);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
index 67af47f..9762c4f 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessDriver.java
@@ -18,18 +18,26 @@
  */
 package brooklyn.entity.basic;
 
+import static brooklyn.util.JavaGroovyEquivalents.elvis;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import java.io.File;
 import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
 import java.util.Map;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.config.ConfigKey;
+import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.location.Location;
 import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.exceptions.Exceptions;
+import brooklyn.util.os.Os;
+import brooklyn.util.stream.ReaderInputStream;
 import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
@@ -131,7 +139,7 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr
                 DynamicTasks.queue("post-install-command", new Runnable() { public void run() {
                     runPostInstallCommand(entity.getConfig(BrooklynConfigKeys.POST_INSTALL_COMMAND));
                 }});
-            };
+            }
 
             DynamicTasks.queue("customize", new Runnable() { public void run() {
                 waitForConfigKey(BrooklynConfigKeys.CUSTOMIZE_LATCH);
@@ -176,10 +184,8 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr
 
     public abstract void runPreInstallCommand(String command);
     public abstract void setup();
-    public abstract void copyInstallResources();
     public abstract void install();
     public abstract void runPostInstallCommand(String command);
-    public abstract void copyRuntimeResources();
     public abstract void customize();
     public abstract void runPreLaunchCommand(String command);
     public abstract void launch();
@@ -197,25 +203,27 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr
 
     @Override
     public void restart() {
-        DynamicTasks.queue("stop (best effort)", new Runnable() { public void run() {
-            DynamicTasks.markInessential();
-            boolean previouslyRunning = isRunning();
-            try {
-                ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STOPPING);
-                stop();
-            } catch (Exception e) {
-                // queue a failed task so that there is visual indication that this task had a failure,
-                // without interrupting the parent
-                if (previouslyRunning) {
-                    log.warn(getEntity() + " restart: stop failed, when was previously running (ignoring)", e);
-                    DynamicTasks.queue(Tasks.fail("Primary job failure (when previously running)", e));
-                } else {
-                    log.debug(getEntity() + " restart: stop failed (but was not previously running, so not a surprise)", e);
-                    DynamicTasks.queue(Tasks.fail("Primary job failure (when not previously running)", e));
+        DynamicTasks.queue("stop (best effort)", new Runnable() {
+            public void run() {
+                DynamicTasks.markInessential();
+                boolean previouslyRunning = isRunning();
+                try {
+                    ServiceStateLogic.setExpectedState(getEntity(), Lifecycle.STOPPING);
+                    stop();
+                } catch (Exception e) {
+                    // queue a failed task so that there is visual indication that this task had a failure,
+                    // without interrupting the parent
+                    if (previouslyRunning) {
+                        log.warn(getEntity() + " restart: stop failed, when was previously running (ignoring)", e);
+                        DynamicTasks.queue(Tasks.fail("Primary job failure (when previously running)", e));
+                    } else {
+                        log.debug(getEntity() + " restart: stop failed (but was not previously running, so not a surprise)", e);
+                        DynamicTasks.queue(Tasks.fail("Primary job failure (when not previously running)", e));
+                    }
+                    // the above queued tasks will cause this task to be indicated as failed, with an indication of severity
                 }
-                // the above queued tasks will cause this task to be indicated as failed, with an indication of severity
             }
-        }});
+        });
 
         if (doFullStartOnRestart()) {
             DynamicTasks.waitForLast();
@@ -251,6 +259,172 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr
         return resource.getResourceFromUrl(url);
     }
 
+    /**
+     * Files and templates to be copied to the server <em>before</em> installation. This allows the {@link #install()}
+     * process to have access to all required resources.
+     * <p>
+     * Will be prefixed with the entity's {@link #getInstallDir() install directory} if relative.
+     *
+     * @see SoftwareProcess#INSTALL_FILES
+     * @see SoftwareProcess#INSTALL_TEMPLATES
+     * @see #copyRuntimeResources()
+     */
+    public void copyInstallResources() {
+
+        // Ensure environment variables are not looked up here, otherwise sub-classes might
+        // lookup port numbers and fail with ugly error if port is not set; better to wait
+        // until in Entity's code (e.g. customize) where such checks are done explicitly.
+        createDirectory(getInstallDir(), "create install directory");
+
+        // TODO see comment in copyResource, that should be queued as a task like the above
+        // (better reporting in activities console)
+
+        Map<String, String> installFiles = entity.getConfig(SoftwareProcess.INSTALL_FILES);
+        if (installFiles != null && installFiles.size() > 0) {
+            for (String source : installFiles.keySet()) {
+                String target = installFiles.get(source);
+                String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getInstallDir(), target);
+                copyResource(source, destination, true);
+            }
+        }
+
+        Map<String, String> installTemplates = entity.getConfig(SoftwareProcess.INSTALL_TEMPLATES);
+        if (installTemplates != null && installTemplates.size() > 0) {
+            for (String source : installTemplates.keySet()) {
+                String target = installTemplates.get(source);
+                String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getInstallDir(), target);
+                copyTemplate(source, destination, true, MutableMap.<String, Object>of());
+            }
+        }
+
+    }
+
+    protected abstract void createDirectory(String directoryName, String summaryForLogging);
+
+    /**
+     * Files and templates to be copied to the server <em>after</em> customisation. This allows overwriting of
+     * existing files such as entity configuration which may be copied from the installation directory
+     * during the {@link #customize()} process.
+     * <p>
+     * Will be prefixed with the entity's {@link #getRunDir() run directory} if relative.
+     *
+     * @see SoftwareProcess#RUNTIME_FILES
+     * @see SoftwareProcess#RUNTIME_TEMPLATES
+     * @see #copyInstallResources()
+     */
+    public void copyRuntimeResources() {
+        try {
+            // Ensure environment variables are not looked up here, otherwise sub-classes might
+            // lookup port numbers and fail with ugly error if port is not set. It could also
+            // cause us to block for attribute ready earlier than we need.
+            DynamicTasks.queue(SshEffectorTasks.ssh("mkdir -p " + getRunDir()).summary("create run directory")
+                    .requiringExitCodeZero()).get();
+
+            Map<String, String> runtimeFiles = entity.getConfig(SoftwareProcess.RUNTIME_FILES);
+            if (runtimeFiles != null && runtimeFiles.size() > 0) {
+                for (String source : runtimeFiles.keySet()) {
+                    String target = runtimeFiles.get(source);
+                    String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getRunDir(), target);
+                    copyResource(source, destination, true);
+                }
+            }
+
+            Map<String, String> runtimeTemplates = entity.getConfig(SoftwareProcess.RUNTIME_TEMPLATES);
+            if (runtimeTemplates != null && runtimeTemplates.size() > 0) {
+                for (String source : runtimeTemplates.keySet()) {
+                    String target = runtimeTemplates.get(source);
+                    String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getRunDir(), target);
+                    copyTemplate(source, destination, true, MutableMap.<String, Object>of());
+                }
+            }
+        } catch (Exception e) {
+            log.warn("Error copying runtime resources", e);
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    /**
+     * @param template File to template and copy.
+     * @param target Destination on server.
+     * @return The exit code the SSH command run.
+     */
+    public int copyTemplate(File template, String target) {
+        return copyTemplate(template.toURI().toASCIIString(), target);
+    }
+
+    /**
+     * @param template URI of file to template and copy, e.g. file://.., http://.., classpath://..
+     * @param target Destination on server.
+     * @return The exit code of the SSH command run.
+     */
+    public int copyTemplate(String template, String target) {
+        return copyTemplate(template, target, false, ImmutableMap.<String, String>of());
+    }
+
+    /**
+     * @param template URI of file to template and copy, e.g. file://.., http://.., classpath://..
+     * @param target Destination on server.
+     * @param extraSubstitutions Extra substitutions for the templater to use, for example
+     *               "foo" -> "bar", and in a template ${foo}.
+     * @return The exit code of the SSH command run.
+     */
+    public int copyTemplate(String template, String target, boolean createParent, Map<String, ?> extraSubstitutions) {
+        String data = processTemplate(template, extraSubstitutions);
+        return copyResource(MutableMap.<Object,Object>of(), new StringReader(data), target, createParent);
+    }
+
+    public abstract int copyResource(Map<Object,Object> sshFlags, String source, String target, boolean createParentDir);
+
+    public abstract int copyResource(Map<Object,Object> sshFlags, InputStream source, String target, boolean createParentDir);
+
+    /**
+     * @param file File to copy.
+     * @param target Destination on server.
+     * @return The exit code the SSH command run.
+     */
+    public int copyResource(File file, String target) {
+        return copyResource(file.toURI().toASCIIString(), target);
+    }
+
+    /**
+     * @param resource URI of file to copy, e.g. file://.., http://.., classpath://..
+     * @param target Destination on server.
+     * @return The exit code of the SSH command run
+     */
+    public int copyResource(String resource, String target) {
+        return copyResource(MutableMap.of(), resource, target);
+    }
+
+    public int copyResource(String resource, String target, boolean createParentDir) {
+        return copyResource(MutableMap.of(), resource, target, createParentDir);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public int copyResource(Map sshFlags, String source, String target) {
+        return copyResource(sshFlags, source, target, false);
+    }
+
+    /**
+     * @see #copyResource(Map, InputStream, String, boolean)
+     */
+    public int copyResource(Reader source, String target) {
+        return copyResource(MutableMap.of(), source, target, false);
+    }
+
+    /**
+     * @see #copyResource(Map, InputStream, String, boolean)
+     */
+    public int copyResource(Map<Object,Object> sshFlags, Reader source, String target, boolean createParent) {
+        return copyResource(sshFlags, new ReaderInputStream(source), target, createParent);
+    }
+
+    /**
+     * @see #copyResource(Map, InputStream, String, boolean)
+     */
+    public int copyResource(InputStream source, String target) {
+        return copyResource(MutableMap.of(), source, target, false);
+    }
+
     public String getResourceAsString(String url) {
         return resource.getResourceAsString(url);
     }
@@ -289,4 +463,33 @@ public abstract class AbstractSoftwareProcessDriver implements SoftwareProcessDr
         Object val = entity.getConfig(configKey);
         if (val != null) log.debug("{} finished waiting for {} (value {}); continuing...", new Object[] {this, configKey, val});
     }
+
+    /**
+     * @deprecated since 0.5.0; instead rely on {@link brooklyn.entity.drivers.downloads.DownloadResolverManager} to include local-repo, such as:
+     *
+     * <pre>
+     * {@code
+     * DownloadResolver resolver = Entities.newDownloader(this);
+     * List<String> urls = resolver.getTargets();
+     * }
+     * </pre>
+     */
+    protected String getEntityVersionLabel() {
+        return getEntityVersionLabel("_");
+    }
+
+    /**
+     * @deprecated since 0.5.0; instead rely on {@link brooklyn.entity.drivers.downloads.DownloadResolverManager} to include local-repo
+     */
+    protected String getEntityVersionLabel(String separator) {
+        return elvis(entity.getEntityType().getSimpleName(),
+                entity.getClass().getName())+(getVersion() != null ? separator+getVersion() : "");
+    }
+
+    public String getVersion() {
+        return getEntity().getConfig(SoftwareProcess.SUGGESTED_VERSION);
+    }
+
+    public abstract String getRunDir();
+    public abstract String getInstallDir();
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
index d731dc5..597a967 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessSshDriver.java
@@ -22,10 +22,7 @@ import static brooklyn.util.JavaGroovyEquivalents.elvis;
 import static brooklyn.util.JavaGroovyEquivalents.groovyTruth;
 
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,7 +39,6 @@ import brooklyn.entity.effector.EffectorTasks;
 import brooklyn.entity.software.SshEffectorTasks;
 import brooklyn.event.feed.ConfigToAttributes;
 import brooklyn.location.basic.SshMachineLocation;
-import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.internal.ssh.SshTool;
@@ -50,7 +46,6 @@ import brooklyn.util.internal.ssh.sshj.SshjTool;
 import brooklyn.util.os.Os;
 import brooklyn.util.ssh.BashCommands;
 import brooklyn.util.stream.KnownSizeInputStream;
-import brooklyn.util.stream.ReaderInputStream;
 import brooklyn.util.stream.Streams;
 import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.task.Tasks;
@@ -61,7 +56,6 @@ import brooklyn.util.time.Duration;
 
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
@@ -112,10 +106,6 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
         return (SshMachineLocation) super.getLocation();
     }
 
-    public String getVersion() {
-        return getEntity().getConfig(SoftwareProcess.SUGGESTED_VERSION);
-    }
-
     /**
      * Name to be used in the local repo, when looking for the download file.
      */
@@ -131,28 +121,6 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
         return "tar.gz";
     }
     
-    /**
-     * @deprecated since 0.5.0; instead rely on {@link DownloadResolverManager} to include local-repo, such as:
-     * 
-     * <pre>
-     * {@code
-     * DownloadResolver resolver = Entities.newDownloader(this);
-     * List<String> urls = resolver.getTargets();
-     * }
-     * </pre>
-     */
-    protected String getEntityVersionLabel() {
-        return getEntityVersionLabel("_");
-    }
-    
-    /**
-     * @deprecated since 0.5.0; instead rely on {@link DownloadResolverManager} to include local-repo
-     */
-    protected String getEntityVersionLabel(String separator) {
-        return elvis(entity.getEntityType().getSimpleName(),  
-                entity.getClass().getName())+(getVersion() != null ? separator+getVersion() : "");
-    }
-    
     protected void setInstallDir(String installDir) {
         this.installDir = installDir;
         entity.setAttribute(SoftwareProcess.INSTALL_DIR, installDir);
@@ -324,94 +292,16 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
         return getMachine().execScript(flags, summaryForLogging, script, environment);
     }
 
-    /**
-     * Files and templates to be copied to the server <em>before</em> installation. This allows the {@link #install()}
-     * process to have access to all required resources. 
-     * <p>
-     * Will be prefixed with the entity's {@link #getInstallDir() install directory} if relative.
-     *
-     * @see SoftwareProcess#INSTALL_FILES
-     * @see SoftwareProcess#INSTALL_TEMPLATES
-     * @see #copyRuntimeResources()
-     */
     @Override
     public void copyInstallResources() {
-        getLocation().acquireMutex("installing "+elvis(entity,this),  "installation lock at host for files and templates");
+        getLocation().acquireMutex("installing " + elvis(entity, this), "installation lock at host for files and templates");
         try {
-            // Ensure environment variables are not looked up here, otherwise sub-classes might
-            // lookup port numbers and fail with ugly error if port is not set; better to wait
-            // until in Entity's code (e.g. customize) where such checks are done explicitly.
-            DynamicTasks.queue(SshEffectorTasks.ssh("mkdir -p " + getInstallDir()).summary("create install directory")
-                .requiringExitCodeZero()).get();
-
-            // TODO see comment in copyResource, that should be queued as a task like the above
-            // (better reporting in activities console)
-            
-            Map<String, String> installFiles = entity.getConfig(SoftwareProcess.INSTALL_FILES);
-            if (installFiles != null && installFiles.size() > 0) {
-                for (String source : installFiles.keySet()) {
-                    String target = installFiles.get(source);
-                    String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getInstallDir(), target);
-                    copyResource(source, destination, true);
-                }
-            }
-
-            Map<String, String> installTemplates = entity.getConfig(SoftwareProcess.INSTALL_TEMPLATES);
-            if (installTemplates != null && installTemplates.size() > 0) {
-                for (String source : installTemplates.keySet()) {
-                    String target = installTemplates.get(source);
-                    String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getInstallDir(), target);
-                    copyTemplate(source, destination, true, MutableMap.<String, Object>of());
-                }
-            }
+            super.copyInstallResources();
         } catch (Exception e) {
             log.warn("Error copying install resources", e);
             throw Exceptions.propagate(e);
         } finally {
-            getLocation().releaseMutex("installing "+elvis(entity,this));
-        }
-    }
-
-    /**
-     * Files and templates to be copied to the server <em>after</em> customisation. This allows overwriting of
-     * existing files such as entity configuration which may be copied from the installation directory
-     * during the {@link #customize()} process.
-     * <p>
-     * Will be prefixed with the entity's {@link #getRunDir() run directory} if relative.
-     *
-     * @see SoftwareProcess#RUNTIME_FILES
-     * @see SoftwareProcess#RUNTIME_TEMPLATES
-     * @see #copyInstallResources()
-     */
-    @Override
-    public void copyRuntimeResources() {
-        try {
-            // Ensure environment variables are not looked up here, otherwise sub-classes might
-            // lookup port numbers and fail with ugly error if port is not set. It could also
-            // cause us to block for attribute ready earlier than we need.
-            DynamicTasks.queue(SshEffectorTasks.ssh("mkdir -p " + getRunDir()).summary("create run directory")
-                .requiringExitCodeZero()).get();
-
-            Map<String, String> runtimeFiles = entity.getConfig(SoftwareProcess.RUNTIME_FILES);
-            if (runtimeFiles != null && runtimeFiles.size() > 0) {
-                for (String source : runtimeFiles.keySet()) {
-                    String target = runtimeFiles.get(source);
-                    String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getRunDir(), target);
-                    copyResource(source, destination, true);
-                }
-            }
-
-            Map<String, String> runtimeTemplates = entity.getConfig(SoftwareProcess.RUNTIME_TEMPLATES);
-            if (runtimeTemplates != null && runtimeTemplates.size() > 0) {
-                for (String source : runtimeTemplates.keySet()) {
-                    String target = runtimeTemplates.get(source);
-                    String destination = Os.isAbsolutish(target) ? target : Os.mergePathsUnix(getRunDir(), target);
-                    copyTemplate(source, destination, true, MutableMap.<String, Object>of());
-                }
-            }
-        } catch (Exception e) {
-            log.warn("Error copying runtime resources", e);
-            throw Exceptions.propagate(e);
+            getLocation().releaseMutex("installing " + elvis(entity, this));
         }
     }
 
@@ -443,62 +333,7 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
         return Strings.toStringMap(entity.getConfig(SoftwareProcess.SHELL_ENVIRONMENT));
     }
 
-    /**
-     * @param template File to template and copy.
-     * @param target Destination on server.
-     * @return The exit code the SSH command run.
-     */
-    public int copyTemplate(File template, String target) {
-        return copyTemplate(template.toURI().toASCIIString(), target);
-    }
-
-    /**
-     * @param template URI of file to template and copy, e.g. file://.., http://.., classpath://..
-     * @param target Destination on server.
-     * @return The exit code of the SSH command run.
-     */
-    public int copyTemplate(String template, String target) {
-        return copyTemplate(template, target, false, ImmutableMap.<String, String>of());
-    }
-
-    /**
-     * @param template URI of file to template and copy, e.g. file://.., http://.., classpath://..
-     * @param target Destination on server.
-     * @param extraSubstitutions Extra substitutions for the templater to use, for example
-     *               "foo" -> "bar", and in a template ${foo}.
-     * @return The exit code of the SSH command run.
-     */
-    public int copyTemplate(String template, String target, boolean createParent, Map<String, ?> extraSubstitutions) {
-        String data = processTemplate(template, extraSubstitutions);
-        return copyResource(MutableMap.<Object,Object>of(), new StringReader(data), target, createParent);
-    }
-
-    /**
-     * @param file File to copy.
-     * @param target Destination on server.
-     * @return The exit code the SSH command run.
-     */
-    public int copyResource(File file, String target) {
-        return copyResource(file.toURI().toASCIIString(), target);
-    }
-
-    /**
-     * @param resource URI of file to copy, e.g. file://.., http://.., classpath://..
-     * @param target Destination on server.
-     * @return The exit code of the SSH command run
-     */
-    public int copyResource(String resource, String target) {
-        return copyResource(MutableMap.of(), resource, target);
-    }
 
-    public int copyResource(String resource, String target, boolean createParentDir) {
-        return copyResource(MutableMap.of(), resource, target, createParentDir);
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public int copyResource(Map sshFlags, String source, String target) {
-        return copyResource(sshFlags, source, target, false);
-    }
     
     /**
      * @param sshFlags Extra flags to be used when making an SSH connection to the entity's machine.
@@ -539,26 +374,6 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
         return result;
     }
 
-    /**
-     * @see #copyResource(Map, InputStream, String)
-     */
-    public int copyResource(Reader source, String target) {
-        return copyResource(MutableMap.of(), source, target, false);
-    }
-
-    /**
-     * @see #copyResource(Map, InputStream, String)
-     */
-    public int copyResource(Map<Object,Object> sshFlags, Reader source, String target, boolean createParent) {
-        return copyResource(sshFlags, new ReaderInputStream(source), target, createParent);
-    }
-
-    /**
-     * @see #copyResource(Map, InputStream, String)
-     */
-    public int copyResource(InputStream source, String target) {
-        return copyResource(MutableMap.of(), source, target, false);
-    }
     
     /**
      * Input stream will be closed automatically.
@@ -837,6 +652,12 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
         return s;
     }
 
+    @Override
+    protected void createDirectory(String directoryName, String summaryForLogging) {
+        DynamicTasks.queue(SshEffectorTasks.ssh("mkdir -p " + directoryName).summary(summaryForLogging)
+                .requiringExitCodeZero()).get();
+    }
+
     public Set<Integer> getPortsUsed() {
         Set<Integer> result = Sets.newLinkedHashSet();
         result.add(22);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessWinRmDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessWinRmDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessWinRmDriver.java
index b53685b..ed92e15 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessWinRmDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractSoftwareProcessWinRmDriver.java
@@ -21,33 +21,115 @@ package brooklyn.entity.basic;
 import java.io.File;
 import java.io.InputStream;
 import java.util.List;
+import java.util.Map;
 
+import brooklyn.config.ConfigKey;
 import brooklyn.location.basic.WinRmMachineLocation;
 
+import com.google.api.client.util.Strings;
+import com.google.common.collect.ImmutableList;
+
 public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwareProcessDriver {
 
     public AbstractSoftwareProcessWinRmDriver(EntityLocal entity, WinRmMachineLocation location) {
         super(entity, location);
     }
 
-    public WinRmMachineLocation getMachine() {
-        return (WinRmMachineLocation) getLocation();
+    @Override
+    public void runPreInstallCommand(String command) {
+        execute(ImmutableList.of(command));
+    }
+
+    @Override
+    public void setup() {
+        // Default to no-op
+    }
+
+    @Override
+    public void runPostInstallCommand(String command) {
+        execute(ImmutableList.of(command));
+    }
+
+    @Override
+    public void runPreLaunchCommand(String command) {
+        execute(ImmutableList.of(command));
+    }
+
+    @Override
+    public void runPostLaunchCommand(String command) {
+        execute(ImmutableList.of(command));
+    }
+
+    @Override
+    public WinRmMachineLocation getLocation() {
+        return (WinRmMachineLocation)super.getLocation();
+    }
+
+    @Override
+    public String getRunDir() {
+        // TODO: This needs to be tidied, and read from the appropriate flags (if set)
+        return "$HOME\\brooklyn-managed-processes\\apps\\" + entity.getApplicationId() + "\\entities\\" + getEntityVersionLabel()+"_"+entity.getId();
+    }
+
+    @Override
+    public String getInstallDir() {
+        // TODO: This needs to be tidied, and read from the appropriate flags (if set)
+        return "$HOME\\brooklyn-managed-processes\\installs\\" + entity.getApplicationId() + "\\" + getEntityVersionLabel()+"_"+entity.getId();
+    }
+
+    @Override
+    public int copyResource(Map<Object, Object> sshFlags, String source, String target, boolean createParentDir) {
+        if (createParentDir) {
+            createDirectory(getDirectory(target), "Creating resource directory");
+        }
+        return copyTo(new File(source), new File(target));
+    }
+
+    @Override
+    public int copyResource(Map<Object, Object> sshFlags, InputStream source, String target, boolean createParentDir) {
+        if (createParentDir) {
+            createDirectory(getDirectory(target), "Creating resource directory");
+        }
+        return copyTo(source, new File(target));
+    }
+
+    @Override
+    protected void createDirectory(String directoryName, String summaryForLogging) {
+        getLocation().executePsScript("New-Item -path \"" + directoryName + "\" -type directory -ErrorAction SilentlyContinue");
+    }
+
+    protected boolean executeCommand(ConfigKey<String> regularCommandKey, ConfigKey<String> powershellCommandKey) {
+        String regularCommand = getEntity().getConfig(regularCommandKey);
+        String powershellCommand = getEntity().getConfig(powershellCommandKey);
+        if ((Strings.isNullOrEmpty(regularCommand) && Strings.isNullOrEmpty(powershellCommand)) || (
+                !Strings.isNullOrEmpty(regularCommand) && !Strings.isNullOrEmpty(powershellCommand))) {
+            throw new IllegalStateException(String.format("Exactly one of %s or %s must be set", regularCommandKey.getName(), powershellCommandKey.getName()));
+        }
+        if (Strings.isNullOrEmpty(regularCommand)) {
+            return getLocation().executePsScript(ImmutableList.of(powershellCommand)) == 0;
+        } else {
+            return getLocation().executeScript(ImmutableList.of(regularCommand)) == 0;
+        }
     }
 
     public int execute(List<String> script) {
-        return getMachine().executeScript(script);
+        return getLocation().executeScript(script);
     }
 
     public int executePowerShell(List<String> psScript) {
-        return getMachine().executePsScript(psScript);
+        return getLocation().executePsScript(psScript);
     }
 
     public int copyTo(File source, File destination) {
-        return getMachine().copyTo(source, destination);
+        return getLocation().copyTo(source, destination);
     }
 
     public int copyTo(InputStream source, File destination) {
-        return getMachine().copyTo(source, destination);
+        return getLocation().copyTo(source, destination);
+    }
+
+    private String getDirectory(String fileName) {
+        return fileName.substring(0, fileName.lastIndexOf("\\"));
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcess.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcess.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcess.java
new file mode 100644
index 0000000..18fda37
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcess.java
@@ -0,0 +1,32 @@
+/*
+ * 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 brooklyn.entity.basic;
+
+import brooklyn.config.ConfigKey;
+import brooklyn.event.basic.AttributeSensorAndConfigKey;
+
+public interface AbstractVanillaSoftwareProcess extends SoftwareProcess {
+    AttributeSensorAndConfigKey<String, String> DOWNLOAD_URL = SoftwareProcess.DOWNLOAD_URL;
+
+    ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "0.0.0");
+
+    ConfigKey<String> LAUNCH_COMMAND = ConfigKeys.newStringConfigKey("launch.command", "command to run to launch the process", "./start.sh");
+    ConfigKey<String> CHECK_RUNNING_COMMAND = ConfigKeys.newStringConfigKey("checkRunning.command", "command to determine whether the process is running");
+    ConfigKey<String> STOP_COMMAND = ConfigKeys.newStringConfigKey("stop.command", "command to run to stop the process");
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcessDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcessDriver.java b/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcessDriver.java
new file mode 100644
index 0000000..ca80f63
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/basic/AbstractVanillaSoftwareProcessDriver.java
@@ -0,0 +1,22 @@
+/*
+ * 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 brooklyn.entity.basic;
+
+public interface AbstractVanillaSoftwareProcessDriver extends SoftwareProcessDriver {
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcess.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcess.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcess.java
index f6d6ec1..a1cca3f 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcess.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcess.java
@@ -19,9 +19,7 @@
 package brooklyn.entity.basic;
 
 import brooklyn.catalog.Catalog;
-import brooklyn.config.ConfigKey;
 import brooklyn.entity.proxying.ImplementedBy;
-import brooklyn.event.basic.AttributeSensorAndConfigKey;
 
 /** 
  * A {@link SoftwareProcess} entity that runs commands from an archive.
@@ -54,14 +52,6 @@ import brooklyn.event.basic.AttributeSensorAndConfigKey;
  */
 @Catalog(name="Vanilla Software Process", description="A software process configured with scripts, e.g. for launch, check-running and stop")
 @ImplementedBy(VanillaSoftwareProcessImpl.class)
-public interface VanillaSoftwareProcess extends SoftwareProcess {
-
-    AttributeSensorAndConfigKey<String, String> DOWNLOAD_URL = SoftwareProcess.DOWNLOAD_URL;
-
-    ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "0.0.0");
-
-    ConfigKey<String> LAUNCH_COMMAND = ConfigKeys.newStringConfigKey("launch.command", "command to run to launch the process", "./start.sh");
-    ConfigKey<String> CHECK_RUNNING_COMMAND = ConfigKeys.newStringConfigKey("checkRunning.command", "command to determine whether the process is running");
-    ConfigKey<String> STOP_COMMAND = ConfigKeys.newStringConfigKey("stop.command", "command to run to stop the process");
+public interface VanillaSoftwareProcess extends AbstractVanillaSoftwareProcess {
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessDriver.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessDriver.java
index f03796b..31ecdde 100644
--- a/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessDriver.java
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaSoftwareProcessDriver.java
@@ -18,5 +18,6 @@
  */
 package brooklyn.entity.basic;
 
-public interface VanillaSoftwareProcessDriver extends SoftwareProcessDriver {
+public interface VanillaSoftwareProcessDriver extends AbstractVanillaSoftwareProcessDriver {
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcess.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcess.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcess.java
new file mode 100644
index 0000000..133ea45
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcess.java
@@ -0,0 +1,27 @@
+/*
+ * 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 brooklyn.entity.basic;
+
+import brooklyn.config.ConfigKey;
+
+public interface VanillaWindowsProcess extends AbstractVanillaSoftwareProcess {
+    ConfigKey<String> LAUNCH_POWERSHELL_COMMAND = ConfigKeys.newStringConfigKey("launch.powershell.command", "command to run to launch the process", "./start.sh");
+    ConfigKey<String> CHECK_RUNNING_POWERSHELL_COMMAND = ConfigKeys.newStringConfigKey("checkRunning.powershell.command", "command to determine whether the process is running");
+    ConfigKey<String> STOP_POWERSHELL_COMMAND = ConfigKeys.newStringConfigKey("stop.powershell.command", "command to run to stop the process");
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessDriver.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessDriver.java
new file mode 100644
index 0000000..228c443
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessDriver.java
@@ -0,0 +1,23 @@
+/*
+ * 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 brooklyn.entity.basic;
+
+public interface VanillaWindowsProcessDriver extends AbstractVanillaSoftwareProcessDriver {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessImpl.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessImpl.java
new file mode 100644
index 0000000..f09bb87
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessImpl.java
@@ -0,0 +1,38 @@
+/*
+ * 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 brooklyn.entity.basic;
+
+public class VanillaWindowsProcessImpl extends SoftwareProcessImpl implements VanillaWindowsProcess {
+    @Override
+    public Class getDriverInterface() {
+        return VanillaWindowsProcessDriver.class;
+    }
+
+    @Override
+    protected void connectSensors() {
+        super.connectSensors();
+        connectServiceUpIsRunning();
+    }
+
+    @Override
+    protected void disconnectSensors() {
+        disconnectServiceUpIsRunning();
+        super.disconnectSensors();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/2b07bed4/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java
new file mode 100644
index 0000000..c966685
--- /dev/null
+++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java
@@ -0,0 +1,54 @@
+/*
+ * 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 brooklyn.entity.basic;
+
+import brooklyn.location.basic.WinRmMachineLocation;
+
+public class VanillaWindowsProcessWinRmDriver extends AbstractSoftwareProcessWinRmDriver implements VanillaWindowsProcessDriver {
+
+    public VanillaWindowsProcessWinRmDriver(EntityLocal entity, WinRmMachineLocation location) {
+        super(entity, location);
+    }
+
+    @Override
+    public void install() {
+
+    }
+
+    @Override
+    public void customize() {
+
+    }
+
+    @Override
+    public void launch() {
+
+    }
+
+    @Override
+    public boolean isRunning() {
+        return executeCommand(VanillaWindowsProcess.CHECK_RUNNING_COMMAND, VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND);
+    }
+
+    @Override
+    public void stop() {
+
+    }
+
+}