http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7582ebeb/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/customize/UserMetadataStringOption.java
----------------------------------------------------------------------
diff --git 
a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/customize/UserMetadataStringOption.java
 
b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/customize/UserMetadataStringOption.java
new file mode 100644
index 0000000..b01fdcb
--- /dev/null
+++ 
b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/templates/customize/UserMetadataStringOption.java
@@ -0,0 +1,80 @@
+/*
+ * 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.location.jclouds.templates.customize;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.Strings;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.ec2.compute.options.EC2TemplateOptions;
+import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class UserMetadataStringOption implements TemplateOptionCustomizer {
+    private static final Logger LOG = 
LoggerFactory.getLogger(UserMetadataStringOption.class);
+
+    public void apply(TemplateOptions t, ConfigBag props, Object v) {
+        if (t instanceof EC2TemplateOptions) {
+            // See AWS docs: 
http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/UsingConfig_WinAMI.html#user-data-execution
+            if (v == null) return;
+            String data = v.toString();
+            if (!(data.startsWith("<script>") || 
data.startsWith("<powershell>"))) {
+                data = "<script> " + data + " </script>";
+            }
+            ((EC2TemplateOptions) t).userData(data.getBytes());
+        } else if (t instanceof SoftLayerTemplateOptions) {
+            ((SoftLayerTemplateOptions) t).userData(Strings.toString(v));
+        } else {
+            // Try reflection: userData(String), or 
guestCustomizationScript(String);
+            // the latter is used by vCloud Director.
+            Class<? extends TemplateOptions> clazz = t.getClass();
+            Method userDataMethod = null;
+            try {
+                userDataMethod = clazz.getMethod("userData", String.class);
+            } catch (SecurityException e) {
+                LOG.info("Problem reflectively inspecting methods of " + 
t.getClass() + " for setting userData", e);
+            } catch (NoSuchMethodException e) {
+                try {
+                    // For vCloud Director
+                    userDataMethod = 
clazz.getMethod("guestCustomizationScript", String.class);
+                } catch (NoSuchMethodException e2) {
+                    // expected on various other clouds
+                }
+            }
+            if (userDataMethod != null) {
+                try {
+                    userDataMethod.invoke(t, Strings.toString(v));
+                } catch (InvocationTargetException e) {
+                    LOG.info("Problem invoking " + userDataMethod.getName() + 
" of " + t.getClass() + ", for setting userData (rethrowing)", e);
+                    throw Exceptions.propagate(e);
+                } catch (IllegalAccessException e) {
+                    LOG.debug("Unable to reflectively invoke " + 
userDataMethod.getName() + " of " + t.getClass() + ", for setting userData 
(rethrowing)", e);
+                    throw Exceptions.propagate(e);
+                }
+            } else {
+                LOG.info("ignoring userDataString({}) in VM creation because 
not supported for cloud/type ({})", v, t.getClass());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7582ebeb/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
----------------------------------------------------------------------
diff --git 
a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
 
b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
index b47595f..475fc78 100644
--- 
a/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
+++ 
b/locations/jclouds/src/test/java/org/apache/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 import org.apache.brooklyn.config.ConfigKey;
+import 
org.apache.brooklyn.location.jclouds.templates.customize.TemplateOptionCustomizer;
 import org.apache.brooklyn.util.core.config.ConfigBag;
 import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions;
 import org.jclouds.compute.options.TemplateOptions;
@@ -98,7 +99,7 @@ public class 
JcloudsLocationTemplateOptionsCustomisersLiveTest extends AbstractJ
         checkState(locationConfig.containsKey(keyToTest),
                 "location config does not contain the key " + 
keyToTest.getName());
 
-        JcloudsLocation.CustomizeTemplateOptions code = 
JcloudsLocation.SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.get(keyToTest);
+        TemplateOptionCustomizer code = 
JcloudsLocation.SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.get(keyToTest);
         code.apply(templateOptions, locationConfig, 
locationConfig.get(keyToTest));
     }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7582ebeb/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
----------------------------------------------------------------------
diff --git 
a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
 
b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
index 9a4d3b9..dcd1dde 100644
--- 
a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
+++ 
b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/MachineLifecycleEffectorTasks.java
@@ -491,6 +491,8 @@ public abstract class MachineLifecycleEffectorTasks {
                 SshMachineLocation sshMachine = (SshMachineLocation) machine;
                 UserAndHostAndPort sshAddress = UserAndHostAndPort.fromParts(
                         sshMachine.getUser(), 
sshMachine.getAddress().getHostName(), sshMachine.getPort());
+                // FIXME: Who or what is SSH_ADDRESS intended for? It's not 
necessarily the address that
+                // the SshMachineLocation is using for ssh connections 
(because it accepts SSH_HOST as an override).
                 entity().sensors().set(Attributes.SSH_ADDRESS, sshAddress);
             }
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/7582ebeb/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java
index 22a4f7c..db24b58 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/text/Strings.java
@@ -28,7 +28,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
-
 import javax.annotation.Nullable;
 
 import org.apache.brooklyn.util.collections.MutableList;
@@ -44,7 +43,9 @@ import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 
 public class Strings {
@@ -827,6 +828,33 @@ public class Strings {
         return result;
     }
 
+    /**
+     * Tries to convert v to a list of strings.
+     * <p>
+     * If v is <code>null</code> then the method returns an empty immutable 
list.
+     * If v is {@link Iterable} or an <code>Object[]</code> then toString is 
called on its elements.
+     * If v is a {@link String} then a singleton list containing v is returned.
+     * Otherwise the method throws an {@link IllegalArgumentException}.
+     */
+    public static List<String> toStringList(Object v) {
+        if (v == null) return ImmutableList.of();
+        List<String> result = Lists.newArrayList();
+        if (v instanceof Iterable) {
+            for (Object o : (Iterable<?>) v) {
+                result.add(o.toString());
+            }
+        } else if (v instanceof Object[]) {
+            for (int i = 0; i < ((Object[]) v).length; i++) {
+                result.add(((Object[]) v)[i].toString());
+            }
+        } else if (v instanceof String) {
+            result.add((String) v);
+        } else {
+            throw new IllegalArgumentException("Invalid type for List<String>: 
" + v + " of type " + v.getClass());
+        }
+        return result;
+    }
+
     /** returns base repeated count times */
     public static String repeat(String base, int count) {
         if (base==null) return null;

Reply via email to