You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by tb...@apache.org on 2018/04/27 15:43:46 UTC

[1/3] brooklyn-server git commit: SshCommandSensor: no anonymous inner classes

Repository: brooklyn-server
Updated Branches:
  refs/heads/master cd40893d1 -> 7e2b497cb


SshCommandSensor: no anonymous inner classes

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

Branch: refs/heads/master
Commit: d490115023b6c53d3a79b72f8ec190f14e5eb949
Parents: 3afc8fe
Author: Aled Sage <al...@gmail.com>
Authored: Thu Apr 26 20:57:33 2018 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Fri Apr 27 10:09:31 2018 +0100

----------------------------------------------------------------------
 .../SshCommandSensorYamlRebindTest.java         |  89 +++++++++++++
 .../core/sensor/ssh/SshCommandSensor.java       | 124 +++++++++++++++----
 .../brooklyn/util/core/flags/TypeCoercions.java |  15 ++-
 .../core/mgmt/rebind/RebindTestFixture.java     |  33 ++++-
 .../apache/brooklyn/util/guava/Functionals.java |  21 +++-
 .../brooklyn/util/text/StringFunctions.java     |  12 ++
 6 files changed, 263 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d4901150/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlRebindTest.java
----------------------------------------------------------------------
diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlRebindTest.java
new file mode 100644
index 0000000..0c0730e
--- /dev/null
+++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/SshCommandSensorYamlRebindTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.camp.brooklyn;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.entity.StartableApplication;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess;
+import org.apache.brooklyn.feed.ssh.SshFeed;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecCmd;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+@Test
+public class SshCommandSensorYamlRebindTest extends AbstractYamlRebindTest {
+
+   @Test
+   public void testSshCommandSensorWithEffectorInEnv() throws Exception {
+       RecordingSshTool.setCustomResponse(".*myCommand.*", new RecordingSshTool.CustomResponse(0, "myResponse", null));
+       
+       createStartWaitAndLogApplication(
+           "location:",
+           "  localhost:",
+           "    sshToolClass: "+RecordingSshTool.class.getName(),
+           "services:",
+           "- type: " + VanillaSoftwareProcess.class.getName(),
+           "  brooklyn.config:",
+           "    onbox.base.dir.skipResolution: true",
+           "  brooklyn.initializers:",
+           "  - type: org.apache.brooklyn.core.sensor.ssh.SshCommandSensor",
+           "    brooklyn.config:",
+           "      name: mySensor",
+           "      command: myCommand",
+           "      executionDir: '/path/to/myexecutiondir'",
+           "      shell.env:",
+           "        MY_ENV: myEnvVal",
+           "      period: 10ms",
+           "      onlyIfServiceUp: false");
+
+       StartableApplication newApp = rebind();
+       VanillaSoftwareProcess newEntity = (VanillaSoftwareProcess) Iterables.getOnlyElement(newApp.getChildren());
+       SshFeed newFeed = (SshFeed) Iterables.find(((EntityInternal)newEntity).feeds().getFeeds(), Predicates.instanceOf(SshFeed.class));
+       
+       // Clear history of commands, and the sensor, so can confirm it gets re-set by the ssh feed
+       RecordingSshTool.clearCmdHistory();
+       newEntity.sensors().set(Sensors.newStringSensor("mySensor"), null);
+       
+       // Assert sensor is set, and command is executed as expected
+       EntityAsserts.assertAttributeEqualsEventually(newEntity, Sensors.newStringSensor("mySensor"), "myResponse");
+       ExecCmd cmd = Asserts.succeedsEventually(() -> RecordingSshTool.getLastExecCmd());
+       
+       assertTrue(cmd.commands.toString().contains("myCommand"), "cmds="+cmd.commands);
+       assertEquals(cmd.env.get("MY_ENV"), "myEnvVal", "env="+cmd.env);
+       assertTrue(cmd.commands.toString().contains("/path/to/myexecutiondir"), "cmds="+cmd.commands);
+       
+       // Confirm feed's memento is 'clean' - no anonymous inner classes
+       BrooklynMementoRawData rawMemento = loadMementoRawData();
+       String rawFeedMemento = rawMemento.getFeeds().get(newFeed.getId());
+       assertFalse(rawFeedMemento.contains("$1"), rawFeedMemento);
+       assertFalse(rawFeedMemento.contains("$2"), rawFeedMemento);
+       assertFalse(rawFeedMemento.contains("$3"), rawFeedMemento);
+   }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d4901150/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
index b03b54f..b77dde3 100644
--- a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
+++ b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
@@ -41,7 +41,9 @@ import org.apache.brooklyn.util.core.flags.TypeCoercions;
 import org.apache.brooklyn.util.core.json.ShellEnvironmentSerializer;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Functionals;
 import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.StringFunctions;
 import org.apache.brooklyn.util.text.Strings;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
@@ -53,6 +55,7 @@ import com.google.common.base.Functions;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.reflect.TypeToken;
 
 /** 
  * Configurable {@link EntityInitializer} which adds an SSH sensor feed running the <code>command</code> supplied
@@ -99,7 +102,34 @@ public final class SshCommandSensor<T> extends AbstractAddSensorFeed<T> {
         final Duration logWarningGraceTimeOnStartup = EntityInitializers.resolve(params, LOG_WARNING_GRACE_TIME_ON_STARTUP);
         final Duration logWarningGraceTime = EntityInitializers.resolve(params, LOG_WARNING_GRACE_TIME);
 
-        Supplier<Map<String,String>> envSupplier = new Supplier<Map<String,String>>() {
+        Supplier<Map<String,String>> envSupplier = new EnvSupplier(entity, params);
+        
+        Supplier<String> commandSupplier = new CommandSupplier(entity, params);
+
+        CommandPollConfig<T> pollConfig = new CommandPollConfig<T>(sensor)
+                .period(period)
+                .env(envSupplier)
+                .command(commandSupplier)
+                .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates))
+                .checkSuccess(SshValueFunctions.exitStatusEquals(0))
+                .onFailureOrException(Functions.constant((T)params.get(VALUE_ON_ERROR)))
+                .onSuccess(Functionals.chain(
+                        SshValueFunctions.stdout(),
+                        StringFunctions.trimEnd(),
+                        TypeCoercions.function((Class<T>) sensor.getType())))
+                .logWarningGraceTimeOnStartup(logWarningGraceTimeOnStartup)
+                .logWarningGraceTime(logWarningGraceTime);
+
+        SshFeed feed = SshFeed.builder()
+                .entity(entity)
+                .onlyIfServiceUp()
+                .poll(pollConfig)
+                .build();
+
+        entity.addFeed(feed);
+        
+        // Deprecated; kept for backwards compatibility with historic persisted state
+        new Supplier<Map<String,String>>() {
             @Override
             public Map<String, String> get() {
                 if (entity == null) return ImmutableMap.of(); // See BROOKLYN-568
@@ -123,7 +153,8 @@ public final class SshCommandSensor<T> extends AbstractAddSensorFeed<T> {
             }
         };
 
-        Supplier<String> commandSupplier = new Supplier<String>() {
+        // Deprecated; kept for backwards compatibility with historic persisted state
+        new Supplier<String>() {
             @Override
             public String get() {
                 // Note that entity may be null during rebind (e.g. if this SshFeed is orphaned, with no associated entity):
@@ -135,28 +166,12 @@ public final class SshCommandSensor<T> extends AbstractAddSensorFeed<T> {
             }
         };
 
-        CommandPollConfig<T> pollConfig = new CommandPollConfig<T>(sensor)
-                .period(period)
-                .env(envSupplier)
-                .command(commandSupplier)
-                .suppressDuplicates(Boolean.TRUE.equals(suppressDuplicates))
-                .checkSuccess(SshValueFunctions.exitStatusEquals(0))
-                .onFailureOrException(Functions.constant((T)params.get(VALUE_ON_ERROR)))
-                .onSuccess(Functions.compose(new Function<String, T>() {
-                        @Override
-                        public T apply(String input) {
-                            return TypeCoercions.coerce(Strings.trimEnd(input), (Class<T>) sensor.getType());
-                        }}, SshValueFunctions.stdout()))
-                .logWarningGraceTimeOnStartup(logWarningGraceTimeOnStartup)
-                .logWarningGraceTime(logWarningGraceTime);
-
-        SshFeed feed = SshFeed.builder()
-                .entity(entity)
-                .onlyIfServiceUp()
-                .poll(pollConfig)
-                .build();
-
-        entity.addFeed(feed);
+        // Deprecated; kept for backwards compatibility with historic persisted state
+        new Function<String, T>() {
+            @Override public T apply(String input) {
+                return TypeCoercions.coerce(Strings.trimEnd(input), (Class<T>) sensor.getType());
+            }
+        };
     }
 
     @Beta
@@ -183,4 +198,65 @@ public final class SshCommandSensor<T> extends AbstractAddSensorFeed<T> {
         return finalCommand;
     }
 
+    private static class EnvSupplier implements Supplier<Map<String,String>> {
+        private final Entity entity;
+        private final Object rawSensorShellEnv;
+        
+        EnvSupplier(Entity entity, ConfigBag params) {
+            this.entity = entity;
+            this.rawSensorShellEnv = params.getAllConfigRaw().getOrDefault(SENSOR_SHELL_ENVIRONMENT.getName(), SENSOR_SHELL_ENVIRONMENT.getDefaultValue());
+        }
+        
+        @Override
+        public Map<String, String> get() {
+            if (entity == null) return ImmutableMap.of(); // See BROOKLYN-568
+            
+            Map<String, Object> env = MutableMap.copyOf(entity.getConfig(BrooklynConfigKeys.SHELL_ENVIRONMENT));
+
+            // Add the shell environment entries from our configuration
+            if (rawSensorShellEnv != null) {
+                env.putAll(TypeCoercions.coerce(rawSensorShellEnv, new TypeToken<Map<String,Object>>() {}));
+            }
+
+            // Try to resolve the configuration in the env Map
+            try {
+                env = (Map<String, Object>) Tasks.resolveDeepValue(env, Object.class, ((EntityInternal) entity).getExecutionContext());
+            } catch (InterruptedException | ExecutionException e) {
+                Exceptions.propagateIfFatal(e);
+            }
+
+            // Convert the environment into strings with the serializer
+            ShellEnvironmentSerializer serializer = new ShellEnvironmentSerializer(((EntityInternal) entity).getManagementContext());
+            return serializer.serialize(env);
+        }
+    }
+
+    private static class CommandSupplier implements Supplier<String> {
+        private final Entity entity;
+        private final Object rawSensorCommand;
+        private final Object rawSensorExecDir;
+        
+        CommandSupplier(Entity entity, ConfigBag params) {
+            this.entity = entity;
+            this.rawSensorCommand = params.getAllConfigRaw().get(SENSOR_COMMAND.getName());
+            this.rawSensorExecDir = params.getAllConfigRaw().get(SENSOR_EXECUTION_DIR.getName());
+        }
+        
+        @Override
+        public String get() {
+            // Note that entity may be null during rebind (e.g. if this SshFeed is orphaned, with no associated entity):
+            // See https://issues.apache.org/jira/browse/BROOKLYN-568.
+            // We therefore guard against null in makeCommandExecutingInDirectory.
+            ConfigBag params = ConfigBag.newInstance();
+            if (rawSensorCommand != null) {
+                params.putStringKey(SENSOR_COMMAND.getName(), rawSensorCommand);
+            }
+            if (rawSensorExecDir != null) {
+                params.putStringKey(SENSOR_EXECUTION_DIR.getName(), rawSensorExecDir);
+            }
+            String command = Preconditions.checkNotNull(EntityInitializers.resolve(params, SENSOR_COMMAND));
+            String dir = EntityInitializers.resolve(params, SENSOR_EXECUTION_DIR);
+            return makeCommandExecutingInDirectory(command, dir, entity);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d4901150/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index a2dc398..3b3f874 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -28,7 +28,6 @@ import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.core.internal.BrooklynInitialization;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
-import org.apache.brooklyn.core.mgmt.usage.UsageListener;
 import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.util.JavaGroovyEquivalents;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
@@ -93,7 +92,19 @@ public class TypeCoercions {
     }
     
     public static <T> Function<Object, T> function(final Class<T> type) {
-        return coercer.function(type);
+        return new CoerceFunction<T>(type);
+    }
+
+    private static class CoerceFunction<T> implements Function<Object, T> { 
+        private final Class<T> type;
+
+        public CoerceFunction(Class<T> type) {
+            this.type = type;
+        }
+        @Override
+        public T apply(Object input) {
+            return coerce(input, type);
+        }
     }
 
     public static void registerDeprecatedBrooklynAdapters() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d4901150/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java
index b80d857..b09aba6 100644
--- a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java
@@ -19,9 +19,11 @@
 package org.apache.brooklyn.core.mgmt.rebind;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
+import java.util.function.Function;
 
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
@@ -30,6 +32,7 @@ import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode;
 import org.apache.brooklyn.api.mgmt.rebind.RebindExceptionHandler;
 import org.apache.brooklyn.api.mgmt.rebind.RebindManager;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoManifest;
+import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.StartableApplication;
@@ -41,6 +44,7 @@ import org.apache.brooklyn.core.mgmt.persist.FileBasedObjectStore;
 import org.apache.brooklyn.core.mgmt.persist.PersistMode;
 import org.apache.brooklyn.core.server.BrooklynServerConfig;
 import org.apache.brooklyn.util.core.task.BasicExecutionManager;
+import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.os.Os;
 import org.apache.brooklyn.util.osgi.OsgiTestResources;
 import org.apache.brooklyn.util.repeat.Repeater;
@@ -299,6 +303,24 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
     }
     
     protected BrooklynMementoManifest loadMementoManifest() throws Exception {
+        return loadFromPersistedState((persister) -> {
+            RebindExceptionHandler exceptionHandler = new RecordingRebindExceptionHandler(RebindManager.RebindFailureMode.FAIL_AT_END, RebindManager.RebindFailureMode.FAIL_AT_END);
+            try {
+                return persister.loadMementoManifest(null, exceptionHandler);
+            } catch (IOException e) {
+                throw Exceptions.propagate(e);
+            }
+        });
+    }
+    
+    protected BrooklynMementoRawData loadMementoRawData() throws Exception {
+        return loadFromPersistedState((persister) -> {
+            RebindExceptionHandler exceptionHandler = new RecordingRebindExceptionHandler(RebindManager.RebindFailureMode.FAIL_AT_END, RebindManager.RebindFailureMode.FAIL_AT_END);
+            return persister.loadMementoRawData(exceptionHandler);
+        });
+    }
+    
+    protected <U> U loadFromPersistedState(Function<BrooklynMementoPersisterToObjectStore, U> loader) throws Exception {
         newManagementContext = createNewManagementContext();
         FileBasedObjectStore objectStore = new FileBasedObjectStore(mementoDir);
         objectStore.injectManagementContext(newManagementContext);
@@ -307,10 +329,13 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
                 objectStore,
                 newManagementContext,
                 classLoader);
-        RebindExceptionHandler exceptionHandler = new RecordingRebindExceptionHandler(RebindManager.RebindFailureMode.FAIL_AT_END, RebindManager.RebindFailureMode.FAIL_AT_END);
-        BrooklynMementoManifest mementoManifest = persister.loadMementoManifest(null, exceptionHandler);
-        persister.stop(false);
-        return mementoManifest;
+        U result;
+        try {
+            result = loader.apply(persister);
+        } finally {
+            persister.stop(false);
+        }
+        return result;
     }
     
 //    protected void assertCatalogContains(BrooklynCatalog catalog, CatalogItem<?, ?> item) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d4901150/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java b/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
index 18238d2..ef2400b 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/guava/Functionals.java
@@ -135,6 +135,9 @@ public class Functionals {
     }
 
     public static <T> Predicate<T> predicate(final Function<T,Boolean> f) {
+        // Deprecated use of anonymous class (even though it's got a name, it's in a method).
+        // Kept for rebinding to historic persisted state only.
+        @SuppressWarnings({ "unused", "hiding" })
         class FunctionAsPredicate implements Predicate<T> {
             @Override
             public boolean apply(T input) {
@@ -145,7 +148,23 @@ public class Functionals {
                 return "predicate("+f+")";
             }
         }
-        return new FunctionAsPredicate();
+        return new Functionals.FunctionAsPredicate<T>(f);
+    }
+
+    static class FunctionAsPredicate<T> implements Predicate<T> {
+        private final Function<T, Boolean> f;
+        
+        FunctionAsPredicate(final Function<T,Boolean> f) {
+            this.f = f;
+        }
+        @Override
+        public boolean apply(T input) {
+            return f.apply(input);
+        }
+        @Override
+        public String toString() {
+            return "predicate("+f+")";
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/d4901150/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
index ddf1914..13f32fc 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringFunctions.java
@@ -366,6 +366,18 @@ public class StringFunctions {
         }
     }
 
+    public static Function<String, String> trimEnd() {
+        return new TrimEndFunction();
+    }
+    
+    protected static class TrimEndFunction implements Function<String, String> {
+        @Override
+        public String apply(@Nullable String input) {
+            if (input == null) return null;
+            return Strings.trimEnd(input);
+        }
+    }
+
     public static Function<String, String> toLowerCase() {
         return new LowerCaseFunction();
     }


[3/3] brooklyn-server git commit: This closes #959

Posted by tb...@apache.org.
This closes #959


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/7e2b497c
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/7e2b497c
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/7e2b497c

Branch: refs/heads/master
Commit: 7e2b497cbf82ca143fd2276f44aca256fc92ee3b
Parents: cd40893 d490115
Author: Thomas Bouron <th...@cloudsoftcorp.com>
Authored: Fri Apr 27 16:43:31 2018 +0100
Committer: Thomas Bouron <th...@cloudsoftcorp.com>
Committed: Fri Apr 27 16:43:31 2018 +0100

----------------------------------------------------------------------
 .../SshCommandSensorYamlRebindTest.java         |  89 +++++++++++
 .../core/sensor/ssh/SshCommandSensor.java       | 134 +++++++++++++----
 .../brooklyn/util/core/flags/TypeCoercions.java |  15 +-
 .../RebindHistoricSshCommandSensorTest.java     |  96 ++++++++++++
 .../core/mgmt/rebind/RebindTestFixture.java     |  33 +++-
 .../rebind/ssh-command-sensor-entity-dnlz7hpbdg | 150 +++++++++++++++++++
 .../rebind/ssh-command-sensor-feed-a9ekg3cnu0   | 143 ++++++++++++++++++
 .../apache/brooklyn/util/guava/Functionals.java |  21 ++-
 .../brooklyn/util/text/StringFunctions.java     |  12 ++
 9 files changed, 661 insertions(+), 32 deletions(-)
----------------------------------------------------------------------



[2/3] brooklyn-server git commit: Fix SshCommandSensor rebind

Posted by tb...@apache.org.
Fix SshCommandSensor rebind


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/3afc8fe3
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/3afc8fe3
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/3afc8fe3

Branch: refs/heads/master
Commit: 3afc8fe386dc1f6f4149143a7b205c96185a770e
Parents: cd40893
Author: Aled Sage <al...@gmail.com>
Authored: Thu Apr 26 19:36:30 2018 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Fri Apr 27 10:09:31 2018 +0100

----------------------------------------------------------------------
 .../core/sensor/ssh/SshCommandSensor.java       |  10 +-
 .../RebindHistoricSshCommandSensorTest.java     |  96 ++++++++++++
 .../rebind/ssh-command-sensor-entity-dnlz7hpbdg | 150 +++++++++++++++++++
 .../rebind/ssh-command-sensor-feed-a9ekg3cnu0   | 143 ++++++++++++++++++
 4 files changed, 398 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3afc8fe3/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
index 5ec9d22..b03b54f 100644
--- a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
+++ b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java
@@ -74,7 +74,15 @@ public final class SshCommandSensor<T> extends AbstractAddSensorFeed<T> {
             "Value to be used if an error occurs whilst executing the ssh command", null);
     public static final MapConfigKey<Object> SENSOR_SHELL_ENVIRONMENT = BrooklynConfigKeys.SHELL_ENVIRONMENT;
 
-
+    // Fields are kept for deserialization purposes; however will rely on the values being
+    // re-computed from the config map, rather than being restored from persistence.
+    @SuppressWarnings("unused")
+    private String command;
+    @SuppressWarnings("unused")
+    private String executionDir;
+    @SuppressWarnings("unused")
+    private Map<String,Object> sensorEnv;
+    
     public SshCommandSensor(final ConfigBag params) {
         super(params);
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3afc8fe3/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshCommandSensorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshCommandSensorTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshCommandSensorTest.java
new file mode 100644
index 0000000..f463d9e
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshCommandSensorTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.core.mgmt.rebind;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import org.apache.brooklyn.api.location.LocationSpec;
+import org.apache.brooklyn.api.objs.BrooklynObjectType;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool;
+import org.apache.brooklyn.util.core.internal.ssh.RecordingSshTool.ExecCmd;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class RebindHistoricSshCommandSensorTest extends AbstractRebindHistoricTest {
+    
+    private static final String BLACKHOLE_IP = "240.0.0.1";
+    
+    @Override
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    /**
+     * The persisted state was generated when SshCommandSensor used fields for 'command' etc, populating
+     * them during init. Now it populates them lazily (thus handling deferred config suppliers).
+     * 
+     * It also used anonymous inner classes for the deferred suppliers.
+     * 
+     * Generated the persisted state using Brooklyn from Feb 2018, using the blueprint below:
+     * <pre>
+     * services:
+     *   - type: org.apache.brooklyn.entity.stock.BasicApplication
+     *     brooklyn.initializers:
+     *       - type: org.apache.brooklyn.core.sensor.ssh.SshCommandSensor
+     *          brooklyn.config:
+     *            name: myconf
+     *            targetType: String
+     *            period: 100ms
+     *            command: "echo 'myval'"
+     *            shell.env:
+     *              MY_ENV: myEnvVal
+     *            executionDir: '/path/to/myexecutiondir'
+     * <pre>
+    */   
+    @Test
+    public void testSshFeed_2018_02() throws Exception {
+        addMemento(BrooklynObjectType.ENTITY, "ssh-command-sensor-entity", "dnlz7hpbdg");
+        addMemento(BrooklynObjectType.FEED, "ssh-command-sensor-feed", "a9ekg3cnu0");
+        rebind();
+        
+        EntityInternal entity = (EntityInternal) mgmt().getEntityManager().getEntity("dnlz7hpbdg");
+        entity.feeds().getFeeds();
+        
+        SshMachineLocation recordingLocalhostMachine = mgmt().getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+                .configure("address", BLACKHOLE_IP)
+                .configure(SshMachineLocation.SSH_TOOL_CLASS, RecordingSshTool.class.getName()));
+        entity.addLocations(ImmutableList.of(recordingLocalhostMachine));
+        
+        ExecCmd cmd = Asserts.succeedsEventually(() -> RecordingSshTool.getLastExecCmd());
+        
+        assertTrue(cmd.commands.toString().contains("echo 'myval'"), "cmds="+cmd.commands);
+        assertEquals(cmd.env.get("MY_ENV"), "myEnvVal", "env="+cmd.env);
+        assertTrue(cmd.commands.toString().contains("/path/to/myexecutiondir"), "cmds="+cmd.commands);
+    }
+    
+    // This test is similar to testSshFeed_2017_01, except the persisted state file has been 
+    // hand-crafted to remove the bundle prefixes for "org.apache.brooklyn.*" bundles.
+    @Test
+    public void testFoo_2017_01_withoutBundlePrefixes() throws Exception {
+        addMemento(BrooklynObjectType.FEED, "ssh-feed-no-bundle-prefixes", "zv7t8bim62");
+        rebind();
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3afc8fe3/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-entity-dnlz7hpbdg
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-entity-dnlz7hpbdg b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-entity-dnlz7hpbdg
new file mode 100644
index 0000000..cb16e80
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-entity-dnlz7hpbdg
@@ -0,0 +1,150 @@
+<!--
+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.
+-->
+
+<entity>
+  <brooklynVersion>1.0.0-20180227.1009</brooklynVersion>
+  <type>org.apache.brooklyn.core:org.apache.brooklyn.entity.stock.BasicApplicationImpl</type>
+  <id>dnlz7hpbdg</id>
+  <displayName>Basic Application</displayName>
+  <searchPath class="ImmutableList"/>
+  <tags>
+    <org.apache.brooklyn.core:org.apache.brooklyn.core.mgmt.BrooklynTags_-NamedStringTag>
+      <kind>yaml_spec</kind>
+      <contents>services:
+  - type: org.apache.brooklyn.entity.stock.BasicApplication
+    brooklyn.initializers:
+      - type: org.apache.brooklyn.core.sensor.ssh.SshCommandSensor
+        brooklyn.config:
+          name: myconf
+          targetType: String
+          period: 5s
+          command: echo &apos;myval&apos;
+          shell.env:
+            MY_ENV: myEnvVal
+          executionDir: /path/to/myexecutiondir
+</contents>
+    </org.apache.brooklyn.core:org.apache.brooklyn.core.mgmt.BrooklynTags_-NamedStringTag>
+    <org.apache.brooklyn.core:org.apache.brooklyn.core.mgmt.BrooklynTags_-NamedStringTag>
+      <kind>yaml_spec</kind>
+      <contents>services:
+- type: org.apache.brooklyn.entity.stock.BasicApplication
+  name: Basic Application
+  description: The simplest application.</contents>
+    </org.apache.brooklyn.core:org.apache.brooklyn.core.mgmt.BrooklynTags_-NamedStringTag>
+  </tags>
+  <config>
+    <camp.template.id>kyb9EoI6</camp.template.id>
+  </config>
+  <attributes>
+    <service.notUp.indicators>
+      <MutableMap/>
+    </service.notUp.indicators>
+    <entity.id>dnlz7hpbdg</entity.id>
+    <application.id>dnlz7hpbdg</application.id>
+    <service.isUp type="boolean">true</service.isUp>
+    <service.problems>
+      <MutableMap/>
+    </service.problems>
+    <service.state type="org.apache.brooklyn.core:org.apache.brooklyn.core.entity.lifecycle.Lifecycle">RUNNING</service.state>
+    <service.state.expected>
+      <org.apache.brooklyn.core:org.apache.brooklyn.core.entity.lifecycle.Lifecycle_-Transition>
+        <state>RUNNING</state>
+        <timestampUtc>1524765391349</timestampUtc>
+      </org.apache.brooklyn.core:org.apache.brooklyn.core.entity.lifecycle.Lifecycle_-Transition>
+    </service.state.expected>
+    <myconf>
+      <null/>
+    </myconf>
+  </attributes>
+  <enrichers/>
+  <feeds>
+    <string>a9ekg3cnu0</string>
+  </feeds>
+  <configKeys>
+    <camp.template.id>
+      <configKey>
+        <name>camp.template.id</name>
+        <deprecatedNames class="ImmutableList" reference="../../../../searchPath"/>
+        <type>java.lang.String</type>
+        <description>UID of the component in the CAMP template from which this entity was created</description>
+        <reconfigurable>false</reconfigurable>
+        <runtimeInheritance class="org.apache.brooklyn.core:org.apache.brooklyn.core.config.BasicConfigInheritance$NeverInherited"/>
+        <constraint class="com.google.common.base.Predicates$ObjectPredicate">ALWAYS_TRUE</constraint>
+      </configKey>
+    </camp.template.id>
+  </configKeys>
+  <attributeKeys>
+    <service.notUp.indicators>
+      <attributeSensor>
+        <typeToken class="org.apache.brooklyn.core:org.apache.brooklyn.core.entity.Attributes$1" resolves-to="com.google.guava:com.google.common.reflect.TypeToken$SimpleTypeToken">
+          <runtimeType class="com.google.guava:com.google.common.reflect.Types$ParameterizedTypeImpl">
+            <argumentsList>
+              <java-class>java.lang.String</java-class>
+              <java-class>java.lang.Object</java-class>
+            </argumentsList>
+            <rawType>java.util.Map</rawType>
+          </runtimeType>
+        </typeToken>
+        <name>service.notUp.indicators</name>
+        <description>A map of namespaced indicators that the service is not up</description>
+        <persistence>REQUIRED</persistence>
+      </attributeSensor>
+    </service.notUp.indicators>
+    <service.problems>
+      <attributeSensor>
+        <typeToken class="org.apache.brooklyn.core:org.apache.brooklyn.core.entity.Attributes$2" resolves-to="com.google.guava:com.google.common.reflect.TypeToken$SimpleTypeToken">
+          <runtimeType class="com.google.guava:com.google.common.reflect.Types$ParameterizedTypeImpl">
+            <argumentsList>
+              <java-class>java.lang.String</java-class>
+              <java-class>java.lang.Object</java-class>
+            </argumentsList>
+            <rawType>java.util.Map</rawType>
+          </runtimeType>
+        </typeToken>
+        <name>service.problems</name>
+        <description>A map of namespaced indicators of problems with a service</description>
+        <persistence>REQUIRED</persistence>
+      </attributeSensor>
+    </service.problems>
+    <service.state>
+      <attributeSensor>
+        <type>org.apache.brooklyn.core:org.apache.brooklyn.core.entity.lifecycle.Lifecycle</type>
+        <name>service.state</name>
+        <description>Actual lifecycle state of the service</description>
+        <persistence>REQUIRED</persistence>
+      </attributeSensor>
+    </service.state>
+    <service.state.expected>
+      <attributeSensor>
+        <type>org.apache.brooklyn.core:org.apache.brooklyn.core.entity.lifecycle.Lifecycle$Transition</type>
+        <name>service.state.expected</name>
+        <description>Last controlled change to service state, indicating what the expected state should be</description>
+        <persistence>REQUIRED</persistence>
+      </attributeSensor>
+    </service.state.expected>
+    <myconf>
+      <attributeSensor>
+        <type>java.lang.String</type>
+        <name>myconf</name>
+        <description>myconf</description>
+        <persistence>REQUIRED</persistence>
+      </attributeSensor>
+    </myconf>
+  </attributeKeys>
+</entity>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/3afc8fe3/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-feed-a9ekg3cnu0
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-feed-a9ekg3cnu0 b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-feed-a9ekg3cnu0
new file mode 100644
index 0000000..240ff30
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-command-sensor-feed-a9ekg3cnu0
@@ -0,0 +1,143 @@
+<!--
+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.
+-->
+
+<feed>
+  <brooklynVersion>1.0.0-20180227.1009</brooklynVersion>
+  <type>org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed</type>
+  <id>a9ekg3cnu0</id>
+  <displayName>org.apache.brooklyn.feed.ssh.SshFeed</displayName>
+  <searchPath class="ImmutableList"/>
+  <tags>
+    <string>SshFeed[ssh[mkdir -p &apos;/path/to/myexecutio.../85a25c3b-&gt;myconf]]</string>
+  </tags>
+  <uniqueTag>SshFeed[ssh[mkdir -p &apos;/path/to/myexecutio.../85a25c3b-&gt;myconf]]</uniqueTag>
+  <config>
+    <feed.onlyIfServiceUp type="boolean">true</feed.onlyIfServiceUp>
+    <machine>
+      <null/>
+    </machine>
+    <execAsCommand type="boolean">false</execAsCommand>
+    <polls>
+      <com.google.guava:com.google.common.collect.HashMultimap serialization="custom">
+        <unserializable-parents/>
+        <com.google.guava:com.google.common.collect.HashMultimap>
+          <default/>
+          <int>2</int>
+          <int>1</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.AbstractCommandFeed_-CommandPollIdentifier>
+            <command class="org.apache.brooklyn.core:org.apache.brooklyn.core.sensor.ssh.SshCommandSensor$2">
+              <val_-entity>dnlz7hpbdg</val_-entity>
+              <outer-class>
+                <name>myconf</name>
+                <period>
+                  <nanos>100000000</nanos>
+                </period>
+                <type>String</type>
+                <sensor class="attributeSensor">
+                  <type>java.lang.String</type>
+                  <name>myconf</name>
+                  <description>myconf</description>
+                  <persistence>REQUIRED</persistence>
+                </sensor>
+                <params>
+                  <config>
+                    <name>myconf</name>
+                    <targetType>String</targetType>
+                    <period>5s</period>
+                    <command>echo &apos;myval&apos;</command>
+                    <shell.env>
+                      <map>
+                        <MY__ENV>myEnvVal</MY__ENV>
+                      </map>
+                    </shell.env>
+                    <executionDir>/path/to/myexecutiondir</executionDir>
+                  </config>
+                  <unusedConfig/>
+                  <live>false</live>
+                  <sealed>false</sealed>
+                </params>
+                <command>echo &apos;myval&apos;</command>
+                <executionDir>/path/to/myexecutiondir</executionDir>
+                <sensorEnv>
+                  <MY__ENV>myEnvVal</MY__ENV>
+                </sensorEnv>
+              </outer-class>
+            </command>
+            <env class="org.apache.brooklyn.core:org.apache.brooklyn.feed.CommandPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>1</size>
+                  </default>
+                  <int>1</int>
+                  <org.apache.brooklyn.core:org.apache.brooklyn.core.sensor.ssh.SshCommandSensor_-1>
+                    <val_-entity reference="../../../../../command/val_-entity"/>
+                    <outer-class reference="../../../../../command/outer-class"/>
+                  </org.apache.brooklyn.core:org.apache.brooklyn.core.sensor.ssh.SshCommandSensor_-1>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.AbstractCommandFeed_-CommandPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.CommandPollConfig>
+            <sensor class="attributeSensor" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.AbstractCommandFeed_-CommandPollIdentifier/command/outer-class/sensor"/>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$FunctionComposition">
+              <g class="org.apache.brooklyn.core:org.apache.brooklyn.core.sensor.ssh.SshCommandSensor$3">
+                <outer-class reference="../../../../org.apache.brooklyn.core:org.apache.brooklyn.feed.AbstractCommandFeed_-CommandPollIdentifier/command/outer-class"/>
+              </g>
+              <f class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshValueFunctions$Stdout"/>
+            </onsuccess>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.utils-common:org.apache.brooklyn.util.guava.Functionals$1FunctionAsPredicate">
+              <val_-f class="com.google.guava:com.google.common.base.Functions$FunctionComposition">
+                <g class="com.google.guava:com.google.common.base.Functions$PredicateFunction">
+                  <predicate class="com.google.guava:com.google.common.base.Predicates$IsEqualToPredicate">
+                    <target class="int">0</target>
+                  </predicate>
+                </g>
+                <f class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshValueFunctions$ExitStatus"/>
+              </val_-f>
+            </checkSuccess>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <logWarningGraceTimeOnStartup>
+              <nanos>0</nanos>
+            </logWarningGraceTimeOnStartup>
+            <logWarningGraceTime>
+              <nanos>0</nanos>
+            </logWarningGraceTime>
+            <period>5000</period>
+            <commandSupplier class="org.apache.brooklyn.core:org.apache.brooklyn.core.sensor.ssh.SshCommandSensor$2" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.AbstractCommandFeed_-CommandPollIdentifier/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.CommandPollConfig>
+        </com.google.guava:com.google.common.collect.HashMultimap>
+      </com.google.guava:com.google.common.collect.HashMultimap>
+    </polls>
+  </config>
+</feed>