This closes #1166
diff --git a/brooklyn-docs/guide/misc/release-notes.md b/brooklyn-docs/guide/misc/release-notes.md
index 3661ab1..ef79155 100644
--- a/brooklyn-docs/guide/misc/release-notes.md
+++ b/brooklyn-docs/guide/misc/release-notes.md
@@ -16,7 +16,7 @@
### Introduction
-Version 0.8.0 is [TODO add description]
+Version 0.9.0 is [TODO add description]
Thanks go to our community for their improvements, feedback and guidance, and
to Brooklyn's commercial users for funding much of this development.
@@ -24,7 +24,21 @@
### New Features
-[TODO]
+1. Parameters (config keys) can now be defined in YAML, using `brooklyn.parameters`.
+This allows YAML entities to advertise how they should be parameterized,
+for use in the UI and in documentation tools, and do coercion on these values.
+For a good demonstration, see the "Custom Entities" section of the YAML chapter of the user guide.
+
+[ TODO -
+restructuring and graduation;
+the `br` client CLI tool;
+test framework;
+`$brooklyn:external(...)` extension for taking values from other sources is supported in more places;
+better YAML editor in the UI;
+OSGi-native mode using Karaf, to simplify packaging of blueprints;
+a new pure-java WinRM client (winrm4j), hugely reducing the number of dependencies and distro size;
+several version bumps (jclouds) and performance and reliability improvements
+]
### Backwards Compatibility
@@ -49,10 +63,10 @@
and the referencing catalog item ID also takes priority; this has no effect in most cases, but if you have a chain of
referenced types blueprint plan source code and the catalog item ID are now set correctly.
-For changes in prior versions, please refer to the release notes for
-[0.8.0](/v/0.8.0-incubating/misc/release-notes.html).
-
3. Task cancellation is now propagated to dependent submitted tasks, including backgrounded tasks if they are transient.
Previously when a task was cancelled the API did not guarantee semantics but the behaviour was to cancel sub-tasks only
in very limited cases. Now the semantics are more precise and controllable, and more sub-tasks are cancelled.
This can prevent some leaked waits on `attributeWhenReady`.
+
+For changes in prior versions, please refer to the release notes for
+[0.8.0](/v/0.8.0-incubating/misc/release-notes.html).
diff --git a/brooklyn-docs/guide/yaml/custom-entities.md b/brooklyn-docs/guide/yaml/custom-entities.md
index 6f21c14..9698aa8 100644
--- a/brooklyn-docs/guide/yaml/custom-entities.md
+++ b/brooklyn-docs/guide/yaml/custom-entities.md
@@ -24,15 +24,26 @@
{% endhighlight %}
This starts a simple `nc` listener on port 4321 which will respond `hello` to the first
-session which connects to it. Test it by running `telnet localhost 4321`.
+session which connects to it. Test it by running `telnet localhost 4321`
+or opening `http://localhost:4321` in a browser.
+
+Note that it only allows you connect once, and after that it fails.
+This is deliberate! We'll repair this later in this example.
+Until then however, in the *Applications* view you can click the server,
+go to the `Effectors` tab, and click `restart` to bring if back to life.
This is just a simple script, but it shows how any script can be easily embedded here,
including a script to download and run other artifacts.
Many artifacts are already packaged such that they can be downloaded and launched
with a simple script, and `VanillaSoftwareProcess` can also be used for them.
-We can specify a `download.url` which downloads artifacts (unpacking TAR, TGZ, and ZIP archives)
+
+
+#### Downloading Files
+
+We can specify a `download.url` which downloads an artifact
+(and automatically unpacking TAR, TGZ, and ZIP archives)
before running `launch.command` relative to where that file is installed (or unpacked),
-with `./start.sh` being the default `launch.command`.
+with the default `launch.command` being `./start.sh`.
So if we create a file `/tmp/netcat-server.tgz` containing just `start.sh` in the root
which consists of the two lines in the previous example,
@@ -42,17 +53,130 @@
{% readj example_yaml/vanilla-bash-netcat-file.yaml %}
{% endhighlight %}
-The one requirement of the script is that it store the process ID (PID) in the file
+
+#### Port Inferencing
+
+If you're deploying to a cloud machine, a firewall might block the port 4321.
+We can tell Brooklyn to open this port explicitly by specifying `inboundPorts: [ 4321 ]`;
+however a more idiomatic way is to specify a config ending with `.port`,
+such as:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-port.yaml %}
+{% endhighlight %}
+
+The regex for ports to be opened can be configured using
+the config `inboundPorts.configRegex` (which has `.*\.port` as the default value).
+
+Config keys of type `org.apache.brooklyn.api.location.PortRange` (aka `port`)
+have special behaviour: when configuring, you can use range notation `8000-8100` or `8000+` to tell Brooklyn
+to find **one** port matching; this is useful when ports might be in use.
+In addition, any such config key will be opened,
+irrespective of whether it matches the `inboundPorts.configRegex`.
+To prevent any inferencing of ports to open, you can set the config `inboundPorts.autoInfer` to `false`.
+
+Note that in the example above, `netcat.port` must be specified in a `brooklyn.config` block.
+This block can be used to hold any config (including for example the `launch.command`),
+but for convenience Brooklyn allows config keys declared on the underlying type
+to be specified up one level, alongside the type.
+However config keys which are *not* declared on the type *must* be declared in the `brooklyn.config` block.
+
+
+#### Declaring New Config Keys
+
+We can define config keys to be presented to the user
+using the `brooklyn.parameters` block:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-port-parameter.yaml %}
+{% endhighlight %}
+
+The example above will allow a user to specify a message to send back
+and the port where netcat will listen.
+The metadata on these parameters is available at runtime in the UI
+and through the API, and is used when populating a catalog.
+
+The example also shows how these values can be passed as environment variables to the launch command.
+The `$brooklyn:config(...)` function returns the config value supplied or default.
+For the type `port`, an attribute sensor is also created to report the *actual* port used after port inference,
+and so the `$brooklyn:attributeWhenReady(...)` function is used.
+(If `$brooklyn:config("netcat.port")` had been used, `4321+` would be passed as `NETCAT_PORT`.)
+
+This gives us quite a bit more power in writing our blueprint:
+
+* Multiple instances of the server can be launched simultaneously on the same host,
+ as the `4321+` syntax enables Brooklyn to assign them different ports
+* If this type is added to the catalog, a user can configure the message and the port;
+ we'll show this in the next section
+
+
+#### Using the Catalog and Clustering
+
+The *Catalog* tab allows you to add blueprints which you can refer to in other blueprints.
+In that tab, click *+* then *YAML*, and enter the following:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-catalog.bom %}
+{% endhighlight %}
+
+This is the same example as in the previous section, wrapped according to the catalog YAML requirements,
+with one new block added defining an enricher. An enricher creates a new sensor from other values;
+in this case it will create a `main.uri` sensor by populating a `printf`-style string `"http://%s:%s"`
+with the sensor values.
+
+With this added to the catalog, we can reference the type `netcat-example` when we deploy an application.
+Return to the *Home* or *Applications* tab, click *+*, and submit this YAML blueprint:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-reference.yaml %}
+{% endhighlight %}
+
+This extends the previous blueprint which we registered in the catalog,
+meaning that we don't need to include it each time.
+Here, we've elected to supply our own message, but we'll use the default port.
+More importantly, we can package it for others to consume -- or take items others have built.
+
+We can go further and use this to deploy a cluster,
+this time giving a custom port as well as a custom message:
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-cluster.yaml %}
+{% endhighlight %}
+
+In either of the above examples, if you explore the tree in the *Applications* view
+and look at the *Summary* tab of any of the server instances, you'll now see the URL where netcat is running.
+But remember, netcat will stop after one run, so you'll only be able to use each link once
+before you have to restart it. You can also run `restart` on the cluster,
+and if you haven't yet experimented with `resize` on the cluster you might want to do that.
+
+
+#### Determining Successful Launch
+
+One requirement of the launch script is that it store the process ID (PID) in the file
pointed to by `$PID_FILE`, hence the second line of the script.
-This is because Brooklyn wants to monitor the services under management.
-(There are other options; you can set `checkRunning.command` and `stop.command` instead,
-as documented on the Javadoc of the `VanillaSoftwareProcess` class,
-and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.)
+This is because Brooklyn wants to monitor the services under management.
+You'll observe this if you connect to one of the netcat services,
+as the process exits afterwards and Brooklyn sets the entity to an `ON_FIRE` state.
+(You can also test this with a `killall nc` before connecting
+or issueing a `stop` command on the server -- but not on the example,
+as stopping an application root causes it to be removed altogether!)
+
+There are other options for determining launch: you can set `checkRunning.command` and `stop.command` instead,
+as documented on the javadoc and config keys of the {% include java_link.html class_name="VanillaSoftwareProcess" package_path="org/apache/brooklyn/entity/software/base" project_subpath="software/base" %} class,
+and those scripts will be used instead of checking and stopping the process whose PID is in `$PID_FILE`.
+
+{% highlight yaml %}
+{% readj example_yaml/vanilla-bash-netcat-more-commands.yaml %}
+{% endhighlight %}
And indeed, once you've run one `telnet` to the server, you'll see that the
-service has gone "on fire" in Brooklyn -- because the `nc` process has stopped.
+service has gone "on fire" in Brooklyn -- because the `nc` process stops after one run.
+
+
+#### Attaching Policies
+
Besides detecting this failure, Brooklyn policies can be added to the YAML to take appropriate
-action. A simple recovery here might just be to restart the process:
+action. A simple recovery here might just to automatically restart the process:
{% highlight yaml %}
{% readj example_yaml/vanilla-bash-netcat-restarter.yaml %}
@@ -67,6 +191,9 @@
Running with this blueprint, you'll see that the service shows as on fire for 15s after a `telnet`,
before the policy restarts it.
+
+### Sensors and Effectors
+
For an even more interesting way to test it, look at the blueprint defining
[a netcat server and client](example_yaml/vanilla-bash-netcat-w-client.yaml).
This uses `initializers` to define an effector to `sayHiNetcat` on the `Simple Pinger` client,
@@ -100,8 +227,11 @@
period: 1s
command: tail -1 server-input
-This is still a simple example, but worth going through carefully.
-It shows many of the building blocks used in real-world blueprints,
+
+#### Summary
+
+These examples are relatively simple example, but they
+illustrate many of the building blocks used in real-world blueprints,
and how they can often be easily described and combined in Brooklyn YAML blueprints.
Next, if you need to drive off-piste, or you want to write tests against these blueprints,
have a look at, for example, `VanillaBashNetcatYamlTest.java` in the Brooklyn codebase,
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom
new file mode 100644
index 0000000..7551818
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-catalog.bom
@@ -0,0 +1,35 @@
+brooklyn.catalog:
+ id: netcat-example
+ version: "1.0"
+ item:
+ type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+ name: Simple Netcat Server
+
+ launch.command: |
+ echo $MESSAGE | nc -l $NETCAT_PORT &
+ echo $! > $PID_FILE
+
+ env:
+ MESSAGE: $brooklyn:config("message")
+ NETCAT_PORT: $brooklyn:attributeWhenReady("netcat.port")
+
+ brooklyn.parameters:
+ - name: message
+ description: a message to send to the caller
+ default: hello
+ - name: netcat.port
+ type: port
+ description: the port netcat should run on
+ default: 4321+
+
+ brooklyn.enrichers:
+ - type: org.apache.brooklyn.enricher.stock.Transformer
+ brooklyn.config:
+ uniqueTag: main-uri-generator
+ enricher.sourceSensor: $brooklyn:sensor("host.address")
+ enricher.targetSensor: $brooklyn:sensor("main.uri")
+ enricher.targetValue:
+ $brooklyn:formatString:
+ - "http://%s:%s/"
+ - $brooklyn:attributeWhenReady("host.address")
+ - $brooklyn:attributeWhenReady("netcat.port")
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml
new file mode 100644
index 0000000..70b69af
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-cluster.yaml
@@ -0,0 +1,11 @@
+name: Netcat Cluster Example
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.group.DynamicCluster
+ memberSpec:
+ $brooklyn:entitySpec:
+ type: netcat-example
+ message: hello from cluster member
+ netcat.port: 8000+
+ initialSize: 3
+ restartMode: parallel
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml
new file mode 100644
index 0000000..f4e894f
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-more-commands.yaml
@@ -0,0 +1,16 @@
+name: Netcat Example with Explicit Check and Stop Commands
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+ name: Simple Netcat Server
+ launch.command: |
+ echo hello | nc -l 4321 &
+ echo $! > $PID_FILE
+
+ # The following overrides demonstrate the use of a custom shell environment as well as
+ # check-running and stop commands. These are optional; default behavior will "do the
+ # right thing" with the pid file automatically.
+
+ env: { CHECK_MARKER: "checkRunning", STOP_MARKER: "stop" }
+ checkRunning.command: echo $CHECK_MARKER >> DATE && test -f "$PID_FILE" && ps -p `cat $PID_FILE` >/dev/null
+ stop.command: echo $STOP_MARKER >> DATE && test -f "$PID_FILE" && { kill -9 `cat $PID_FILE`; rm /tmp/vanilla.pid; }
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml
new file mode 100644
index 0000000..90f83b4
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port-parameter.yaml
@@ -0,0 +1,21 @@
+name: Netcat Example with Parameter
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+ name: Simple Netcat Server
+ launch.command: |
+ echo $MESSAGE | nc -l $NETCAT_PORT &
+ echo $! > $PID_FILE
+
+ env:
+ MESSAGE: $brooklyn:config("message")
+ NETCAT_PORT: $brooklyn:attributeWhenReady("netcat.port")
+
+ brooklyn.parameters:
+ - name: message
+ description: a message to send to the caller
+ default: hello
+ - name: netcat.port
+ type: port
+ description: the port netcat should run on
+ default: 4321+
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml
new file mode 100644
index 0000000..3ec0212
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-port.yaml
@@ -0,0 +1,13 @@
+name: Netcat Example with Port Opened
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
+ name: Simple Netcat Server
+ launch.command: |
+ echo hello | nc -l 4321 &
+ echo $! > $PID_FILE
+
+ brooklyn.config:
+ # matching the regex `.*\.port` will cause the port to be opened
+ # if in a cloud where configurable security groups are available
+ netcat.port: 4321
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml
new file mode 100644
index 0000000..0f10c55
--- /dev/null
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-reference.yaml
@@ -0,0 +1,5 @@
+name: Netcat Type Reference Example
+location: localhost
+services:
+- type: netcat-example
+ message: hello from netcat using a registered type
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
index adaa933..47e54ab 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-restarter.yaml
@@ -1,4 +1,4 @@
-name: Simple Netcat Example with Restarter Policy
+name: Netcat Example with Restarter Policy
location: localhost
services:
- type: org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
index 8290b79..78ed99e 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat-w-client.yaml
@@ -1,4 +1,4 @@
-name: Simple Netcat with Client
+name: Netcat Example with Client
location: localhost
diff --git a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
index 3023038..df616af 100644
--- a/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
+++ b/brooklyn-docs/guide/yaml/example_yaml/vanilla-bash-netcat.yaml
@@ -6,13 +6,3 @@
launch.command: |
echo hello | nc -l 4321 &
echo $! > $PID_FILE
-
- # The following overrides demonstrate the use of a custom shell environment as well as
- # check-running and stop commands. These are optional; default behavior will "do the
- # right thing" with the pid file automatically.
-
- env: { CHECK_MARKER: "checkRunning", STOP_MARKER: "stop" }
- checkRunning.command: echo $CHECK_MARKER >> DATE && test -f "$PID_FILE" && ps -p `cat $PID_FILE` >/dev/null
- stop.command: echo $STOP_MARKER >> DATE && test -f "$PID_FILE" && { kill -9 `cat $PID_FILE`; rm /tmp/vanilla.pid; }
-
-# can also define download.url, in which case the launch command defaults to ./start.sh in that (archive) file
diff --git a/brooklyn-docs/guide/yaml/yaml-reference.md b/brooklyn-docs/guide/yaml/yaml-reference.md
index 36656bb..7a628a0 100644
--- a/brooklyn-docs/guide/yaml/yaml-reference.md
+++ b/brooklyn-docs/guide/yaml/yaml-reference.md
@@ -67,22 +67,32 @@
* `description`: short text describing the parameter behaviour/usage, presented
to the user
* `type`: the type of the parameter, one of `string`, `integer`, `long`, `float`,
- `double`, `timestamp`, `port`, a fully qualified Java type name. Default is `string`.
- * `default`: a default value, converted to the type above
- * `constraints`: a list of constraints the parameter should meet, currently
- `required` is supported
+ `double`, `timestamp`, `duration`, `port`, or a fully qualified Java type name;
+ the default is `string`;
+ obvious coercion is supported so
+ `timestamp` accepts most common ISO date formats, `duration` accepts `5m`, and port accepts `8080+`
+ * `default`: a default value; this will be coerced to the declared `type`
+ * `constraints`: a list of constraints the parameter should meet;
+ currently `required` is supported, with the default being not required
- A shorthand notation is also supported where the name of the parameter is directly
- passed as an item in the list. For example:
+ A shorthand notation is also supported where just the name of the parameter is supplied
+ as an item in the list, with the other values being unset or the default.
+ See `displayName` in the following example for an illustration of this:
~~~ yaml
brooklyn.parameters:
-- displayName
-- name: user.name
- constraints:
- - required
+# user.age parameter is required, and fully specified
- name: user.age
type: integer
+ label: Age
+ description: the age of the user
+ constraints:
+ - required
+# user.name is optional, and has a default
+- name: user.name
+ default: You
+# shorthand notation: displayName will be an optional config of type string with no default
+- displayName
~~~
Entities, policies, and initializers may accept additional key-value pairs,
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
index 4e9dc62..fd7047e 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/objs/SpecParameter.java
@@ -20,13 +20,23 @@
import java.io.Serializable;
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigKey;
+/** A wrapper around a {@link ConfigKey} which will be added to an {@link Entity},
+ * providing additional information for rendering in a UI */
public interface SpecParameter<T> extends Serializable {
/** Short name, to be used in UI */
String getLabel();
- /** Visible by default in UI, not all inputs may be visible at once */
+ /** Whether visible by default in UI, not all inputs may be visible at once */
boolean isPinned();
- /** Type information for the input */
- ConfigKey<T> getType();
+ /** All config key info for this spec parameter;
+ * this is the config key which is added to the defined type */
+ ConfigKey<T> getConfigKey();
+ /** An optional sensor which may also be added to the defined type */
+ @Nullable AttributeSensor<?> getSensor();
+
}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
index fdf5807..869ebc0 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/SpecParameterParsingTest.java
@@ -62,20 +62,20 @@
SpecParameter<?> firstInput = inputs.get(0);
assertEquals(firstInput.getLabel(), "simple");
assertEquals(firstInput.isPinned(), true);
- assertEquals(firstInput.getType().getName(), "simple");
- assertEquals(firstInput.getType().getTypeToken(), TypeToken.of(String.class));
+ assertEquals(firstInput.getConfigKey().getName(), "simple");
+ assertEquals(firstInput.getConfigKey().getTypeToken(), TypeToken.of(String.class));
SpecParameter<?> secondInput = inputs.get(1);
assertEquals(secondInput.getLabel(), "explicit_name");
assertEquals(secondInput.isPinned(), true);
- assertEquals(secondInput.getType().getName(), "explicit_name");
- assertEquals(secondInput.getType().getTypeToken(), TypeToken.of(String.class));
+ assertEquals(secondInput.getConfigKey().getName(), "explicit_name");
+ assertEquals(secondInput.getConfigKey().getTypeToken(), TypeToken.of(String.class));
SpecParameter<?> thirdInput = inputs.get(2);
assertEquals(thirdInput.getLabel(), "third_input");
assertEquals(thirdInput.isPinned(), true);
- assertEquals(thirdInput.getType().getName(), "third_input");
- assertEquals(thirdInput.getType().getTypeToken(), TypeToken.of(Integer.class));
+ assertEquals(thirdInput.getConfigKey().getName(), "third_input");
+ assertEquals(thirdInput.getConfigKey().getTypeToken(), TypeToken.of(Integer.class));
}
@Test
@@ -99,8 +99,8 @@
SpecParameter<?> firstInput = inputs.get(0);
assertEquals(firstInput.getLabel(), "simple");
assertTrue(firstInput.isPinned());
- assertEquals(firstInput.getType().getName(), "simple");
- assertEquals(firstInput.getType().getTypeToken().getRawType().getName(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY);
+ assertEquals(firstInput.getConfigKey().getName(), "simple");
+ assertEquals(firstInput.getConfigKey().getTypeToken().getRawType().getName(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY);
}
@Test
@@ -126,7 +126,7 @@
SpecParameter<?> input = inputs.get(0);
assertEquals(input.getLabel(), "more_config");
assertFalse(input.isPinned());
- assertEquals(input.getType().getName(), "more_config");
+ assertEquals(input.getConfigKey().getName(), "more_config");
}
private String add(String... def) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
index 2e59185..f158c2c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/BasicConfigKey.java
@@ -49,7 +49,6 @@
public class BasicConfigKey<T> implements ConfigKeySelfExtracting<T>, Serializable {
- @SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(BasicConfigKey.class);
private static final long serialVersionUID = -1762014059150215376L;
@@ -119,6 +118,13 @@
public BasicConfigKey<T> build() {
return new BasicConfigKey<T>(this);
}
+
+ public String getName() {
+ return name;
+ }
+ public String getDescription() {
+ return description;
+ }
}
private String name;
@@ -165,7 +171,7 @@
this.constraint = Predicates.alwaysTrue();
}
- protected BasicConfigKey(Builder<T> builder) {
+ public BasicConfigKey(Builder<T> builder) {
this.name = checkNotNull(builder.name, "name");
this.type = TypeTokens.getRawTypeIfRaw(checkNotNull(builder.type, "type"));
this.typeToken = TypeTokens.getTypeTokenIfNotRaw(builder.type);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
index 1a273f6..c508349 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/config/ConfigConstraints.java
@@ -28,10 +28,9 @@
import org.apache.brooklyn.api.objs.BrooklynObject;
import org.apache.brooklyn.api.objs.EntityAdjunct;
import org.apache.brooklyn.config.ConfigKey;
-import org.apache.brooklyn.core.location.AbstractLocation;
import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
-import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
+import org.apache.brooklyn.core.objs.BrooklynObjectPredicate;
import org.apache.brooklyn.util.guava.Maybe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
index 63ea52d..9b10d1d 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
@@ -173,14 +173,26 @@
return ConfigKeys.newConfigKey(paramType.getParameterClass(), paramType.getName(), paramType.getDescription(), paramType.getDefaultValue());
}
- /** returns an unsubmitted task which will invoke the given effector on the given entities;
- * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+ /** convenience for {@link #invocationParallel(Effector, Map, Iterable)} */
public static TaskAdaptable<List<?>> invocation(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+ return invocationParallel(eff, params, entities);
+ }
+
+ /** returns an unsubmitted task which will invoke the given effector on the given entities in parallel;
+ * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+ public static TaskAdaptable<List<?>> invocationParallel(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
for (Entity e: entities) tasks.add(invocation(e, eff, params));
return Tasks.parallel("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
}
+ /** as {@link #invocationParallel(Effector, Map, Iterable)} but executing sequentially */
+ public static TaskAdaptable<List<?>> invocationSequential(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+ List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
+ for (Entity e: entities) tasks.add(invocation(e, eff, params));
+ return Tasks.sequential("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
+ }
+
/** returns an unsubmitted task which will invoke the given effector on the given entities
* (this form of method is a convenience for {@link #invocation(Effector, Map, Iterable)}) */
public static TaskAdaptable<List<?>> invocation(Effector<?> eff, MutableMap<?, ?> params, Entity ...entities) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
index 6cb8bb4..1d4cfea 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/AbstractConfigurationSupportInternal.java
@@ -19,7 +19,6 @@
package org.apache.brooklyn.core.objs;
-import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.apache.brooklyn.api.mgmt.ExecutionContext;
@@ -28,8 +27,8 @@
import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
import org.apache.brooklyn.util.core.flags.TypeCoercions;
import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.core.task.ValueResolver;
import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.time.Duration;
public abstract class AbstractConfigurationSupportInternal implements BrooklynObjectInternal.ConfigurationSupportInternal {
@@ -63,7 +62,7 @@
Object resolved = Tasks.resolving(unresolved)
.as(Object.class)
.defaultValue(marker)
- .timeout(Duration.of(5, TimeUnit.MILLISECONDS))
+ .timeout(ValueResolver.REAL_REAL_QUICK_WAIT)
.context(getContext())
.swallowExceptions()
.get();
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
index dfbe1c1..2bc2346 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BasicSpecParameter.java
@@ -36,12 +36,18 @@
import org.apache.brooklyn.api.objs.BrooklynObject;
import org.apache.brooklyn.api.objs.BrooklynType;
import org.apache.brooklyn.api.objs.SpecParameter;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
+import org.apache.brooklyn.core.sensor.PortAttributeSensorAndConfigKey;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.time.Duration;
+import com.google.common.annotations.Beta;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
@@ -54,14 +60,26 @@
public class BasicSpecParameter<T> implements SpecParameter<T>{
private static final long serialVersionUID = -4728186276307619778L;
- private String label;
- private boolean pinned;
- private ConfigKey<T> type;
+ private final String label;
+
+ /** pinning may become a priority or other more expansive indicator */
+ @Beta
+ private final boolean pinned;
+
+ private final ConfigKey<T> configKey;
+ private final AttributeSensor<?> sensor;
- public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> type) {
+ @Beta // TBD whether "pinned" stays
+ public BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config) {
+ this(label, pinned, config, null);
+ }
+
+ @Beta // TBD whether "pinned" and "sensor" stay
+ public <SensorType> BasicSpecParameter(String label, boolean pinned, ConfigKey<T> config, AttributeSensor<SensorType> sensor) {
this.label = label;
this.pinned = pinned;
- this.type = type;
+ this.configKey = config;
+ this.sensor = sensor;
}
@Override
@@ -73,15 +91,20 @@
public boolean isPinned() {
return pinned;
}
+
+ @Override
+ public ConfigKey<T> getConfigKey() {
+ return configKey;
+ }
@Override
- public ConfigKey<T> getType() {
- return type;
+ public AttributeSensor<?> getSensor() {
+ return sensor;
}
@Override
public int hashCode() {
- return Objects.hashCode(label, pinned, type);
+ return Objects.hashCode(label, pinned, configKey);
}
@Override
@@ -95,7 +118,7 @@
BasicSpecParameter<?> other = (BasicSpecParameter<?>) obj;
return Objects.equal(label, other.label) &&
pinned == other.pinned &&
- Objects.equal(type, other.type);
+ Objects.equal(configKey, other.configKey);
}
@Override
@@ -103,7 +126,7 @@
return Objects.toStringHelper(this)
.add("label", label)
.add("pinned", pinned)
- .add("type", type)
+ .add("type", configKey)
.toString();
}
@@ -154,6 +177,7 @@
.put("long", Long.class)
.put("float", Float.class)
.put("double", Double.class)
+ .put("duration", Duration.class)
.put("timestamp", Date.class)
.put("port", PortRange.class)
.build();
@@ -191,13 +215,23 @@
throw new IllegalArgumentException("'name' value missing from input definition " + obj + " but is required. Check for typos.");
}
- ConfigKey inputType = BasicConfigKey.builder(inferType(type, loader))
- .name(name)
- .description(description)
- .defaultValue(defaultValue)
- .constraint(constraints)
- .build();
- return new BasicSpecParameter(Objects.firstNonNull(label, name), true, inputType);
+ ConfigKey configType;
+ AttributeSensor sensorType = null;
+
+ TypeToken typeToken = inferType(type, loader);
+ Builder builder = BasicConfigKey.builder(typeToken)
+ .name(name)
+ .description(description)
+ .defaultValue(defaultValue)
+ .constraint(constraints);
+
+ if (PortRange.class.equals(typeToken.getRawType())) {
+ sensorType = new PortAttributeSensorAndConfigKey(builder);
+ configType = ((HasConfigKey)sensorType).getConfigKey();
+ } else {
+ configType = builder.build();
+ }
+ return new BasicSpecParameter(Objects.firstNonNull(label, name), true, configType, sensorType);
}
@SuppressWarnings({ "rawtypes" })
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
index 2da7463..89c4e32 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/BrooklynObjectInternal.java
@@ -69,6 +69,9 @@
/**
* Returns the uncoerced value for this config key, if available, not taking any default.
* If there is no local value and there is an explicit inherited value, will return the inherited.
+ * Returns {@link Maybe#absent()} if the key is not explicitly set on this object or an ancestor.
+ * <p>
+ * See also {@link #getLocalRaw(ConfigKey).
*/
@Beta
Maybe<Object> getRaw(ConfigKey<?> key);
@@ -82,6 +85,9 @@
/**
* Returns the uncoerced value for this config key, if available,
* not following any inheritance chains and not taking any default.
+ * Returns {@link Maybe#absent()} if the key is not explicitly set on this object.
+ * <p>
+ * See also {@link #getRaw(ConfigKey).
*/
@Beta
Maybe<Object> getLocalRaw(ConfigKey<?> key);
@@ -96,6 +102,11 @@
* Attempts to coerce the value for this config key, if available,
* taking a default and {@link Maybe#absent absent} if the uncoerced
* cannot be resolved within a short timeframe.
+ * <p>
+ * Note: if no value for the key is available, not even as a default,
+ * this returns a {@link Maybe#isPresent()} containing <code>null</code>
+ * (following the semantics of {@link #get(ConfigKey)}
+ * rather than {@link #getRaw(ConfigKey)}).
*/
@Beta
<T> Maybe<T> getNonBlocking(ConfigKey<T> key);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
index eb4ff10..e55d6d9 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/objs/proxy/InternalEntityFactory.java
@@ -44,6 +44,7 @@
import org.apache.brooklyn.core.entity.AbstractApplication;
import org.apache.brooklyn.core.entity.AbstractEntity;
import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityDynamicType;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
@@ -248,13 +249,13 @@
}
entity.tags().addTags(spec.getTags());
- ((AbstractEntity)entity).configure(getConfigKeysFromSpecParameters(spec));
- ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
+ addSpecParameters(spec, ((AbstractEntity)entity).getMutableEntityType());
+ ((AbstractEntity)entity).configure(MutableMap.copyOf(spec.getFlags()));
for (Map.Entry<ConfigKey<?>, Object> entry : spec.getConfig().entrySet()) {
entity.config().set((ConfigKey)entry.getKey(), entry.getValue());
}
-
+
Entity parent = spec.getParent();
if (parent != null) {
parent = (parent instanceof AbstractEntity) ? ((AbstractEntity)parent).getProxyIfAvailable() : parent;
@@ -268,12 +269,11 @@
}
}
- private <T extends Entity> List<ConfigKey<?>> getConfigKeysFromSpecParameters(EntitySpec<T> spec) {
- List<ConfigKey<?>> configKeys = MutableList.of();
+ private void addSpecParameters(EntitySpec<?> spec, EntityDynamicType edType) {
for (SpecParameter<?> param : spec.getParameters()) {
- configKeys.add(param.getType());
+ edType.addConfigKey(param.getConfigKey());
+ if (param.getSensor()!=null) edType.addSensor(param.getSensor());
}
- return configKeys;
}
/**
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
index f76baaa..2a26fa7 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/AttributeSensorAndConfigKey.java
@@ -19,11 +19,11 @@
package org.apache.brooklyn.core.sensor;
import org.apache.brooklyn.api.entity.Entity;
-import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey.Builder;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.entity.AbstractEntity;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
@@ -45,6 +45,7 @@
*/
public abstract class AttributeSensorAndConfigKey<ConfigType,SensorType> extends BasicAttributeSensor<SensorType>
implements ConfigKey.HasConfigKey<ConfigType> {
+
private static final long serialVersionUID = -3103809215973264600L;
private static final Logger log = LoggerFactory.getLogger(AttributeSensorAndConfigKey.class);
@@ -87,6 +88,10 @@
configKey = ConfigKeys.newConfigKeyWithDefault(orig.configKey,
TypeCoercions.coerce(defaultValue, orig.configKey.getTypeToken()));
}
+ public AttributeSensorAndConfigKey(Builder<ConfigType> configKeyBuilder, TypeToken<SensorType> sensorType) {
+ super(sensorType, configKeyBuilder.getName(), configKeyBuilder.getDescription());
+ configKey = new BasicConfigKey<ConfigType>(configKeyBuilder);
+ }
public ConfigKey<ConfigType> getConfigKey() { return configKey; }
@@ -106,7 +111,7 @@
SensorType sensorValue = e.getAttribute(this);
if (sensorValue!=null) return sensorValue;
- ConfigType v = ((EntityLocal)e).getConfig(this);
+ ConfigType v = e.config().get(this);
try {
return convertConfigToSensor(v, e);
} catch (Throwable t) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
index 3de0de6..aa396d2 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/PortAttributeSensorAndConfigKey.java
@@ -29,6 +29,7 @@
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.BasicConfigKey;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.internal.BrooklynInitialization;
import org.apache.brooklyn.core.location.Locations;
@@ -40,6 +41,7 @@
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
+import com.google.common.reflect.TypeToken;
/**
* A {@link Sensor} describing a port on a system,
@@ -68,6 +70,10 @@
public PortAttributeSensorAndConfigKey(PortAttributeSensorAndConfigKey orig, Object defaultValue) {
super(orig, TypeCoercions.coerce(defaultValue, PortRange.class));
}
+ public PortAttributeSensorAndConfigKey(BasicConfigKey.Builder<PortRange> builder) {
+ super(builder, TypeToken.of(Integer.class));
+ }
+
@Override
protected Integer convertConfigToSensor(PortRange value, Entity entity) {
if (value==null) return null;
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
index 781cb0c..f09e72f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicCluster.java
@@ -98,6 +98,14 @@
MethodEffector<Collection<Entity>> RESIZE_BY_DELTA = new MethodEffector<Collection<Entity>>(DynamicCluster.class, "resizeByDelta");
+ @SetFromFlag("restartMode")
+ ConfigKey<String> RESTART_MODE = ConfigKeys.newStringConfigKey(
+ "dynamiccluster.restartMode",
+ "How this cluster should handle restarts; "
+ + "by default it is disallowed, but this key can specify a different mode. "
+ + "Modes supported by dynamic cluster are 'off', 'sequqential', or 'parallel'. "
+ + "However subclasses can define their own modes or may ignore this.", null);
+
@SetFromFlag("quarantineFailedEntities")
ConfigKey<Boolean> QUARANTINE_FAILED_ENTITIES = ConfigKeys.newBooleanConfigKey(
"dynamiccluster.quarantineFailedEntities", "If true, will quarantine entities that fail to start; if false, will get rid of them (i.e. delete them)", true);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
index f434fcf..aaf06c5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
@@ -47,6 +47,7 @@
import org.apache.brooklyn.core.config.render.RendererHints;
import org.apache.brooklyn.core.effector.Effectors;
import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityPredicates;
import org.apache.brooklyn.core.entity.factory.EntityFactory;
import org.apache.brooklyn.core.entity.factory.EntityFactoryForLocation;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
@@ -522,7 +523,28 @@
@Override
public void restart() {
- throw new UnsupportedOperationException();
+ String mode = getConfig(RESTART_MODE);
+ if (mode==null) {
+ throw new UnsupportedOperationException("Restart not supported for this cluster: "+RESTART_MODE.getName()+" is not configured.");
+ }
+ if ("off".equalsIgnoreCase(mode)) {
+ throw new UnsupportedOperationException("Restart not supported for this cluster.");
+ }
+
+ if ("sequential".equalsIgnoreCase(mode)) {
+ ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+ DynamicTasks.queue(Effectors.invocationSequential(Startable.RESTART, null,
+ Iterables.filter(getChildren(), Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged()))));
+ } else if ("parallel".equalsIgnoreCase(mode)) {
+ ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
+ DynamicTasks.queue(Effectors.invocationParallel(Startable.RESTART, null,
+ Iterables.filter(getChildren(), Predicates.and(Predicates.instanceOf(Startable.class), EntityPredicates.isManaged()))));
+ } else {
+ throw new IllegalArgumentException("Unknown "+RESTART_MODE.getName()+" '"+mode+"'");
+ }
+
+ DynamicTasks.waitForLast();
+ ServiceStateLogic.setExpectedState(this, Lifecycle.RUNNING);
}
@Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
index 85c9537..3bc22e0 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/FlagUtils.java
@@ -234,7 +234,7 @@
public static List<FlagConfigKeyAndValueRecord> findAllParameterConfigKeys(List<SpecParameter<?>> parameters, ConfigBag input) {
List<FlagConfigKeyAndValueRecord> output = new ArrayList<FlagUtils.FlagConfigKeyAndValueRecord>();
for (SpecParameter<?> param : parameters) {
- FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getType(), input);
+ FlagConfigKeyAndValueRecord record = getFlagConfigKeyRecord(null, param.getConfigKey(), input);
if (record.isValuePresent())
output.add(record);
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
index f850fbd..872e713 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/flags/TypeCoercions.java
@@ -122,10 +122,10 @@
return coerce(value, TypeToken.of(targetType));
}
- /** @see #coerce(Object, Class) */
+ /** @see #coerce(Object, Class); allows a null value in the contents of the Maybe */
public static <T> Maybe<T> tryCoerce(Object value, TypeToken<T> targetTypeToken) {
try {
- return Maybe.of( coerce(value, targetTypeToken) );
+ return Maybe.ofAllowingNull( coerce(value, targetTypeToken) );
} catch (Throwable t) {
Exceptions.propagateIfFatal(t);
return Maybe.absent(t);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
index 2809482..0f85c3f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java
@@ -42,6 +42,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.Beta;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -57,14 +58,25 @@
*/
public class ValueResolver<T> implements DeferredSupplier<T> {
+ // TODO most of these usages should be removed when we have
+ // an ability to run resolution in a non-blocking mode
+ // (i.e. running resolution tasks in the same thread,
+ // or in a context where they can only wait on subtasks
+ // which are guaranteed to have the same constraint)
/**
* Period to wait if we're expected to return real quick
* but we want fast things to have time to finish.
* <p>
* Timings are always somewhat arbitrary but this at least
* allows some intention to be captured in code rather than arbitrary values. */
+ @Beta
public static Duration REAL_QUICK_WAIT = Duration.millis(50);
/**
+ * Like {@link #REAL_QUICK_WAIT} but even smaller, for use when potentially
+ * resolving multiple items in sequence. */
+ @Beta
+ public static Duration REAL_REAL_QUICK_WAIT = Duration.millis(5);
+ /**
* Period to wait if we're expected to return quickly
* but we want to be a bit more generous for things to finish,
* without letting a caller get annoyed.
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
index 49cb2d6..30745f0 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromClassTest.java
@@ -103,7 +103,7 @@
private void assertInput(SpecParameter<?> input, String label, boolean pinned, ConfigKey<?> type) {
assertEquals(input.getLabel(), label);
assertEquals(input.isPinned(), pinned);
- assertEquals(input.getType(), type);
+ assertEquals(input.getConfigKey(), type);
}
}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
index 9f2eaaf..07ad81a 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/objs/BasicSpecParameterFromListTest.java
@@ -56,7 +56,7 @@
SpecParameter<?> input = parse(name);
assertEquals(input.getLabel(), name);
assertTrue(input.isPinned());
- ConfigKey<?> type = input.getType();
+ ConfigKey<?> type = input.getConfigKey();
assertEquals(type.getName(), name);
assertEquals(type.getTypeToken(), TypeToken.of(String.class));
assertNull(type.getDefaultValue());
@@ -70,8 +70,8 @@
String name = "minRam";
SpecParameter<?> input = parse(ImmutableMap.of("name", name));
assertEquals(input.getLabel(), name);
- assertEquals(input.getType().getName(), name);
- assertEquals(input.getType().getTypeToken(), TypeToken.of(String.class));
+ assertEquals(input.getConfigKey().getName(), name);
+ assertEquals(input.getConfigKey().getTypeToken(), TypeToken.of(String.class));
}
@Test
@@ -99,7 +99,7 @@
assertEquals(input.getLabel(), label);
assertTrue(input.isPinned());
- ConfigKey<?> type = input.getType();
+ ConfigKey<?> type = input.getConfigKey();
assertEquals(type.getName(), name);
assertEquals(type.getTypeToken(), TypeToken.of(String.class));
assertEquals(type.getDefaultValue(), defaultValue);
@@ -123,7 +123,7 @@
assertEquals(input.getLabel(), name);
assertTrue(input.isPinned());
- ConfigKey<?> type = input.getType();
+ ConfigKey<?> type = input.getConfigKey();
assertEquals(type.getName(), name);
assertEquals(type.getDefaultValue(), defaultValue);
assertEquals(type.getDescription(), description);
@@ -137,7 +137,7 @@
SpecParameter<?> input = parse(ImmutableMap.of(
"name", name,
"constraints", ImmutableList.of(constraint)));
- ConfigKey<?> type = input.getType();
+ ConfigKey<?> type = input.getConfigKey();
assertConstraint(type.getConstraint(), StringPredicates.isNonBlank());
}
@@ -153,7 +153,7 @@
SpecParameter<?> input = parse(ImmutableMap.of(
"name", name,
"type", BasicSpecParameterFromListTest.class.getName()));
- assertEquals(input.getType().getTypeToken(), TypeToken.of(BasicSpecParameterFromListTest.class));
+ assertEquals(input.getConfigKey().getTypeToken(), TypeToken.of(BasicSpecParameterFromListTest.class));
}
@Test(expectedExceptions = IllegalArgumentException.class)
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
index 74625fd..514d9c9 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/CatalogTransformer.java
@@ -68,15 +68,21 @@
Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator());
EntitySpec<?> spec = null;
-
try {
- spec = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ // the raw type isn't needed according to eclipse IDE, but jenkins maven fails without it;
+ // must be a java version or compiler thing. don't remove even though it looks okay without it!
+ EntitySpec<?> specRaw = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
+ spec = specRaw;
EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType());
EntityType type = typeMap.getSnapshot();
AtomicInteger paramPriorityCnt = new AtomicInteger();
- for (SpecParameter<?> input: spec.getParameters())
+ for (SpecParameter<?> input: spec.getParameters()) {
config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt));
+ if (input.getSensor()!=null)
+ sensors.add(SensorTransformer.sensorSummaryForCatalog(input.getSensor()));
+ }
for (Sensor<?> x: type.getSensors())
sensors.add(SensorTransformer.sensorSummaryForCatalog(x));
for (Effector<?> x: type.getEffectors())
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
index 2d9f8a0..f5079b9 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/transform/EntityTransformer.java
@@ -159,7 +159,7 @@
// which results in correctly ordered items on the wire (as a list). Clients which use the java bindings
// though will push the items in an unordered set - so give them means to recover the correct order.
Double priority = input.isPinned() ? Double.valueOf(paramPriorityCnt.incrementAndGet()) : null;
- return entityConfigSummary(input.getType(), input.getLabel(), priority, null);
+ return entityConfigSummary(input.getConfigKey(), input.getLabel(), priority, null);
}
}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
index 91aa294..cb4149a 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessDriverLifecycleEffectorTasks.java
@@ -118,7 +118,8 @@
entity().initDriver(machine);
// Note: must only apply config-sensors after adding to locations and creating driver;
- // otherwise can't do things like acquire free port from location, or allowing driver to set up ports
+ // otherwise can't do things like acquire free port from location
+ // or allowing driver to set up ports; but must be careful in init not to block on these!
super.preStartCustom(machine);
entity().preStart();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
index dd01a69..9c955ed 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/VanillaSoftwareProcessSshDriver.java
@@ -21,11 +21,12 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolver;
+import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal.ConfigurationSupportInternal;
import org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.collections.MutableMap;
@@ -54,15 +55,30 @@
*/
@Override
protected String getInstallLabelExtraSalt() {
+ // run non-blocking in case a value set later is used (e.g. a port)
+ Integer hash = hashCodeIfResolved(SoftwareProcess.DOWNLOAD_URL.getConfigKey(),
+ VanillaSoftwareProcess.INSTALL_COMMAND, SoftwareProcess.SHELL_ENVIRONMENT);
- Maybe<Object> url = getEntity().getConfigRaw(SoftwareProcess.DOWNLOAD_URL, true);
- String installCommand = getEntity().getConfig(VanillaSoftwareProcess.INSTALL_COMMAND);
- Map<String, Object> shellEnv = getEntity().getConfig(VanillaSoftwareProcess.SHELL_ENVIRONMENT);
+ // if any of the above blocked then we must make a unique install label,
+ // as other yet-unknown config is involved
+ if (hash==null) return Identifiers.makeRandomId(8);
- // TODO a user-friendly hash would be nice, but tricky since we don't want it to be too long or contain path chars
- return Identifiers.makeIdFromHash(Objects.hash(url.or(null), installCommand, shellEnv));
+ // a user-friendly hash is nice, but tricky since it would have to be short;
+ // go with a random one unless it's totally blank
+ if (hash==0) return "default";
+ return Identifiers.makeIdFromHash(hash);
}
+ private Integer hashCodeIfResolved(ConfigKey<?> ...keys) {
+ int hash = 0;
+ for (ConfigKey<?> k: keys) {
+ Maybe<?> value = ((ConfigurationSupportInternal)getEntity().config()).getNonBlocking(k);
+ if (value.isAbsent()) return null;
+ hash = hash*31 + (value.get()==null ? 0 : value.get().hashCode());
+ }
+ return hash;
+ }
+
@Override
public void install() {
Maybe<Object> url = getEntity().getConfigRaw(SoftwareProcess.DOWNLOAD_URL, true);