This closes #1148
diff --git a/LICENSE b/LICENSE
index ca60394..ef288dc 100644
--- a/LICENSE
+++ b/LICENSE
@@ -282,6 +282,13 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Vitaly Puzrin (2011-2015)
+This project includes the software: marked.js
+ Available at: https://github.com/chjj/marked
+ Developed by: Christopher Jeffrey (https://github.com/chjj)
+ Version used: 0.3.1
+ Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+ Copyright (c) Christopher Jeffrey (2011-2014)
+
This project includes the software: moment.js
Available at: http://momentjs.com
Developed by: Tim Wood (http://momentjs.com)
@@ -323,19 +330,11 @@
Arpad Borsos (2012)
Used under the BSD 2-Clause license.
-This project includes the software: Swagger JS
- Available at: https://github.com/wordnik/swagger-js
- Inclusive of: swagger.js
- Version used: 1.0.1
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- Copyright (c) SmartBear Software (2011-2015)
+This project includes the software: swagger
+ Used under the following license: <no license info>
-This project includes the software: Swagger UI
- Available at: https://github.com/wordnik/swagger-ui
- Inclusive of: swagger-ui.js
- Version used: 1.0.1
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- Copyright (c) SmartBear Software (2011-2015)
+This project includes the software: swagger-ui
+ Used under the following license: <no license info>
This project includes the software: typeahead.js
Available at: https://github.com/twitter/typeahead.js
diff --git a/brooklyn-dist/dist/licensing/extras-files b/brooklyn-dist/dist/licensing/extras-files
index bb4a3b3..cbba9c5 100644
--- a/brooklyn-dist/dist/licensing/extras-files
+++ b/brooklyn-dist/dist/licensing/extras-files
@@ -1 +1 @@
-../jsgui/src/main/license/source-inclusions.yaml:../cli/src/main/license/source-inclusions.yaml
+../../brooklyn-ui/src/main/license/source-inclusions.yaml:../../brooklyn-server/server-cli/src/main/license/source-inclusions.yaml
diff --git a/brooklyn-dist/dist/licensing/licenses/jsgui/BSD-2-Clause b/brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-2-Clause
similarity index 100%
rename from brooklyn-dist/dist/licensing/licenses/jsgui/BSD-2-Clause
rename to brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-2-Clause
diff --git a/brooklyn-dist/dist/licensing/licenses/jsgui/BSD-3-Clause b/brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-3-Clause
similarity index 100%
rename from brooklyn-dist/dist/licensing/licenses/jsgui/BSD-3-Clause
rename to brooklyn-dist/dist/licensing/licenses/brooklyn-ui/BSD-3-Clause
diff --git a/brooklyn-dist/dist/licensing/licenses/cli/MIT b/brooklyn-dist/dist/licensing/licenses/brooklyn-ui/MIT
similarity index 100%
copy from brooklyn-dist/dist/licensing/licenses/cli/MIT
copy to brooklyn-dist/dist/licensing/licenses/brooklyn-ui/MIT
diff --git a/brooklyn-dist/dist/licensing/licenses/jsgui/MIT b/brooklyn-dist/dist/licensing/licenses/jsgui/MIT
deleted file mode 100644
index 71dfb45..0000000
--- a/brooklyn-dist/dist/licensing/licenses/jsgui/MIT
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License ("MIT")
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-
diff --git a/brooklyn-dist/dist/licensing/licenses/cli/MIT b/brooklyn-dist/dist/licensing/licenses/server-cli/MIT
similarity index 100%
rename from brooklyn-dist/dist/licensing/licenses/cli/MIT
rename to brooklyn-dist/dist/licensing/licenses/server-cli/MIT
diff --git a/brooklyn-dist/dist/licensing/projects-with-custom-licenses b/brooklyn-dist/dist/licensing/projects-with-custom-licenses
index ebf210c..97839fc 100644
--- a/brooklyn-dist/dist/licensing/projects-with-custom-licenses
+++ b/brooklyn-dist/dist/licensing/projects-with-custom-licenses
@@ -1,2 +1,2 @@
-../jsgui
-../cli
+../../brooklyn-ui
+../../brooklyn-server/server-cli
diff --git a/brooklyn-dist/dist/src/main/license/files/LICENSE b/brooklyn-dist/dist/src/main/license/files/LICENSE
index b280a7d..39db9db 100644
--- a/brooklyn-dist/dist/src/main/license/files/LICENSE
+++ b/brooklyn-dist/dist/src/main/license/files/LICENSE
@@ -232,21 +232,33 @@
Used under the following license: Eclipse Public License, version 1.0 (http://www.eclipse.org/legal/epl-v10.html)
This project includes the software: com.fasterxml.jackson.core
- Available at: http://wiki.fasterxml.com/JacksonHome
+ Available at: http://github.com/FasterXML/jackson https://github.com/FasterXML/jackson
Developed by: FasterXML (http://fasterxml.com/)
- Version used: 2.4.2; 2.4.0
+ Version used: 2.4.5
+ Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+This project includes the software: com.fasterxml.jackson.dataformat
+ Available at: https://github.com/FasterXML/jackson http://wiki.fasterxml.com/JacksonExtensionXmlDataBinding
+ Developed by: FasterXML (http://fasterxml.com/)
+ Version used: 2.4.5
+ Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+This project includes the software: com.fasterxml.jackson.datatype
+ Available at: http://wiki.fasterxml.com/JacksonModuleJoda
+ Developed by: FasterXML (http://fasterxml.com/)
+ Version used: 2.4.5
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
This project includes the software: com.fasterxml.jackson.jaxrs
Available at: http://wiki.fasterxml.com/JacksonHome
Developed by: FasterXML (http://fasterxml.com/)
- Version used: 2.4.2
+ Version used: 2.4.5
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
This project includes the software: com.fasterxml.jackson.module
Available at: http://wiki.fasterxml.com/JacksonJAXBAnnotations
Developed by: FasterXML (http://fasterxml.com/)
- Version used: 2.4.2
+ Version used: 2.4.5
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
This project includes the software: com.google.code.findbugs
@@ -329,13 +341,13 @@
This project includes the software: com.sun.jersey
Available at: https://jersey.java.net/
Developed by: Oracle Corporation (http://www.oracle.com/)
- Version used: 1.18.1
+ Version used: 1.19
Used under the following license: Common Development and Distribution License, version 1.1 (http://glassfish.java.net/public/CDDL+GPL_1_1.html)
This project includes the software: com.sun.jersey.contribs
Available at: https://jersey.java.net/
Developed by: Oracle Corporation (http://www.oracle.com/)
- Version used: 1.18.1
+ Version used: 1.19
Used under the following license: Common Development and Distribution License, version 1.1 (http://glassfish.java.net/public/CDDL+GPL_1_1.html)
This project includes the software: com.thoughtworks.xstream
@@ -344,11 +356,6 @@
Version used: 1.4.7
Used under the following license: BSD License (http://xstream.codehaus.org/license.html)
-This project includes the software: com.wordnik
- Available at: https://github.com/wordnik/swagger-core
- Version used: 1.0.1
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
-
This project includes the software: commons-beanutils
Available at: http://commons.apache.org/proper/commons-beanutils/
Developed by: The Apache Software Foundation (http://www.apache.org/)
@@ -409,6 +416,11 @@
Version used: 0.1.0
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+This project includes the software: io.swagger
+ Available at: https://github.com/swagger-api/swagger-core
+ Version used: 1.5.3
+ Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
+
This project includes the software: javax.annotation
Available at: https://jcp.org/en/jsr/detail?id=250
Version used: 1.0
@@ -426,8 +438,9 @@
Used under the following license: CDDL + GPLv2 with classpath exception (https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html)
This project includes the software: javax.validation
- Version used: 1.0.0.GA
- Used under the following license: Apache License, version 2.0 (in-project reference: license.txt)
+ Available at: http://beanvalidation.org
+ Version used: 1.1.0.Final
+ Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
This project includes the software: javax.ws.rs
Available at: https://jsr311.java.net/
@@ -435,6 +448,12 @@
Version used: 1.1.1
Used under the following license: CDDL License (http://www.opensource.org/licenses/cddl1.php)
+This project includes the software: joda-time
+ Available at: http://joda-time.sourceforge.net
+ Developed by: Joda.org (http://www.joda.org)
+ Version used: 2.2
+ Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
This project includes the software: jQuery JavaScript Library
Available at: http://jquery.com/
Developed by: The jQuery Foundation (http://jquery.org/)
@@ -495,6 +514,13 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Vitaly Puzrin (2011-2015)
+This project includes the software: marked.js
+ Available at: https://github.com/chjj/marked
+ Developed by: Christopher Jeffrey (https://github.com/chjj)
+ Version used: 0.3.1
+ Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+ Copyright (c) Christopher Jeffrey (2011-2014)
+
This project includes the software: moment.js
Available at: http://momentjs.com
Developed by: Tim Wood (http://momentjs.com)
@@ -532,7 +558,7 @@
This project includes the software: org.apache.commons
Available at: http://commons.apache.org/
Developed by: The Apache Software Foundation (http://www.apache.org/)
- Version used: 3.1; 1.4
+ Version used: 3.3.2; 1.4
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
This project includes the software: org.apache.felix
@@ -604,12 +630,24 @@
Version used: 1.1
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+This project includes the software: org.codehaus.woodstox
+ Available at: http://wiki.fasterxml.com/WoodstoxStax2
+ Developed by: fasterxml.com (http://fasterxml.com)
+ Version used: 3.1.4
+ Used under the following license: BSD License (http://www.opensource.org/licenses/bsd-license.php)
+
This project includes the software: org.eclipse.jetty
Available at: http://www.eclipse.org/jetty
Developed by: Webtide (http://webtide.com)
Version used: 9.2.13.v20150730
Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+This project includes the software: org.eclipse.jetty.toolchain
+ Available at: http://www.eclipse.org/jetty
+ Developed by: Mort Bay Consulting (http://www.mortbay.com)
+ Version used: 3.1.M0
+ Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
This project includes the software: org.freemarker
Available at: http://freemarker.org/
Version used: 2.3.22
@@ -650,12 +688,6 @@
Version used: 0.9.9-RC1
Used under the following license: WTFPL (http://www.wtfpl.net/)
-This project includes the software: org.scala-lang
- Available at: http://www.scala-lang.org/
- Developed by: LAMP/EPFL (http://lamp.epfl.ch/)
- Version used: 2.9.1-1
- Used under the following license: BSD License (http://www.scala-lang.org/downloads/license.html)
-
This project includes the software: org.slf4j
Available at: http://www.slf4j.org
Developed by: QOS.ch (http://www.qos.ch)
@@ -706,19 +738,11 @@
Arpad Borsos (2012)
Used under the BSD 2-Clause license.
-This project includes the software: Swagger JS
- Available at: https://github.com/wordnik/swagger-js
- Inclusive of: swagger.js
- Version used: 1.0.1
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- Copyright (c) SmartBear Software (2011-2015)
+This project includes the software: swagger
+ Used under the following license: <no license info>
-This project includes the software: Swagger UI
- Available at: https://github.com/wordnik/swagger-ui
- Inclusive of: swagger-ui.js
- Version used: 1.0.1
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- Copyright (c) SmartBear Software (2011-2015)
+This project includes the software: swagger-ui
+ Used under the following license: <no license info>
This project includes the software: typeahead.js
Available at: https://github.com/twitter/typeahead.js
diff --git a/brooklyn-dist/downstream-parent/pom.xml b/brooklyn-dist/downstream-parent/pom.xml
index 6a7d694..f97dba3 100644
--- a/brooklyn-dist/downstream-parent/pom.xml
+++ b/brooklyn-dist/downstream-parent/pom.xml
@@ -57,7 +57,7 @@
<jclouds.groupId>org.apache.jclouds</jclouds.groupId> <!-- JCLOUDS_GROUPID_VERSION -->
<!-- versions should match those used by Brooklyn, to avoid conflicts -->
- <jclouds.version>1.9.1</jclouds.version> <!-- JCLOUDS_VERSION -->
+ <jclouds.version>1.9.2</jclouds.version> <!-- JCLOUDS_VERSION -->
<logback.version>1.0.7</logback.version>
<slf4j.version>1.6.6</slf4j.version> <!-- used for java.util.logging jul-to-slf4j interception -->
<guava.version>17.0</guava.version>
diff --git a/brooklyn-docs/guide/misc/release-notes.md b/brooklyn-docs/guide/misc/release-notes.md
index e67048e..3661ab1 100644
--- a/brooklyn-docs/guide/misc/release-notes.md
+++ b/brooklyn-docs/guide/misc/release-notes.md
@@ -36,6 +36,23 @@
Classes such as HttpFeed that previously returned org.apache.brooklyn.util.core.http.HttpToolResponse in some methods now
return org.apache.brooklyn.util.HttpToolResponse.
+2. **Major:** Locations set in YAML or on a spec are no longer passed to `child.start(...)` by `AbstractApplication`;
+this has no effect in most cases as `SoftwareProcess.start` looks at local and inherited locations, but in ambiguous cases
+it means that locally defined locations are now preferred. Other classes of entities may need to do similar behaviour,
+and it means that calls to `Entity.getLocations()` in some cases will not show parent locations,
+unless discovered and set locally e.g. `start()`. The new method `Entities.getAllInheritedLocations(Entity)`
+can be used to traverse the hierarchy. It also means that when a type in the registry (catalog) includes a location,
+and a caller references it, that location will now take priority over a location defined in a parent.
+Additionally, any locations specified in YAML extending the registered type will now *replace* locations on the referenced type;
+this means in many cases an explicit `locations: []` when extending a type will cause locations to be taken from the
+parent or application root in YAML. Related to this, tags from referencing specs now preceed tags in the referenced types,
+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`.
diff --git a/docs/guide/yaml/test/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml b/brooklyn-docs/guide/yaml/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml
similarity index 100%
rename from docs/guide/yaml/test/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml
rename to brooklyn-docs/guide/yaml/example_yaml/entities/infrastructuredeploymenttestcase-entity.yaml
diff --git a/docs/guide/yaml/test/example_yaml/entities/loopovergroupmembers-entity.yaml b/brooklyn-docs/guide/yaml/example_yaml/entities/loopovergroupmembers-entity.yaml
similarity index 100%
rename from docs/guide/yaml/test/example_yaml/entities/loopovergroupmembers-entity.yaml
rename to brooklyn-docs/guide/yaml/example_yaml/entities/loopovergroupmembers-entity.yaml
diff --git a/brooklyn-docs/guide/yaml/winrm/index.md b/brooklyn-docs/guide/yaml/winrm/index.md
index 959f88c..a787aa0 100644
--- a/brooklyn-docs/guide/yaml/winrm/index.md
+++ b/brooklyn-docs/guide/yaml/winrm/index.md
@@ -250,6 +250,16 @@
install.command: $brooklyn:formatString("c:\\myscript.bat %s", component("db").attributeWhenReady("datastore.url"))
+### Powershell - Using Start-Process
+
+When you are invoking a command from a powershell script with `Start-Process` cmdlet,
+please use the `-Wait` and the `-PassThru` arguments.
+Example `Start-Process C:\mycommand -Wait -PassThru`
+
+Using `-Wait` guarantees that the script process and its children and thus the winrm session won't be terminated until it is finished.
+`-PassThru` Returns a process object for each process that the cmdlet started. By default, this cmdlet does not generate any output.
+See https://technet.microsoft.com/en-us/library/hh849848.aspx
+
### Rebooting
Where a reboot is required as part of the entity setup, this can be configured using
@@ -378,6 +388,12 @@
This could cause the WinRM connection attempts to timeout. The location configuration option
`waitForWinRmAvailable` defaults to `30m` (i.e. 30 minutes). This can be increased if required.
+Incorrectly prepared Windows template can cause the deployment to time-out expecting an interaction by the user.
+You can verify if this is the case by RDP to the deployment which is taking to much time to complete.
+It is recommended to manually deploy a single VM for every newly created Windows template to verify that it can be
+used for unattended installations and it doesn't wait and/or require an input by the user.
+See [Windows template settings for an Unattended Installation](#windows-template-settings-for-an-unattended-installation) under Known Limitations below.
+
### Windows log files
Details of the commands executed, and their results, can be found in the Brooklyn log and in the Brooklyn
@@ -499,3 +515,12 @@
Blueprint authors are strongly encourages to explicitly specific directories for file
uploads and in their Powershell scripts.
+
+### Windows template settings for an Unattended Installation
+
+Windows template needs certain configuration to be applied to prevent windows setup UI from being displayed.
+The default behavior is to display it if there are incorrect or empty settings. Showing Setup UI will prevent the proper
+deployment, because it will expect interaction by the user such as agreeing on the license agreement or some of the setup dialogs.
+
+Detailed instruction how to prepare an Unattended installation are provided at [https://technet.microsoft.com/en-us/library/cc722411%28v=ws.10%29.aspx](https://technet.microsoft.com/en-us/library/cc722411%28v=ws.10%29.aspx).
+
diff --git a/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java b/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
index 6306956..5003d4d 100644
--- a/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
+++ b/brooklyn-library/software/database/src/main/java/org/apache/brooklyn/entity/database/mysql/MySqlNode.java
@@ -75,6 +75,9 @@
@SetFromFlag("socketUid")
StringAttributeSensorAndConfigKey SOCKET_UID = new StringAttributeSensorAndConfigKey(
"mysql.socketUid", "Socket uid, for use in file /tmp/mysql.sock.<uid>.3306 (or randomly generated if not set)", null);
+
+ @SetFromFlag("generalLog")
+ ConfigKey GENERAL_LOG = ConfigKeys.newBooleanConfigKey("mysql.general_log", "Enable general log", false);
/** @deprecated since 0.7.0 use DATASTORE_URL */ @Deprecated
AttributeSensor<String> MYSQL_URL = DATASTORE_URL;
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
index 85f55ab..40b4086 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql.conf
@@ -13,7 +13,15 @@
basedir = ${driver.baseDir}
datadir = ${driver.dataDir}
bind-address = 0.0.0.0
+log_error = ${driver.runDir}/mysql_error_${entity.getId()}.log
+log_warnings = 2
+general_log = ${config["mysql.general_log"]?string('on','off')}
+general_log_file = ${driver.runDir}/mysql_general_${entity.getId()}.log
# skip-networking
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
# Custom configuration options
${driver.mySqlServerOptionsString}
\ No newline at end of file
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
index 791f2da..54a773b 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_master.conf
@@ -15,6 +15,10 @@
bind-address = 0.0.0.0
# skip-networking
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
# Replication config
server-id = 1
binlog-format = mixed
diff --git a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
index 1c69423..b4af02a 100644
--- a/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
+++ b/brooklyn-library/software/database/src/main/resources/org/apache/brooklyn/entity/database/mysql/mysql_slave.conf
@@ -16,6 +16,10 @@
bind-address = 0.0.0.0
# skip-networking
+#Prevent the GRANT statement from automatically creating new user accounts if it would otherwise do so,
+#unless authentication information is specified
+sql_mode = NO_AUTO_CREATE_USER
+
# Replication config
server-id = ${config["mysql.server_id"]}
relay-log = mysql-slave-${config["mysql.server_id"]}-relay
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
index 8f4e7fb..a95adf9 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/kafka/KafkaClusterImpl.java
@@ -28,6 +28,7 @@
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.enricher.stock.Enrichers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -104,11 +105,14 @@
@Override
public void start(Collection<? extends Location> locations) {
if (isLegacyConstruction()) {
+ // TODO should no longer be needed?
init();
}
- if (locations.isEmpty()) locations = getLocations();
+ locations = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locations, this));
+
Iterables.getOnlyElement(locations); // Assert just one
+ // set it; here we don't allow changing locations
addLocations(locations);
List<Entity> childrenToStart = MutableList.<Entity>of(getCluster());
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
index fc4eddf..874bed8 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitBroker.java
@@ -44,14 +44,19 @@
public interface RabbitBroker extends SoftwareProcess, MessageBroker, AmqpServer {
@SetFromFlag("version")
- public static final ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "2.8.7");
+ public static final ConfigKey<String> SUGGESTED_VERSION = ConfigKeys.newConfigKeyWithDefault(SoftwareProcess.SUGGESTED_VERSION, "3.6.0");
@SetFromFlag("downloadUrl")
public static final BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>(
- SoftwareProcess.DOWNLOAD_URL, "http://www.rabbitmq.com/releases/rabbitmq-server/v${version}/rabbitmq-server-generic-unix-${version}.tar.gz");
+ SoftwareProcess.DOWNLOAD_URL, "http://www.rabbitmq.com/releases/rabbitmq-server/v${version}/rabbitmq-server-generic-unix-${version}.tar.xz");
@SetFromFlag("erlangVersion")
- public static final BasicConfigKey<String> ERLANG_VERSION = new BasicConfigKey<String>(String.class, "erlang.version", "Erlang runtime version", "R15B");
+ public static final BasicConfigKey<String> ERLANG_VERSION = new BasicConfigKey<String>(String.class, "erlang.version", "Erlang runtime version", "18.2");
+
+ @SetFromFlag("erlangDebRepoUrl")
+ public static final BasicConfigKey<String> ERLANG_DEB_REPO_URL = new BasicConfigKey<String>(String.class, "erlang.deb.repo.url",
+ "Deb file used to configure an external Erlang repository which provides up to date packages for Ubuntu/Debian",
+ "http://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb");
@SetFromFlag("rabbitmqConfigTemplateUrl")
ConfigKey<String> CONFIG_TEMPLATE_URL = ConfigKeys.newStringConfigKey(
diff --git a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
index c8d0a89..ad04d55 100644
--- a/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
+++ b/brooklyn-library/software/messaging/src/main/java/org/apache/brooklyn/entity/messaging/rabbit/RabbitSshDriver.java
@@ -27,7 +27,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -39,6 +38,7 @@
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.net.Networking;
import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.text.Strings;
/**
* TODO javadoc
@@ -81,40 +81,32 @@
public void install() {
List<String> urls = resolver.getTargets();
String saveAs = resolver.getFilename();
- // Version and architecture are only required for download of epel package on RHEL/Centos systems so pick sensible
- // defaults if unavailable
- String osMajorVersion = getMachine().getOsDetails().getVersion();
- if (Strings.isNullOrEmpty(osMajorVersion)) {
- osMajorVersion = "7";
- } else {
- osMajorVersion = osMajorVersion.indexOf(".") > 0 ? osMajorVersion.substring(0, osMajorVersion.indexOf('.')) : osMajorVersion;
- if (!CENTOS_VERSION_TO_EPEL_VERSION.keySet().contains(osMajorVersion)) {
- osMajorVersion = "7";
- }
- }
- String epelVersion = CENTOS_VERSION_TO_EPEL_VERSION.get(osMajorVersion);
- String osArchitecture = getMachine().getOsDetails().getArch();
- if (Strings.isNullOrEmpty(osArchitecture)) {
- osArchitecture = "x86_64";
- }
+
List<String> commands = ImmutableList.<String>builder()
- // EPEL repository for erlang install required on some Centos distributions
- .add(chainGroup("which yum", sudo("yum -y update ca-certificates"), sudo("rpm -Uvh --replacepkgs " +
- format("http://download.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, osArchitecture, epelVersion))))
- .add(ifExecutableElse0("zypper", chainGroup(
- ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP3 erlang_sles_11")),
- ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_11.4 erlang_suse_11")),
- ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_12.3 erlang_suse_12")),
- ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_13.1 erlang_suse_13")))))
+ // RabbitMQ recommends Erlang 18. Release notes state:
+ // ====
+ // Minimum required Erlang version is R16B03 for plain ("just TCP") connections for all protocols
+ // and 17.5 for TLS ones (18.x is recommended for both).
+ // ====
+ //
+ // The recommended provider for up to date Erlang versions is Erlang Solutions now.
+ // Supported platforms by Erlang Solutions are: CentOS, RHEL, Ubuntu and Debian.
+ //
+ // New Erlang versions for SUSE are provided via the openSUSE repositories
+ //
+ // EPEL 6 provides only packages for Erlang 14, but EPEL 7 - Erlang 16
+ //
+ .add(ifExecutableElse0("apt-get", getAptRepository()))
+ .add(ifExecutableElse0("yum", getYumRepository()))
+ .add(ifExecutableElse0("zypper", getZypperRepository()))
.add(installPackage( // NOTE only 'port' states the version of Erlang used, maybe remove this constraint?
ImmutableMap.of(
- "apt", "erlang-nox erlang-dev",
"port", "erlang@"+getErlangVersion()+"+ssl"),
- "erlang"))
+ "erlang"))
.addAll(commandsToDownloadUrlsAs(urls, saveAs))
.add(installExecutable("tar"))
- .add(format("tar xvzf %s",saveAs))
+ .add(format("tar xvf %s",saveAs))
.build();
newScript(INSTALLING).
@@ -147,9 +139,9 @@
newScript(MutableMap.of("usePidFile", false), LAUNCHING)
.body.append(
"nohup ./sbin/rabbitmq-server > console-out.log 2> console-err.log &",
- "for i in {1..30}\n" +
+ "for i in {1..60}\n" +
"do\n" +
- " grep 'broker running' console-out.log && exit\n" +
+ " grep 'Starting broker... completed' console-out.log && exit\n" +
" sleep 1\n" +
"done",
"echo \"Couldn't determine if rabbitmq-server is running\"",
@@ -166,7 +158,6 @@
).execute();
}
-
public String getPidFile() { return "rabbitmq.pid"; }
@Override
@@ -183,7 +174,6 @@
.execute();
}
-
@Override
public void kill() {
stop(); // TODO No pid file to easily do `kill -9`
@@ -205,4 +195,69 @@
private String getConfigPath() {
return getRunDir() + "/rabbitmq";
}
+
+ private String getYumRepository() {
+ String yumRepoFileName = "erlang_solutions.repo";
+
+ // Version and architecture are only required for download of epel package on RHEL/Centos systems so pick sensible
+ // defaults if unavailable
+ //
+ // EPEL is still required as it is a prerequisite for the packages provided by Erlang Solutions
+
+ String osMajorVersion = getMachine().getOsDetails().getVersion();
+ if (Strings.isBlank(osMajorVersion)) {
+ osMajorVersion = "7";
+ } else {
+ osMajorVersion = osMajorVersion.indexOf(".") > 0 ? osMajorVersion.substring(0, osMajorVersion.indexOf('.')) : osMajorVersion;
+ if (!CENTOS_VERSION_TO_EPEL_VERSION.keySet().contains(osMajorVersion)) {
+ osMajorVersion = "7";
+ }
+ }
+ String epelVersion = CENTOS_VERSION_TO_EPEL_VERSION.get(osMajorVersion);
+ String osArchitecture = getMachine().getOsDetails().getArch();
+ if (Strings.isBlank(osArchitecture)) {
+ osArchitecture = "x86_64";
+ }
+
+ // Erlang Solutions provide separate packages for RHEL and CentOS, but they are hosted in a single repo.
+ // E.g. - http://packages.erlang-solutions.com/rpm/centos/6/x86_64/
+ //
+ // Erlang Solutions created a repo configuration RPM which can NOT be used on RedHat, because it has explicit checks for CentOS.
+ //
+ // Bellow we are creating a repo file that works for CentOS and RedHat
+ return chainGroup(
+ sudo("yum -y update ca-certificates"),
+ sudo("rpm -Uvh --replacepkgs " + format("http://download.fedoraproject.org/pub/epel/%s/%s/epel-release-%s.noarch.rpm", osMajorVersion, osArchitecture, epelVersion)),
+ "( cat << 'EOF_BROOKLYN'\n"
+ + "[erlang-solutions]\n"
+ + "name=Centos / RHEL " + osMajorVersion + " - $basearch - Erlang Solutions\n"
+ + "baseurl=http://packages.erlang-solutions.com/rpm/centos/" + osMajorVersion + "/$basearch\n"
+ + "gpgcheck=0\n"
+ + "gpgkey=http://packages.erlang-solutions.com/debian/erlang_solutions.asc\n"
+ + "enabled=1\n"
+ + "EOF_BROOKLYN\n"
+ + ") > " + yumRepoFileName,
+ sudo(format("mv %s /etc/yum.repos.d/", yumRepoFileName)),
+ sudo(format("chown root:root /etc/yum.repos.d/%s", yumRepoFileName))
+ );
+ }
+
+ private String getAptRepository() {
+ String debFileName = "erlang-repo.deb";
+
+ return chainGroup(
+ INSTALL_WGET,
+ format("wget --quiet -O %s %s", debFileName, entity.getConfig(RabbitBroker.ERLANG_DEB_REPO_URL)),
+ sudo(format("dpkg -i %s", debFileName))
+ );
+ }
+
+ private String getZypperRepository() {
+ return chainGroup(
+ ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/SLE_11_SP3 erlang_sles_11")),
+ ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_11.4 erlang_suse_11")),
+ ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_12.3 erlang_suse_12")),
+ ok(sudo("zypper --non-interactive addrepo http://download.opensuse.org/repositories/devel:/languages:/erlang/openSUSE_13.1 erlang_suse_13")));
+ }
}
+
diff --git a/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config b/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
index b4428f0..c350b65 100644
--- a/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
+++ b/brooklyn-library/software/messaging/src/main/resources/org/apache/brooklyn/entity/messaging/rabbit/rabbitmq.config
@@ -1,5 +1,6 @@
[
<#if entity.enableManagementPlugin>
+ {rabbit, [{loopback_users, []}]},
{rabbitmq_mochiweb, [{listeners, [{mgmt, [{port, ${entity.managementPort?c}}]}]}]}
</#if>
].
\ No newline at end of file
diff --git a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
index 78898bd..8c52746 100644
--- a/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
+++ b/brooklyn-library/software/webapp/src/main/java/org/apache/brooklyn/entity/webapp/ControlledDynamicWebAppClusterImpl.java
@@ -38,6 +38,7 @@
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.entity.trait.StartableMethods;
import org.apache.brooklyn.core.feed.ConfigToAttributes;
+import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.enricher.stock.Enrichers;
import org.apache.brooklyn.entity.group.DynamicGroupImpl;
import org.apache.brooklyn.entity.proxy.LoadBalancer;
@@ -207,7 +208,8 @@
init();
}
- if (locations.isEmpty()) locations = getLocations();
+ locations = Locations.getLocationsCheckingAncestors(locations, this);
+ // store inherited locations
addLocations(locations);
LoadBalancer loadBalancer = getController();
@@ -229,7 +231,8 @@
}
}
- Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", locations)).get();
+ // don't propagate start locations
+ Entities.invokeEffectorList(this, childrenToStart, Startable.START, ImmutableMap.of("locations", MutableList.of())).get();
if (startControllerTask != null) {
startControllerTask.get();
}
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
index 7440221..58cf946 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/entity/EntitySpec.java
@@ -374,6 +374,13 @@
return this;
}
+ /** clears locations defined in the spec */
+ public <V> EntitySpec<T> clearLocations() {
+ checkMutable();
+ locations.clear();
+ return this;
+ }
+
/** adds the supplied locations to the spec */
public <V> EntitySpec<T> locations(Iterable<? extends Location> val) {
checkMutable();
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
index ab046d5..789d282 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/internal/AbstractBrooklynObjectSpec.java
@@ -38,8 +38,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
@@ -81,7 +83,7 @@
@Override
public String toString() {
- return Objects.toStringHelper(this).add("type", getType()).toString();
+ return Objects.toStringHelper(this).add("type", getType()).toString()+"@"+Integer.toHexString(System.identityHashCode(this));
}
protected abstract void checkValidType(Class<? extends T> type);
@@ -95,6 +97,19 @@
catalogItemId = val;
return self();
}
+ // TODO in many places (callers to this method) we prefer a wrapper item ID;
+ // that is right, because the wrapper's defn will refer to the wrapped,
+ // but we might need also to collect the item ID's so that *all* can be searched.
+ // e.g. if R3 references R2 which references R1 any one of these might supply config keys
+ // referencing resources or types in their local bundles.
+ @Beta
+ public SpecT catalogItemIdIfNotNull(String val) {
+ if (val!=null) {
+ catalogItemId = val;
+ }
+ return self();
+ }
+
public SpecT tag(Object tag) {
tags.add(tag);
@@ -103,12 +118,49 @@
/** adds the given tags */
public SpecT tags(Iterable<Object> tagsToAdd) {
+ return tagsAdd(tagsToAdd);
+ }
+ /** adds the given tags */
+ public SpecT tagsAdd(Iterable<Object> tagsToAdd) {
Iterables.addAll(this.tags, tagsToAdd);
return self();
}
+ /** replaces tags with the given */
+ public SpecT tagsReplace(Iterable<Object> tagsToReplace) {
+ this.tags.clear();
+ Iterables.addAll(this.tags, tagsToReplace);
+ return self();
+ }
+ // TODO which semantics are correct? replace has been the behaviour;
+ // add breaks tests and adds unwanted parameters,
+ // but replacing will cause some desired parameters to be lost.
+ // i (AH) think ideally the caller should remove any parameters which
+ // have been defined as config keys, and then add the others;
+ // or actually we should always add, since this is really defining the config keys,
+ // and maybe extend the SpecParameter object to be able to advertise whether
+ // it is a CatalogConfig or merely a config key, maybe introducing displayable, or even priority
+ // (but note part of the reason for CatalogConfig.priority is that java reflection doesn't preserve field order) .
+ // see also comments on the camp SpecParameterResolver.
+ @Beta
public SpecT parameters(List<? extends SpecParameter<?>> parameters) {
- this.parameters = ImmutableList.copyOf(checkNotNull(parameters, "parameters"));
+ return parametersReplace(parameters);
+ }
+ /** adds the given parameters */
+ @Beta
+ public SpecT parametersAdd(List<? extends SpecParameter<?>> parameters) {
+ // parameters follows immutable pattern, unlike the other fields
+ Builder<SpecParameter<?>> result = ImmutableList.<SpecParameter<?>>builder();
+ if (this.parameters!=null)
+ result.addAll(this.parameters);
+ result.addAll( checkNotNull(parameters, "parameters") );
+ this.parameters = result.build();
+ return self();
+ }
+ /** replaces parameters with the given */
+ @Beta
+ public SpecT parametersReplace(List<? extends SpecParameter<?>> parameters) {
+ this.parameters = ImmutableList.copyOf( checkNotNull(parameters, "parameters") );
return self();
}
diff --git a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java
index c8f1c00..42147c5 100644
--- a/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java
+++ b/brooklyn-server/api/src/main/java/org/apache/brooklyn/api/mgmt/Task.java
@@ -81,6 +81,24 @@
public boolean isError();
/**
+ * As {@link Future#isDone()}. In particular if cancelled, this will return true
+ * as soon as it is cancelled. The thread for this task may still be running,
+ * if the cancellation (often an interruption, but may be weaker) has not applied,
+ * and submitted threads may also be running depending on cancellation parameters.
+ * <p>
+ * {@link #get()} is guaranteed to return immediately, throwing in the case of cancellation
+ * prior to completion (and including the case above where a thread may still be running).
+ * <p>
+ * To check whether cancelled threads for this task have completed,
+ * inspect {@link #getEndTimeUtc()}, which is guaranteed to be set when threads complete
+ * if the thread is started (as determinable by whether {@link #getStartTimeUtc()} is set).
+ * (The threads of submitted/child tasks will usually be independent; to determine their
+ * completion requires inspecting the {@link ExecutionManager}.)
+ */
+ @Override
+ public boolean isDone();
+
+ /**
* Causes calling thread to block until the task is started.
*/
public void blockUntilStarted();
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java
index 56a51f4..c24eab1 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/AbstractResource.java
@@ -30,7 +30,6 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
/** Superclass of CAMP resource implementation objects.
* Typically used to hold common state of implementation objects
@@ -91,7 +90,7 @@
return representationSkew;
}
public Map<String, Object> getCustomAttributes() {
- return ImmutableMap.copyOf(customAttributes);
+ return MutableMap.copyOf(customAttributes).asUnmodifiable();
}
// setters
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java
index 792392e..7ad3164 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/pdp/DeploymentPlan.java
@@ -22,13 +22,11 @@
import java.util.List;
import java.util.Map;
+import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.commons.lang3.builder.ToStringBuilder;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
public class DeploymentPlan {
String name;
@@ -107,15 +105,15 @@
}
public List<Artifact> getArtifacts() {
- return ImmutableList.copyOf(artifacts);
+ return MutableList.copyOf(artifacts).asUnmodifiable();
}
public List<Service> getServices() {
- return ImmutableList.copyOf(services);
+ return MutableList.copyOf(services).asUnmodifiable();
}
public Map<String, Object> getCustomAttributes() {
- return ImmutableMap.copyOf(customAttributes);
+ return MutableMap.copyOf(customAttributes).asUnmodifiable();
}
/**
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
index 26822aa..f45d7c6 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationContext.java
@@ -22,9 +22,9 @@
import java.util.Map;
import org.apache.brooklyn.camp.spi.resolve.PlanInterpreter;
+import org.apache.brooklyn.util.collections.MutableMap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
public class PlanInterpretationContext {
@@ -34,7 +34,7 @@
public PlanInterpretationContext(Map<String,?> originalDeploymentPlan, List<PlanInterpreter> interpreters) {
super();
- this.originalDeploymentPlan = ImmutableMap.copyOf(originalDeploymentPlan);
+ this.originalDeploymentPlan = MutableMap.copyOf(originalDeploymentPlan).asUnmodifiable();
this.interpreters = ImmutableList.copyOf(interpreters);
this.allInterpreter = new PlanInterpreter() {
@Override
diff --git a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java
index 6542b8b..2cf3d5d 100644
--- a/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java
+++ b/brooklyn-server/camp/camp-base/src/main/java/org/apache/brooklyn/camp/spi/resolve/interpret/PlanInterpretationNode.java
@@ -27,8 +27,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
/** Helper class for {@link PlanInterpreter} instances, doing the recursive work */
@@ -228,7 +226,7 @@
public void immutable() {
if (!isChanged()) {
- if (!testCollectionImmutable(getNewValue())) {
+ if (!checkCollectionImmutable(getNewValue())) {
// results of Yaml parse are not typically immutable,
// so force them to be changed so result of interpretation is immutable
changed = true;
@@ -242,19 +240,19 @@
}
private void checkImmutable(Object in) {
- if (!testCollectionImmutable(in))
+ if (!checkCollectionImmutable(in))
log.warn("Node original value "+in+" at "+this+" should be immutable");
}
- private static boolean testCollectionImmutable(Object in) {
- if (in instanceof Map) return (in instanceof ImmutableMap);
- if (in instanceof Iterable) return (in instanceof ImmutableList);
+ private static boolean checkCollectionImmutable(Object in) {
+ // not used -- input might now be UnmodifiableMap, as some args might be null,
+ // and UnmodifiableMap is private :( ... (and same for list)
return true;
}
private static Object immutable(Object in) {
- if (in instanceof Map) return ImmutableMap.copyOf((Map<?,?>)in);
- if (in instanceof Iterable) return ImmutableList.copyOf((Iterable<?>)in);
+ if (in instanceof Map) return MutableMap.copyOf((Map<?,?>)in).asUnmodifiable();
+ if (in instanceof Iterable) return MutableList.copyOf((Iterable<?>)in).asUnmodifiable();
return in;
}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
index 5840440..931c2e7 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynAssemblyTemplateInstantiator.java
@@ -89,20 +89,20 @@
// first build the children into an empty shell app
List<EntitySpec<?>> childSpecs = createServiceSpecs(template, platform, loader, encounteredTypeSymbolicNames);
for (EntitySpec<?> childSpec : childSpecs) {
- app.child(childSpec);
+ // children get parsed and unwrapped irrespective of the NEVER_UNWRAP_APPS setting;
+ // we could support a NEVER_UNWRAP_NESTED_ENTITIES item but i don't know if there's a use case
+ app.child(EntityManagementUtils.unwrapEntity(childSpec));
}
- if (shouldUnwrap(template, app)) {
+ if (allowedToUnwrap(template, app)) {
app = EntityManagementUtils.unwrapApplication(app);
}
return app;
}
- private boolean shouldUnwrap(AssemblyTemplate template, EntitySpec<? extends Application> app) {
- if (Boolean.TRUE.equals(TypeCoercions.coerce(template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY), Boolean.class)))
- return false;
- return EntityManagementUtils.canPromoteWrappedApplication(app);
+ private boolean allowedToUnwrap(AssemblyTemplate template, EntitySpec<? extends Application> app) {
+ return !(Boolean.TRUE.equals(TypeCoercions.coerce(template.getCustomAttributes().get(NEVER_UNWRAP_APPS_PROPERTY), Boolean.class)));
}
private List<EntitySpec<?>> buildTemplateServicesAsSpecs(BrooklynClassLoadingContext loader, AssemblyTemplate template, CampPlatform platform, Set<String> encounteredRegisteredTypeIds) {
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
index 8f4db88..fdc2559 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynComponentTemplateResolver.java
@@ -49,6 +49,7 @@
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.mgmt.BrooklynTags;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
import org.apache.brooklyn.core.mgmt.ManagementContextInjectable;
import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
import org.apache.brooklyn.core.resolve.entity.EntitySpecResolver;
@@ -168,6 +169,7 @@
}
throw new IllegalStateException("Unable to create spec for type " + type + ". " + msgDetails);
}
+ spec = EntityManagementUtils.unwrapEntity(spec);
populateSpec(spec, encounteredRegisteredTypeSymbolicNames);
@@ -207,12 +209,13 @@
// encounteredRegisteredTypeIds must contain the items currently being loaded (the dependency chain),
// but not parent items in this type already resolved.
EntitySpec<? extends Entity> childSpec = entityResolver.resolveSpec(encounteredRegisteredTypeIds);
- spec.child(childSpec);
+ spec.child(EntityManagementUtils.unwrapEntity(childSpec));
}
}
- if (source!=null)
+ if (source!=null) {
spec.tag(BrooklynTags.newYamlSpecTag(source));
+ }
if (!Strings.isBlank(name))
spec.displayName(name);
@@ -221,9 +224,13 @@
if (planId != null)
spec.configure(BrooklynCampConstants.PLAN_ID, planId);
- List<Location> childLocations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(attrs.getAllConfig(), true);
- if (childLocations != null)
- spec.locations(childLocations);
+ List<Location> locations = new BrooklynYamlLocationResolver(mgmt).resolveLocations(attrs.getAllConfig(), true);
+ if (locations != null) {
+ // override locations defined in the type if locations are specified here
+ // empty list can be used by caller to clear, so they are inherited
+ spec.clearLocations();
+ spec.locations(locations);
+ }
decorateSpec(spec, encounteredRegisteredTypeIds);
}
@@ -326,7 +333,7 @@
if (input instanceof Map)
return transformSpecialFlags((Map<?, ?>)input);
else if (input instanceof Set<?>)
- return MutableSet.of(transformSpecialFlags((Iterable<?>)input));
+ return MutableSet.copyOf(transformSpecialFlags((Iterable<?>)input));
else if (input instanceof List<?>)
return MutableList.copyOf(transformSpecialFlags((Iterable<?>)input));
else if (input instanceof Iterable<?>)
@@ -365,7 +372,9 @@
@SuppressWarnings("unchecked")
Map<String, Object> resolvedConfig = (Map<String, Object>)transformSpecialFlags(specConfig.getSpecConfiguration());
specConfig.setSpecConfiguration(resolvedConfig);
- return Factory.newInstance(getLoader(), specConfig.getSpecConfiguration()).resolveSpec(encounteredRegisteredTypeIds);
+ EntitySpec<?> entitySpec = Factory.newInstance(getLoader(), specConfig.getSpecConfiguration()).resolveSpec(encounteredRegisteredTypeIds);
+
+ return EntityManagementUtils.unwrapEntity(entitySpec);
}
if (flag instanceof ManagementContextInjectable) {
log.debug("Injecting Brooklyn management context info object: {}", flag);
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
index 6734875..4913cb1 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/BrooklynEntityDecorationResolver.java
@@ -186,6 +186,9 @@
@Override
public void decorate(EntitySpec<?> entitySpec, ConfigBag attrs) {
List<? extends SpecParameter<?>> explicitParams = buildListOfTheseDecorationsFromEntityAttributes(attrs);
+ // TODO see discussion at EntitySpec.parameters;
+ // maybe we should instead inherit always, or
+ // inherit except where it is set as config and then add the new explicit ones
if (!explicitParams.isEmpty()) {
entitySpec.parameters(explicitParams);
}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
index 602baa3..5639945 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/creation/CampResolver.java
@@ -20,7 +20,6 @@
import java.util.Set;
-import com.google.common.collect.Iterables;
import org.apache.brooklyn.api.entity.Application;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
@@ -115,7 +114,7 @@
throw new IllegalStateException("Creating spec from "+item+", got "+spec.getType()+" which is incompatible with expected "+expectedType);
}
- ((AbstractBrooklynObjectSpec<?, ?>)spec).catalogItemId(item.getId());
+ ((AbstractBrooklynObjectSpec<?, ?>)spec).catalogItemIdIfNotNull(item.getId());
if (Strings.isBlank( ((AbstractBrooklynObjectSpec<?, ?>)spec).getDisplayName() ))
((AbstractBrooklynObjectSpec<?, ?>)spec).displayName(item.getDisplayName());
@@ -131,15 +130,16 @@
if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
EntitySpec<? extends Application> appSpec = ((AssemblyTemplateSpecInstantiator)instantiator).createApplicationSpec(at, camp, loader, encounteredTypes);
- if (!isApplication && EntityManagementUtils.canPromoteChildrenInWrappedApplication(appSpec)) {
- EntitySpec<?> childSpec = Iterables.getOnlyElement(appSpec.getChildren());
- EntityManagementUtils.mergeWrapperParentSpecToChildEntity(appSpec, childSpec);
- return childSpec;
- }
+ // above will unwrap but only if it's an Application (and it's permitted);
+ // but it doesn't know whether we need an App or if an Entity is okay
+ if (!isApplication) return EntityManagementUtils.unwrapEntity(appSpec);
+ // if we need an App then definitely *don't* unwrap here because
+ // the instantiator will have done that, and it knows if the plan
+ // specified a wrapped app explicitly (whereas we don't easily know that here!)
return appSpec;
} else {
- throw new IllegalStateException("Unable to instantiate YAML; incompatible instantiator "+instantiator+" for "+at);
+ throw new IllegalStateException("Unable to instantiate YAML; invalid type or parameters in plan:\n"+plan);
}
}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
index a417e32..48a0283 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslDeferredSupplier.java
@@ -115,7 +115,7 @@
try {
if (log.isDebugEnabled())
- log.debug("Queuing task to resolve "+dsl);
+ log.debug("Queuing task to resolve "+dsl+", called by "+Tasks.current());
EntityInternal entity = (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
ExecutionContext exec =
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
index 909564c..4478f2b 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/AbstractYamlTest.java
@@ -20,6 +20,7 @@
import java.io.Reader;
import java.io.StringReader;
+import java.util.Map;
import java.util.Set;
import org.apache.brooklyn.api.catalog.BrooklynCatalog;
@@ -115,11 +116,14 @@
}
protected Entity createAndStartApplication(String input) throws Exception {
+ return createAndStartApplication(input, MutableMap.<String,String>of());
+ }
+ protected Entity createAndStartApplication(String input, Map<String,String> startParameters) throws Exception {
EntitySpec<?> spec =
mgmt().getTypeRegistry().createSpecFromPlan(CampTypePlanTransformer.FORMAT, input, RegisteredTypeLoadingContexts.spec(Application.class), EntitySpec.class);
final Entity app = brooklynMgmt.getEntityManager().createEntity(spec);
// start the app (happens automatically if we use camp to instantiate, but not if we use crate spec approach)
- app.invoke(Startable.START, MutableMap.<String,String>of()).get();
+ app.invoke(Startable.START, startParameters).get();
return app;
}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java
index bb3eeb9..0554917 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptySoftwareProcessYamlTest.java
@@ -23,14 +23,20 @@
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.collections.Jsonya;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.Test;
-import org.apache.brooklyn.location.ssh.SshMachineLocation;
-import org.apache.brooklyn.util.collections.Jsonya;
+
+import com.google.common.collect.Iterables;
@Test
public class EmptySoftwareProcessYamlTest extends AbstractYamlTest {
@@ -97,6 +103,22 @@
Location actualMachine = entityLocationIterator.next();
Assert.assertTrue(actualMachine instanceof SshMachineLocation, "wrong location: "+actualMachine);
// TODO this, below, probably should be 'localhost on entity', see #1377
- Assert.assertEquals(actualMachine.getParent().getDisplayName(), "loopback on app");
+ Assert.assertEquals(actualMachine.getParent().getDisplayName(), "localhost on entity");
+ }
+
+ @Test(groups="Integration")
+ public void testNoSshing() throws Exception {
+ Entity app = createAndStartApplication(
+ "location: byon:(hosts=\"1.2.3.4\")",
+ "services:",
+ "- type: "+EmptySoftwareProcess.class.getName(),
+ " brooklyn.config:",
+ " sshMonitoring.enabled: false",
+ " "+BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION.getName()+": true");
+ waitForApplicationTasks(app);
+
+ EmptySoftwareProcess entity = Iterables.getOnlyElement(Entities.descendants(app, EmptySoftwareProcess.class));
+ EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+ EntityAsserts.assertAttributeEqualsContinually(entity, Attributes.SERVICE_UP, true);
}
}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptyWindowsProcessYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptyWindowsProcessYamlTest.java
new file mode 100644
index 0000000..77043c7
--- /dev/null
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EmptyWindowsProcessYamlTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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 org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.entity.software.base.EmptyWindowsProcess;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+
+@Test
+public class EmptyWindowsProcessYamlTest extends AbstractYamlTest {
+
+ @Test(groups="Integration")
+ public void testNoWinrm() throws Exception {
+ Entity app = createAndStartApplication(
+ "location: byon:(hosts=\"1.2.3.4\",osFamily=windows)",
+ "services:",
+ "- type: "+EmptyWindowsProcess.class.getName(),
+ " brooklyn.config:",
+ " winrmMonitoring.enabled: false");
+ waitForApplicationTasks(app);
+
+ EmptyWindowsProcess entity = Iterables.getOnlyElement(Entities.descendants(app, EmptyWindowsProcess.class));
+ EntityAsserts.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true);
+ EntityAsserts.assertAttributeEqualsContinually(entity, Attributes.SERVICE_UP, true);
+
+ Iterables.find(entity.getLocations(), Predicates.instanceOf(WinRmMachineLocation.class));
+ }
+}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
index 8131208..337c302 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/EntitiesYamlTest.java
@@ -53,6 +53,7 @@
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.core.test.entity.TestEntityImpl;
import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.group.DynamicFabric;
import org.apache.brooklyn.entity.software.base.SameServerEntity;
import org.apache.brooklyn.entity.stock.BasicEntity;
import org.apache.brooklyn.util.collections.MutableMap;
@@ -60,6 +61,8 @@
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Functionals;
import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,6 +70,7 @@
import org.testng.annotations.Test;
import org.testng.collections.Lists;
+import com.google.common.base.Joiner;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -100,7 +104,6 @@
setupAndCheckTestEntityInBasicYamlWith();
}
- @SuppressWarnings("unchecked")
@Test
public void testBrooklynConfig() throws Exception {
Entity testEntity = setupAndCheckTestEntityInBasicYamlWith(
@@ -113,6 +116,17 @@
" - dogs",
" - cats",
" - badgers",
+ " test.confSetPlain: !!set",
+ " ? square",
+ " ? circle",
+ " ? triangle",
+ " test.confMapThing:",
+ " foo: bar",
+ " baz: qux",
+ " test.confListThing:",
+ " - dogs",
+ " - cats",
+ " - badgers",
" test.confSetThing: !!set",
" ? square",
" ? circle",
@@ -120,14 +134,13 @@
" test.confObject: 5");
Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_NAME), "Test Entity Name");
- List<String> list = testEntity.getConfig(TestEntity.CONF_LIST_PLAIN);
- Assert.assertEquals(list, ImmutableList.of("dogs", "cats", "badgers"));
- Map<String, String> map = testEntity.getConfig(TestEntity.CONF_MAP_PLAIN);
- Assert.assertEquals(map, ImmutableMap.of("foo", "bar", "baz", "qux"));
- Set<String> set = (Set<String>)testEntity.getConfig(TestEntity.CONF_SET_THING);
- Assert.assertEquals(set, ImmutableSet.of("square", "circle", "triangle"));
- Object object = testEntity.getConfig(TestEntity.CONF_OBJECT);
- Assert.assertEquals(object, 5);
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_OBJECT), 5);
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_LIST_PLAIN), ImmutableList.of("dogs", "cats", "badgers"));
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_MAP_PLAIN), ImmutableMap.of("foo", "bar", "baz", "qux"));
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_SET_PLAIN), ImmutableSet.of("square", "circle", "triangle"));
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_LIST_THING), ImmutableList.of("dogs", "cats", "badgers"));
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_MAP_THING), ImmutableMap.of("foo", "bar", "baz", "qux"));
+ Assert.assertEquals(testEntity.getConfig(TestEntity.CONF_SET_THING), ImmutableSet.of("square", "circle", "triangle"));
}
@Test
@@ -592,7 +605,7 @@
Assert.assertEquals(app.getChildren().size(), 1);
Entity entity = app.getChildren().iterator().next();
Assert.assertNotNull(entity);
- Assert.assertEquals(entity.getLocations().size(), 1);
+ Assert.assertEquals(entity.getLocations().size(), 0);
}
@Test
@@ -631,7 +644,8 @@
Assert.assertEquals(app.getChildren().size(), 1);
Entity entity = app.getChildren().iterator().next();
Assert.assertNotNull(entity);
- Assert.assertEquals(entity.getLocations().size(), 2);
+ // 2016-01 locations now not set on entity unless explicitly passed to "start"
+ Assert.assertEquals(entity.getLocations().size(), 0);
}
@Test
@@ -666,6 +680,26 @@
Assert.assertEquals(app.getChildren().size(), 1);
Entity entity = app.getChildren().iterator().next();
+ Assert.assertEquals(entity.getLocations().size(), 1);
+ Iterator<Location> entityLocationIterator = entity.getLocations().iterator();
+ Assert.assertEquals(entityLocationIterator.next().getDisplayName(), "localhost name");
+
+ Location appLocation = app.getLocations().iterator().next();
+ Assert.assertEquals(appLocation.getDisplayName(), "byon name");
+ }
+
+ @Test
+ public void testWithEntityLocationsAndStartInLocation() throws Exception {
+ Entity app = createAndStartApplication(Streams.readFully(loadYaml("test-entity-basic-template.yaml",
+ " location: localhost:(name=localhost name)")),
+ // must pass as JSON list because otherwise the comma confuses the list parser
+ MutableMap.of("locations", "[ "+JavaStringEscapes.wrapJavaString(
+ "byon:(hosts=\"1.1.1.1\", name=\"byon name\")")+" ]") );
+ waitForApplicationTasks(app);
+ Assert.assertEquals(app.getLocations().size(), 1);
+ Assert.assertEquals(app.getChildren().size(), 1);
+ Entity entity = app.getChildren().iterator().next();
+
Assert.assertEquals(entity.getLocations().size(), 2);
Iterator<Location> entityLocationIterator = entity.getLocations().iterator();
Assert.assertEquals(entityLocationIterator.next().getDisplayName(), "localhost name");
@@ -694,6 +728,48 @@
}
@Test
+ public void testCreateFabricWithLocationsAtTopLevel() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "services:",
+ "- type: org.apache.brooklyn.entity.group.DynamicFabric",
+ " memberSpec:",
+ " $brooklyn:entitySpec:",
+ " type: org.apache.brooklyn.core.test.entity.TestEntity",
+ "locations:",
+ "- byon(hosts=\"1.1.1.1\")",
+ "- byon(hosts=\"1.1.1.2\")"
+ );
+
+ Entity app = createAndStartApplication(yaml);
+ waitForApplicationTasks(app);
+ DynamicFabric fabric = Iterables.getOnlyElement(Entities.descendants(app, DynamicFabric.class));
+ Iterable<TestEntity> members = Entities.descendants(fabric, TestEntity.class);
+
+ assertEquals(Iterables.size(members), 2);
+ }
+
+ @Test
+ public void testCreateFabricWithLocationsInline() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "services:",
+ "- type: org.apache.brooklyn.entity.group.DynamicFabric",
+ " memberSpec:",
+ " $brooklyn:entitySpec:",
+ " type: org.apache.brooklyn.core.test.entity.TestEntity",
+ " locations:",
+ " - byon(hosts=\"1.1.1.1\")",
+ " - byon(hosts=\"1.1.1.2\")"
+ );
+
+ Entity app = createAndStartApplication(yaml);
+ waitForApplicationTasks(app);
+ DynamicFabric fabric = Iterables.getOnlyElement(Entities.descendants(app, DynamicFabric.class));
+ Iterable<TestEntity> members = Entities.descendants(fabric, TestEntity.class);
+
+ assertEquals(Iterables.size(members), 2);
+ }
+
+ @Test
public void testEntitySpecConfig() throws Exception {
String yaml =
"services:\n"+
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
index 210f158..66d3cfe 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigYamlTest.java
@@ -24,7 +24,9 @@
import java.io.StringReader;
import java.util.Map;
-import com.google.common.collect.Iterables;
+import org.apache.brooklyn.api.catalog.CatalogItem;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle;
+import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.mgmt.ExecutionContext;
import org.apache.brooklyn.api.mgmt.ManagementContext;
@@ -33,7 +35,6 @@
import org.apache.brooklyn.core.config.external.AbstractExternalConfigSupplier;
import org.apache.brooklyn.core.config.external.ExternalConfigSupplier;
import org.apache.brooklyn.core.internal.BrooklynProperties;
-import org.apache.brooklyn.core.location.AbstractLocation;
import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext;
@@ -48,10 +49,17 @@
import org.testng.annotations.Test;
import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
@Test
public class ExternalConfigYamlTest extends AbstractYamlTest {
private static final Logger log = LoggerFactory.getLogger(ExternalConfigYamlTest.class);
+
+ // Choose a small jar; it is downloaded in some tests.
+ // Pick an OSGi bundle that is not part of core brooklyn.
+ private static final String LIBRARY_URL = "https://repository.apache.org/content/groups/public/org/apache/logging/log4j/log4j-api/2.5/log4j-api-2.5.jar";
+ private static final String LIBRARY_SYMBOLIC_NAME = "org.apache.logging.log4j.api";
+ private static final String LIBRARY_VERSION = "2.5.0";
@Override
protected LocalManagementContext newTestManagementContext() {
@@ -60,8 +68,19 @@
props.put("brooklyn.external.myprovider.mykey", "myval");
props.put("brooklyn.external.myproviderWithoutMapArg", MyExternalConfigSupplierWithoutMapArg.class.getName());
+ props.put("brooklyn.external.myprovider.myCatalogId", "myId");
+ props.put("brooklyn.external.myprovider.myCatalogItemType", "template");
+ props.put("brooklyn.external.myprovider.myCatalogVersion", "1.2");
+ props.put("brooklyn.external.myprovider.myCatalogDescription", "myDescription");
+ props.put("brooklyn.external.myprovider.myCatalogDisplayName", "myDisplayName");
+ props.put("brooklyn.external.myprovider.myCatalogIconUrl", "classpath:///myIconUrl.png");
+ props.put("brooklyn.external.myprovider.myCatalogLibraryUrl", LIBRARY_URL);
+ props.put("brooklyn.external.myprovider.myCatalogLibraryName", LIBRARY_SYMBOLIC_NAME);
+ props.put("brooklyn.external.myprovider.myCatalogLibraryVersion", LIBRARY_VERSION);
+
return LocalManagementContextForTests.builder(true)
.useProperties(props)
+ .disableOsgi(false)
.build();
}
@@ -108,6 +127,97 @@
assertEquals(Iterables.getOnlyElement( app.getLocations() ).config().get(MY_CONFIG_KEY), "myval");
}
+ // Will download the given catalog library jar
+ @Test(groups="Integration")
+ public void testExternalisedCatalogConfigReferencedFromYaml() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "brooklyn.catalog:",
+ " id: $brooklyn:external(\"myprovider\", \"myCatalogId\")",
+ " itemType: $brooklyn:external(\"myprovider\", \"myCatalogItemType\")",
+ " version: $brooklyn:external(\"myprovider\", \"myCatalogVersion\")",
+ " description: $brooklyn:external(\"myprovider\", \"myCatalogDescription\")",
+ " displayName: $brooklyn:external(\"myprovider\", \"myCatalogDisplayName\")",
+ " iconUrl: $brooklyn:external(\"myprovider\", \"myCatalogIconUrl\")",
+ " brooklyn.libraries:",
+ " - $brooklyn:external(\"myprovider\", \"myCatalogLibraryUrl\")",
+ "",
+ " item:",
+ " services:",
+ " - type: brooklyn.entity.database.mysql.MySqlNode");
+
+ catalog.addItems(yaml);
+
+ CatalogItem<Object, Object> item = Iterables.getOnlyElement(catalog.getCatalogItems());
+ CatalogBundle bundle = Iterables.getOnlyElement(item.getLibraries());
+ assertEquals(item.getId(), "myId:1.2");
+ assertEquals(item.getCatalogItemType(), CatalogItemType.TEMPLATE);
+ assertEquals(item.getVersion(), "1.2");
+ assertEquals(item.getDescription(), "myDescription");
+ assertEquals(item.getDisplayName(), "myDisplayName");
+ assertEquals(item.getIconUrl(), "classpath:///myIconUrl.png");
+ assertEquals(bundle.getUrl(), LIBRARY_URL);
+ }
+
+ // Will download the given catalog library jar
+ @Test(groups="Integration")
+ public void testExternalisedCatalogConfigReferencedFromYamlWithLibraryMap() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "brooklyn.catalog:",
+ " id: myid",
+ " itemType: template",
+ " version: 1.2",
+ " description: myDescription",
+ " displayName: myDisplayName",
+ " iconUrl: classpath:///myIconUrl.png",
+ " brooklyn.libraries:",
+ " - name: $brooklyn:external(\"myprovider\", \"myCatalogLibraryName\")",
+ " version: $brooklyn:external(\"myprovider\", \"myCatalogLibraryVersion\")",
+ " url: $brooklyn:external(\"myprovider\", \"myCatalogLibraryUrl\")",
+ "",
+ " item:",
+ " services:",
+ " - type: brooklyn.entity.database.mysql.MySqlNode");
+
+ catalog.addItems(yaml);
+
+ CatalogItem<Object, Object> item = Iterables.getOnlyElement(catalog.getCatalogItems());
+ CatalogBundle bundle = Iterables.getOnlyElement(item.getLibraries());
+ assertEquals(bundle.getUrl(), LIBRARY_URL);
+ assertEquals(bundle.getSymbolicName(), LIBRARY_SYMBOLIC_NAME);
+ assertEquals(bundle.getVersion(), LIBRARY_VERSION);
+ }
+
+ // Will download the given catalog library jar
+ // Confirms "normal" behaviour, when all values in the catalog are hard-coded rather than using external config.
+ @Test(groups="Integration")
+ public void testNonExternalisedCatalogConfigReferencedFromYaml() throws Exception {
+ String yaml = Joiner.on("\n").join(
+ "brooklyn.catalog:",
+ " id: osgi.test",
+ " itemType: template",
+ " version: 1.3",
+ " description: CentOS 6.6 With GUI - 1.3",
+ " displayName: CentOS 6.6",
+ " iconUrl: classpath:///centos.png",
+ " brooklyn.libraries:",
+ " - " + LIBRARY_URL,
+ "",
+ " item:",
+ " services:",
+ " - type: brooklyn.entity.database.mysql.MySqlNode");
+
+ catalog.addItems(yaml);
+
+ CatalogItem<Object, Object> item = Iterables.getOnlyElement(catalog.getCatalogItems());
+ assertEquals(item.getId(), "osgi.test:1.3");
+ assertEquals(item.getCatalogItemType(), CatalogItemType.TEMPLATE);
+ assertEquals(item.getVersion(), "1.3");
+ assertEquals(item.getDescription(), "CentOS 6.6 With GUI - 1.3");
+ assertEquals(item.getDisplayName(), "CentOS 6.6");
+ assertEquals(item.getIconUrl(), "classpath:///centos.png");
+ assertEquals(Iterables.getOnlyElement(item.getLibraries()).getUrl(), LIBRARY_URL);
+ }
+
@Test(groups="Integration")
public void testExternalisedLocationConfigSetViaProvisioningPropertiesReferencedFromYaml() throws Exception {
String yaml = Joiner.on("\n").join(
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java
index 269ff16..371a477 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/LocationsYamlTest.java
@@ -28,15 +28,16 @@
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.location.MachineLocation;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testng.Assert;
-import org.testng.annotations.Test;
+import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.location.byon.FixedListMachineProvisioningLocation;
import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
import org.apache.brooklyn.location.multi.MultiLocation;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -49,7 +50,7 @@
String yaml =
"location: localhost\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -61,7 +62,7 @@
String yaml =
"location: localhost:(name=myname)\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -74,7 +75,7 @@
"location:\n"+
" localhost\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -88,7 +89,7 @@
"- localhost:(name=loc1)\n"+
"- localhost:(name=loc2)\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
List<Location> locs = ImmutableList.copyOf(app.getLocations());
@@ -107,7 +108,7 @@
" displayName: myname\n"+
" myconfkey: myconfval\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(app.getLocations());
@@ -126,7 +127,7 @@
" displayName: myname2\n"+
" myconfkey: myconfval2\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
List<Location> locs = ImmutableList.copyOf(app.getLocations());
@@ -145,7 +146,7 @@
String yaml =
"location: \n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
assertTrue(app.getLocations().isEmpty(), "locs="+app.getLocations());
@@ -158,7 +159,7 @@
"locations:\n"+
"- localhost\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
try {
createStartWaitAndLogApplication(new StringReader(yaml));
@@ -174,7 +175,7 @@
"location:\n"+
"- localhost\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
try {
createStartWaitAndLogApplication(new StringReader(yaml));
@@ -189,11 +190,11 @@
"locations:\n"+
"- localhost:(name=loc1)\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
Entity child = Iterables.getOnlyElement(app.getChildren());
- LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(child.getLocations());
+ LocalhostMachineProvisioningLocation loc = (LocalhostMachineProvisioningLocation) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
assertEquals(loc.getDisplayName(), "loc1");
}
@@ -208,11 +209,11 @@
" - 127.0.0.1\n"+
" - brooklyn@127.0.0.2\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
Entity child = Iterables.getOnlyElement(app.getChildren());
- FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(child.getLocations());
+ FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
Assert.assertEquals(loc.getChildren().size(), 2);
SshMachineLocation l1 = (SshMachineLocation)loc.obtain();
@@ -229,11 +230,11 @@
" user: root\n"+
" hosts: \"{127.0.{0,127}.{1-2},brooklyn@127.0.0.127}\"\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
Entity child = Iterables.getOnlyElement(app.getChildren());
- FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(child.getLocations());
+ FixedListMachineProvisioningLocation<?> loc = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
Assert.assertEquals(loc.getChildren().size(), 5);
assertUserAddress((SshMachineLocation)loc.obtain(), "root", "127.0.0.1");
@@ -257,11 +258,11 @@
" hosts:\n"+
" - 127.0.0.127\n"+
"services:\n"+
- "- serviceType: org.apache.brooklyn.core.test.entity.TestEntity\n";
+ "- type: org.apache.brooklyn.core.test.entity.TestEntity\n";
Entity app = createStartWaitAndLogApplication(new StringReader(yaml));
Entity child = Iterables.getOnlyElement(app.getChildren());
- MultiLocation<?> loc = (MultiLocation<?>) Iterables.getOnlyElement(child.getLocations());
+ MultiLocation<?> loc = (MultiLocation<?>) Iterables.getOnlyElement(Entities.getAllInheritedLocations(child));
Assert.assertEquals(loc.getSubLocations().size(), 2);
assertUserAddress((SshMachineLocation)loc.obtain(), "root", "127.0.0.1");
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
index 47925e5..f792d65 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlLocationTest.java
@@ -32,6 +32,7 @@
import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
import org.apache.brooklyn.core.config.BasicConfigKey;
+import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
import org.apache.brooklyn.core.typereg.RegisteredTypes;
@@ -187,7 +188,7 @@
" - type: org.apache.brooklyn.entity.stock.BasicStartable");
Entity simpleEntity = Iterables.getOnlyElement(app.getChildren());
- Location location = Iterables.getOnlyElement(simpleEntity.getLocations());
+ Location location = Iterables.getOnlyElement(Entities.getAllInheritedLocations(simpleEntity));
assertEquals(location.getClass().getName(), locType);
assertEquals(location.getConfig(new BasicConfigKey<String>(String.class, "config1")), "config1");
assertEquals(location.getConfig(new BasicConfigKey<String>(String.class, "config2")), "config2 override");
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
index c3dbc48..d83711c 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java
@@ -18,18 +18,37 @@
*/
package org.apache.brooklyn.camp.brooklyn.catalog;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTags.NamedStringTag;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest;
+import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
import org.apache.brooklyn.core.typereg.RegisteredTypes;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.test.support.TestResourceUnavailableException;
+import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.osgi.OsgiTestResources;
import org.testng.Assert;
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.Test;
+import com.google.common.collect.Iterables;
+
public class CatalogYamlTemplateTest extends AbstractYamlTest {
@@ -63,6 +82,174 @@
deleteCatalogEntity("t1");
}
+ public void testServiceTypeEntityOfTypeCatalogTemplateNotWrapped() throws Exception {
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " id: t1",
+ " item_type: template",
+ " name: myT1",
+ " item:",
+ " services:",
+ " - type: " + TestEntity.class.getName());
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " id: t2",
+ " item_type: template",
+ " name: myT2",
+ " item:",
+ " services:",
+ " - type: t1",
+ " - type: t1");
+
+ Entity app = createAndStartApplication(
+ "services:",
+ "- type: t2");
+ waitForApplicationTasks(app);
+
+ Entities.dumpInfo(app);
+ Entity t1a = Iterables.get(app.getChildren(), 0);
+ Entity t1b = Iterables.get(app.getChildren(), 1);
+ assertEquals(app.getChildren().size(), 2);
+ assertEquals(t1a.getChildren().size(), 0);
+ assertEquals(t1b.getChildren().size(), 0);
+
+ assertTrue(app instanceof BasicApplication);
+ assertTrue(t1a instanceof TestEntity);
+ assertTrue(t1b instanceof TestEntity);
+ }
+
+ @Test
+ public void testChildEntityOfTypeCatalogTemplateNotWrapped() throws Exception {
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " id: t1",
+ " item_type: template",
+ " name: myT1",
+ " item:",
+ " services:",
+ " - type: " + TestEntity.class.getName());
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " id: t2",
+ " item_type: template",
+ " name: myT2",
+ " item:",
+ " services:",
+ " - type: " + TestEntity.class.getName(),
+ " brooklyn.children:",
+ " - type: t1");
+
+ Entity app = createAndStartApplication(
+ "services:",
+ "- type: t2");
+ waitForApplicationTasks(app);
+
+ Entities.dumpInfo(app);
+ Entity t2 = Iterables.getOnlyElement(app.getChildren());
+ Entity t1 = Iterables.getOnlyElement(t2.getChildren());
+ assertEquals(t1.getChildren().size(), 0);
+
+ assertTrue(app instanceof BasicApplication);
+ assertTrue(t1 instanceof TestEntity);
+ assertTrue(t2 instanceof TestEntity);
+ }
+
+ @Test
+ public void testMemberSpecEntityOfTypeCatalogTemplateNotWrapped() throws Exception {
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " id: t1",
+ " item_type: template",
+ " name: myT1",
+ " item:",
+ " services:",
+ " - type: " + TestEntity.class.getName());
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " id: t2",
+ " item_type: template",
+ " name: myT2",
+ " item:",
+ " services:",
+ " - type: " + DynamicCluster.class.getName(),
+ " brooklyn.config:",
+ " memberSpec:",
+ " $brooklyn:entitySpec:",
+ " type: t1",
+ " cluster.initial.size: 1");
+
+ Entity app = createAndStartApplication(
+ "location: localhost",
+ "services:",
+ "- type: t2");
+ waitForApplicationTasks(app);
+
+ Entities.dumpInfo(app);
+ DynamicCluster t2 = (DynamicCluster) Iterables.getOnlyElement(app.getChildren());
+ Entity t1 = Iterables.getOnlyElement(t2.getMembers());
+ assertEquals(t1.getChildren().size(), 0);
+
+ assertTrue(app instanceof BasicApplication);
+ assertTrue(t2 instanceof DynamicCluster);
+ assertTrue(t1 instanceof TestEntity);
+ }
+
+ @Test
+ public void testMetadataOnSpecCreatedFromItem() throws Exception {
+ makeItem();
+ EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(),
+ "services: [ { type: t1 } ]\n" +
+ "location: localhost");
+
+ List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+ Assert.assertEquals(yamls.size(), 1, "Expected 1 yaml tag; instead had: "+yamls);
+ String yaml = Iterables.getOnlyElement(yamls).getContents();
+ Asserts.assertStringContains(yaml, "services:", "t1", "localhost");
+
+ EntitySpec<?> child = Iterables.getOnlyElement( spec.getChildren() );
+ Assert.assertEquals(child.getType().getName(), SIMPLE_ENTITY_TYPE);
+ Assert.assertEquals(child.getCatalogItemId(), "t1:"+TEST_VERSION);
+ }
+
+ @Test
+ public void testMetadataOnSpecCreatedFromItemReferencingAnApp() throws Exception {
+ // this nested ref to an app caused nested plan contents also to be recorded,
+ // due to how tags are merged. the *first* one is the most important, however,
+ // and ordering of tags should guarantee that.
+ // similarly ensure we get the right outermost non-null catalog item id.
+ addCatalogItems(
+ "brooklyn.catalog:",
+ " version: '1'",
+ " items:",
+ " - id: app1",
+ " name: myApp1",
+ " item:",
+ " type: org.apache.brooklyn.entity.stock.BasicApplication",
+ " brooklyn.config: { foo: bar }",
+ " - id: app1r",
+ " item_type: template",
+ " item:",
+ " services:",
+ " - type: app1",
+ " brooklyn.config:",
+ " foo: boo"
+ );
+
+ EntitySpec<? extends Application> spec = EntityManagementUtils.createEntitySpecForApplication(mgmt(),
+ "services: [ { type: app1r } ]\n" +
+ "location: localhost");
+
+ List<NamedStringTag> yamls = BrooklynTags.findAll(BrooklynTags.YAML_SPEC_KIND, spec.getTags());
+ Assert.assertTrue(yamls.size() >= 1, "Expected at least 1 yaml tag; instead had: "+yamls);
+ String yaml = yamls.iterator().next().getContents();
+ Asserts.assertStringContains(yaml, "services:", "type: app1r", "localhost");
+
+ Assert.assertEquals(spec.getChildren().size(), 0);
+ Assert.assertEquals(spec.getType(), BasicApplication.class);
+ Assert.assertEquals(ConfigBag.newInstance(spec.getConfig()).getStringKey("foo"), "boo");
+ Assert.assertEquals(spec.getCatalogItemId(), "app1r:1");
+ }
+
private RegisteredType makeItem() {
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml b/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml
index b690309..7ace993 100644
--- a/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/resources/test-entity-basic-template.yaml
@@ -19,6 +19,6 @@
description: TestEntity with templated brooklyn.config and additional config (such as services)
origin: https://github.com/apache/incubator-brooklyn
services:
-- serviceType: org.apache.brooklyn.core.test.entity.TestEntity
+- type: org.apache.brooklyn.core.test.entity.TestEntity
name: testentity
# should have nothing below here as the test appends things underneath
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index 96cf452..41a0a9c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -41,12 +41,14 @@
import org.apache.brooklyn.core.catalog.CatalogPredicates;
import org.apache.brooklyn.core.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
import org.apache.brooklyn.core.location.BasicLocationRegistry;
+import org.apache.brooklyn.core.mgmt.internal.CampYamlParser;
import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
import org.apache.brooklyn.core.typereg.BrooklynTypePlanTransformer;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.javalang.AggregateClassLoader;
@@ -395,14 +397,39 @@
if (sourceYaml==null) sourceYaml = new Yaml().dump(itemMetadata);
- Map<Object,Object> catalogMetadata = MutableMap.builder().putAll(parentMetadata).putAll(itemMetadata).build();
+ Map<?, ?> itemMetadataWithoutItemDef = MutableMap.builder()
+ .putAll(itemMetadata)
+ .remove("item")
+ .remove("items")
+ .build();
+ // Parse CAMP-YAML DSL in item metadata (but not in item or items - those will be parsed only when used).
+ CampYamlParser parser = mgmt.getConfig().getConfig(CampYamlParser.YAML_PARSER_KEY);
+ if (parser != null) {
+ itemMetadataWithoutItemDef = parser.parse((Map<String, Object>) itemMetadataWithoutItemDef);
+ try {
+ itemMetadataWithoutItemDef = (Map<String, Object>) Tasks.resolveDeepValue(itemMetadataWithoutItemDef, Object.class, mgmt.getServerExecutionContext());
+ } catch (Exception e) {
+ throw Exceptions.propagate(e);
+ }
+
+ } else {
+ log.info("No Camp-YAML parser regsitered for parsing catalog item DSL; skipping DSL-parsing");
+ }
+
+ Map<Object,Object> catalogMetadata = MutableMap.<Object, Object>builder()
+ .putAll(parentMetadata)
+ .putAll(itemMetadataWithoutItemDef)
+ .putIfNotNull("item", itemMetadata.get("item"))
+ .putIfNotNull("items", itemMetadata.get("items"))
+ .build();
+
// brooklyn.libraries we treat specially, to append the list, with the child's list preferred in classloading order
// `libraries` is supported in some places as a legacy syntax; it should always be `brooklyn.libraries` for new apps
// TODO in 0.8.0 require brooklyn.libraries, don't allow "libraries" on its own
- List<?> librariesNew = MutableList.copyOf(getFirstAs(itemMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
+ List<?> librariesNew = MutableList.copyOf(getFirstAs(itemMetadataWithoutItemDef, List.class, "brooklyn.libraries", "libraries").orNull());
Collection<CatalogBundle> libraryBundlesNew = CatalogItemDtoAbstract.parseLibraries(librariesNew);
-
+
List<?> librariesCombined = MutableList.copyOf(librariesNew)
.appendAll(getFirstAs(parentMetadata, List.class, "brooklyn.libraries", "libraries").orNull());
if (!librariesCombined.isEmpty())
@@ -413,7 +440,7 @@
// (this load is required for the scan below and I think also for yaml resolution)
CatalogUtils.installLibraries(mgmt, libraryBundlesNew);
- Boolean scanJavaAnnotations = getFirstAs(itemMetadata, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
+ Boolean scanJavaAnnotations = getFirstAs(itemMetadataWithoutItemDef, Boolean.class, "scanJavaAnnotations", "scan_java_annotations").orNull();
if (scanJavaAnnotations==null || !scanJavaAnnotations) {
// don't scan
} else {
@@ -433,8 +460,8 @@
if (items!=null) {
int count = 0;
for (Map<?,?> i: ((List<Map<?,?>>)items)) {
- collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(),
- i, result, catalogMetadata);
+ collectCatalogItems(Yamls.getTextOfYamlAtPath(sourceYaml, "items", count).getMatchedYamlTextOrWarn(),
+ i, result, catalogMetadata);
count++;
}
}
@@ -463,8 +490,10 @@
PlanInterpreterGuessingType planInterpreter = new PlanInterpreterGuessingType(null, item, sourceYaml, itemType, libraryBundles, result).reconstruct();
if (!planInterpreter.isResolved()) {
throw Exceptions.create("Could not resolve item"
- + (Strings.isNonBlank(id) ? " "+id : Strings.isNonBlank(symbolicName) ? " "+symbolicName : Strings.isNonBlank(name) ? name : "")
- // better not to show yaml, takes up lots of space, and with multiple plan transformers there might be multiple errors
+ + (Strings.isNonBlank(id) ? " '"+id+"'" : Strings.isNonBlank(symbolicName) ? " '"+symbolicName+"'" : Strings.isNonBlank(name) ? " '"+name+"'" : "")
+ // better not to show yaml, takes up lots of space, and with multiple plan transformers there might be multiple errors;
+ // some of the errors themselves may reproduce it
+ // (ideally in future we'll be able to return typed errors with caret position of error)
// + ":\n"+sourceYaml
, planInterpreter.getErrors());
}
@@ -582,12 +611,12 @@
return oldValue;
}
- private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromLocal(ManagementContext mgmt, Map<Object, Object> catalogMetadata) {
+ private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromLocal(ManagementContext mgmt, Map<?, ?> catalogMetadata) {
CatalogDto dto = CatalogDto.newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the classpath", "scanning-local-classpath");
return scanAnnotationsInternal(mgmt, new CatalogDo(dto), catalogMetadata);
}
- private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromBundles(ManagementContext mgmt, Collection<CatalogBundle> libraries, Map<Object, Object> catalogMetadata) {
+ private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsFromBundles(ManagementContext mgmt, Collection<CatalogBundle> libraries, Map<?, ?> catalogMetadata) {
CatalogDto dto = CatalogDto.newNamedInstance("Bundles Scanned Catalog", "All annotated Brooklyn entities detected in bundles", "scanning-bundles-classpath-"+libraries.hashCode());
List<String> urls = MutableList.of();
for (CatalogBundle b: libraries) {
@@ -608,7 +637,7 @@
return scanAnnotationsInternal(mgmt, subCatalog, catalogMetadata);
}
- private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsInternal(ManagementContext mgmt, CatalogDo subCatalog, Map<Object, Object> catalogMetadata) {
+ private Collection<CatalogItemDtoAbstract<?, ?>> scanAnnotationsInternal(ManagementContext mgmt, CatalogDo subCatalog, Map<?, ?> catalogMetadata) {
// TODO this does java-scanning only;
// the call when scanning bundles should use the CatalogItem instead and use OSGi when loading for scanning
// (or another scanning mechanism). see comments on CatalogClasspathDo.load
@@ -989,7 +1018,7 @@
};
}
- private static <T,SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T,SpecT>> itemDoToDtoAddingSelectedMetadataDuringScan(final Map<Object, Object> catalogMetadata) {
+ private static <T,SpecT> Function<CatalogItemDo<T, SpecT>, CatalogItem<T,SpecT>> itemDoToDtoAddingSelectedMetadataDuringScan(final Map<?, ?> catalogMetadata) {
return new Function<CatalogItemDo<T,SpecT>, CatalogItem<T,SpecT>>() {
@Override
public CatalogItem<T,SpecT> apply(@Nullable CatalogItemDo<T,SpecT> item) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
index e000997..3fd4b05 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractApplication.java
@@ -38,6 +38,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.ImmutableSet;
+
/**
* Users can extend this to define the entities in their application, and the relationships between
* those entities. Users should override the {@link #init()} method, and in there should create
@@ -143,7 +145,8 @@
@Override
public void start(Collection<? extends Location> locations) {
this.addLocations(locations);
- Collection<? extends Location> locationsToUse = getLocations();
+ // 2016-01: only pass locations passed to us, as per ML discussion
+ Collection<? extends Location> locationsToUse = locations==null ? ImmutableSet.<Location>of() : locations;
ServiceProblemsLogic.clearProblemsIndicator(this, START);
ServiceStateLogic.ServiceNotUpLogic.updateNotUpIndicator(this, Attributes.SERVICE_STATE_ACTUAL, "Application starting");
setExpectedStateAndRecordLifecycleEvent(Lifecycle.STARTING);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 0599373..5422fb6 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -874,15 +874,18 @@
@Override
public void addLocations(Collection<? extends Location> newLocations) {
+ if (newLocations==null || newLocations.isEmpty()) {
+ return;
+ }
synchronized (locations) {
List<Location> oldLocations = locations.get();
- Set<Location> truelyNewLocations = Sets.newLinkedHashSet(newLocations);
- truelyNewLocations.removeAll(oldLocations);
- if (truelyNewLocations.size() > 0) {
- locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(truelyNewLocations).build());
+ Set<Location> trulyNewLocations = Sets.newLinkedHashSet(newLocations);
+ trulyNewLocations.removeAll(oldLocations);
+ if (trulyNewLocations.size() > 0) {
+ locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(trulyNewLocations).build());
}
- for (Location loc : truelyNewLocations) {
+ for (Location loc : trulyNewLocations) {
sensors().emit(AbstractEntity.LOCATION_ADDED, loc);
}
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
index 59e3ec1..9c8ebc8 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
@@ -80,6 +80,7 @@
import org.apache.brooklyn.core.objs.proxy.EntityProxyImpl;
import org.apache.brooklyn.core.sensor.DependentConfiguration;
import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.core.ResourceUtils;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.core.flags.FlagUtils;
@@ -804,10 +805,15 @@
((ManagementContextInternal)mgmt).terminate();
}
if (error.get() != null) throw Exceptions.propagate(error.get());
- } catch (InterruptedException e) {
- throw Exceptions.propagate(e);
- } catch (ExecutionException e) {
- throw Exceptions.propagate(e);
+ } catch (Exception e) {
+ if (!mgmt.isRunning()) {
+ // we've checked this above so it would only happen if a different thread stopped it;
+ // this does happen sometimes e.g. in CliTest where the server shutdown occurs concurrently
+ log.debug("Destroying apps gave an error, but mgmt context was concurrently stopped so not really a problem; swallowing (unless fatal): "+e);
+ Exceptions.propagateIfFatal(e);
+ } else {
+ throw Exceptions.propagate(e);
+ }
} finally {
executor.shutdownNow();
}
@@ -1183,4 +1189,13 @@
return root;
}
+ public static Set<Location> getAllInheritedLocations(Entity entity) {
+ Set<Location> result = MutableSet.of();
+ while (entity!=null) {
+ result.addAll(entity.getLocations());
+ entity = entity.getParent();
+ }
+ return result;
+ }
+
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
index 4056eca..3602bee 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/EntityInternal.java
@@ -21,6 +21,8 @@
import java.util.Collection;
import java.util.Map;
+import javax.annotation.Nullable;
+
import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityLocal;
@@ -48,7 +50,7 @@
@Beta
public interface EntityInternal extends BrooklynObjectInternal, EntityLocal, Rebindable {
- void addLocations(Collection<? extends Location> locations);
+ void addLocations(@Nullable Collection<? extends Location> locations);
void removeLocations(Collection<? extends Location> locations);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java
index 68c9398..36e6ba8 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/entity/trait/Resizable.java
@@ -31,6 +31,21 @@
*/
public interface Resizable {
+ /**
+ * Indicates that resizing up to the desired size is not possible - only resized to the
+ * {@link Resizable#getCurrentSize()}, because there is insufficient capacity.
+ */
+ public static class InsufficientCapacityException extends RuntimeException {
+ private static final long serialVersionUID = 953230498564942446L;
+
+ public InsufficientCapacityException(String msg) {
+ super(msg);
+ }
+ public InsufficientCapacityException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+ }
+
MethodEffector<Integer> RESIZE = new MethodEffector<Integer>(Resizable.class, "resize");
/**
@@ -38,6 +53,9 @@
*
* @param desiredSize the new size of the entity group.
* @return the new size of the group.
+ *
+ * @throws InsufficientCapacityException If the request was to grow, but there is no capacity to grow to
+ * the desired size.
*/
@Effector(description="Changes the size of the entity (e.g. the number of nodes in a cluster)")
Integer resize(@EffectorParam(name="desiredSize", description="The new size of the cluster") Integer desiredSize);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
index 4954393..60fb061 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
@@ -430,7 +430,9 @@
public List<Location> resolve(Iterable<?> spec) {
List<Location> result = new ArrayList<Location>();
for (Object id : spec) {
- if (id instanceof String) {
+ if (id==null) {
+ // drop a null entry
+ } if (id instanceof String) {
result.add(resolve((String) id));
} else if (id instanceof Location) {
result.add((Location) id);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
index a08eb56..0ddc79a 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTags.java
@@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.apache.brooklyn.util.collections.MutableList;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
@@ -74,7 +75,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- ListTag that = (ListTag) o;
+ ListTag<?> that = (ListTag<?>) o;
return list == null ? that.list == null : list.equals(that.list);
}
@@ -124,5 +125,14 @@
}
return null;
}
-
+
+ public static List<NamedStringTag> findAll(String kind, Iterable<Object> tags) {
+ List<NamedStringTag> result = MutableList.of();
+ for (Object object: tags) {
+ if (object instanceof NamedStringTag && kind.equals(((NamedStringTag)object).kind))
+ result.add( (NamedStringTag) object );
+ }
+ return result;
+ }
+
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
index ef461db..2f37e7c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/EntityManagementUtils.java
@@ -50,7 +50,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.annotations.Beta;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -67,11 +66,12 @@
/**
* A marker config value which indicates that an {@link Application} entity was created automatically,
* needed because a plan might give multiple top-level entities or a non-Application top-level entity,
- * but brooklyn requires an {@link Application} at the root.
+ * in a context where Brooklyn requires an {@link Application} at the root.
* <p>
* Typically when such a wrapper app wraps another {@link Application}
- * (or when we are adding to an existing entity and it wraps multiple {@link Entity} instances)
- * it will be unwrapped. See {@link #newWrapperApp()} and {@link #unwrapApplication(EntitySpec)}.
+ * (or where we are looking for a single {@link Entity}, or a list to add, and they are so wrapped)
+ * it will be unwrapped.
+ * See {@link #newWrapperApp()} and {@link #unwrapApplication(EntitySpec)}.
*/
public static final ConfigKey<Boolean> WRAPPER_APP_MARKER = ConfigKeys.newBooleanConfigKey("brooklyn.wrapper_app");
@@ -159,21 +159,16 @@
// see whether we can promote children
List<EntitySpec<?>> specs = MutableList.of();
- if (canPromoteChildrenInWrappedApplication(specA)) {
- // we can promote
- for (EntitySpec<?> specC: specA.getChildren()) {
- mergeWrapperParentSpecToChildEntity(specA, specC);
- specs.add(specC);
- }
- } else {
+ if (!canUnwrapEntity(specA)) {
// if not promoting, set a nice name if needed
if (Strings.isEmpty(specA.getDisplayName())) {
int size = specA.getChildren().size();
String childrenCountString = size+" "+(size!=1 ? "children" : "child");
specA.displayName("Dynamically added "+childrenCountString);
}
- specs.add(specA);
}
+
+ specs.add(unwrapEntity(specA));
final List<Entity> children = MutableList.of();
for (EntitySpec<?> spec: specs) {
@@ -219,75 +214,111 @@
return CreationResult.of(children, task);
}
-
- /** if an application should be unwrapped, it does so, returning the child; otherwise returns the argument passed in.
- * use {@link #canPromoteWrappedApplication(EntitySpec)} to test whether it will unwrap. */
- public static EntitySpec<? extends Application> unwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
- if (canPromoteWrappedApplication(wrapperApplication)) {
- @SuppressWarnings("unchecked")
- EntitySpec<? extends Application> wrappedApplication = (EntitySpec<? extends Application>) Iterables.getOnlyElement( wrapperApplication.getChildren() );
-
- // if promoted, apply the transformations done to the app
- // (transformations will be done by the resolveSpec call above, but we are collapsing oldApp so transfer to app=newApp)
- EntityManagementUtils.mergeWrapperParentSpecToChildEntity(wrapperApplication, wrappedApplication);
- return wrappedApplication;
+
+ /** Unwraps a single {@link Entity} if appropriate. See {@link #WRAPPER_APP_MARKER}.
+ * Also see {@link #canUnwrapEntity(EntitySpec)} to test whether it will unwrap. */
+ public static EntitySpec<? extends Entity> unwrapEntity(EntitySpec<? extends Entity> wrapperApplication) {
+ if (!canUnwrapEntity(wrapperApplication)) {
+ return wrapperApplication;
}
- return wrapperApplication;
+ EntitySpec<?> wrappedEntity = Iterables.getOnlyElement(wrapperApplication.getChildren());
+ @SuppressWarnings("unchecked")
+ EntitySpec<? extends Application> wrapperApplicationTyped = (EntitySpec<? extends Application>) wrapperApplication;
+ EntityManagementUtils.mergeWrapperParentSpecToChildEntity(wrapperApplicationTyped, wrappedEntity);
+ return wrappedEntity;
+ }
+
+ /** Unwraps a wrapped {@link Application} if appropriate.
+ * This is like {@link #canUnwrapEntity(EntitySpec)} with an additional check that the wrapped child is an {@link Application}.
+ * See {@link #WRAPPER_APP_MARKER} for an overview.
+ * Also see {@link #canUnwrapApplication(EntitySpec)} to test whether it will unwrap. */
+ public static EntitySpec<? extends Application> unwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
+ if (!canUnwrapApplication(wrapperApplication)) {
+ return wrapperApplication;
+ }
+ @SuppressWarnings("unchecked")
+ EntitySpec<? extends Application> wrappedApplication = (EntitySpec<? extends Application>) unwrapEntity(wrapperApplication);
+ return wrappedApplication;
}
/** Modifies the child so it includes the inessential setup of its parent,
* for use when unwrapping specific children, but a name or other item may have been set on the parent.
* See {@link #WRAPPER_APP_MARKER}. */
- @Beta //where should this live long-term?
- public static void mergeWrapperParentSpecToChildEntity(EntitySpec<? extends Application> wrapperParent, EntitySpec<?> wrappedChild) {
- if (Strings.isNonEmpty(wrapperParent.getDisplayName()))
+ private static void mergeWrapperParentSpecToChildEntity(EntitySpec<? extends Application> wrapperParent, EntitySpec<?> wrappedChild) {
+ if (Strings.isNonEmpty(wrapperParent.getDisplayName())) {
wrappedChild.displayName(wrapperParent.getDisplayName());
- if (!wrapperParent.getLocations().isEmpty())
- wrappedChild.locations(wrapperParent.getLocations());
- if (!wrapperParent.getParameters().isEmpty()) {
- wrappedChild.parameters(wrapperParent.getParameters());
}
+
+ wrappedChild.locations(wrapperParent.getLocations());
+
+ if (!wrapperParent.getParameters().isEmpty())
+ wrappedChild.parametersReplace(wrapperParent.getParameters());
- // NB: this clobbers child config; might prefer to deeply merge maps etc
- // (but this should not be surprising, as unwrapping is often parameterising the nested blueprint, so outer config should dominate)
+ // prefer the wrapper ID (change in 2016-01); see notes on the catalogItemIdIfNotNull method
+ wrappedChild.catalogItemIdIfNotNull(wrapperParent.getCatalogItemId());
+
+ // NB: this clobber's child config wherever they conflict; might prefer to deeply merge maps etc
+ // (or maybe even prevent the merge in these cases;
+ // not sure there is a compelling reason to have config on a pure-wrapper parent)
Map<ConfigKey<?>, Object> configWithoutWrapperMarker = Maps.filterKeys(wrapperParent.getConfig(), Predicates.not(Predicates.<ConfigKey<?>>equalTo(EntityManagementUtils.WRAPPER_APP_MARKER)));
wrappedChild.configure(configWithoutWrapperMarker);
wrappedChild.configure(wrapperParent.getFlags());
- // TODO copying tags to all entities is not ideal;
- // in particular the BrooklynTags.YAML_SPEC tag will show all entities if the root has multiple
- wrappedChild.tags(wrapperParent.getTags());
+ // copying tags to all entities may be something the caller wants to control,
+ // e.g. if we're adding multiple, the caller might not want to copy the parent
+ // (the BrooklynTags.YAML_SPEC tag will include the parents source including siblings),
+ // but OTOH they might because otherwise the parent's tags might get lost.
+ // also if we are unwrapping multiple registry references we will get the YAML_SPEC for each;
+ // putting the parent's tags first however causes the preferred (outer) one to be retrieved first.
+ wrappedChild.tagsReplace(MutableList.copyOf(wrapperParent.getTags()).appendAll(wrappedChild.getTags()));
}
public static EntitySpec<? extends Application> newWrapperApp() {
return EntitySpec.create(BasicApplication.class).configure(WRAPPER_APP_MARKER, true);
}
- /** returns true if the spec is for an empty-ish wrapper app contianing an application,
- * for use when adding from a plan specifying an application which was wrapped because it had to be.
+ /** As {@link #canUnwrapEntity(EntitySpec)}
+ * but additionally requiring that the wrapped item is an {@link Application},
+ * for use when the context requires an {@link Application} ie a root of a spec.
* @see #WRAPPER_APP_MARKER */
+ public static boolean canUnwrapApplication(EntitySpec<? extends Application> wrapperApplication) {
+ if (!canUnwrapEntity(wrapperApplication)) return false;
+
+ EntitySpec<?> childSpec = Iterables.getOnlyElement(wrapperApplication.getChildren());
+ return (childSpec.getType()!=null && Application.class.isAssignableFrom(childSpec.getType()));
+ }
+ /** @deprecated since 0.9.0 use {@link #canUnwrapApplication(EntitySpec)} */ @Deprecated
public static boolean canPromoteWrappedApplication(EntitySpec<? extends Application> app) {
- if (!hasSingleChild(app))
- return false;
-
- EntitySpec<?> childSpec = Iterables.getOnlyElement(app.getChildren());
- if (childSpec.getType()==null || !Application.class.isAssignableFrom(childSpec.getType()))
- return false;
-
- return canPromoteChildrenInWrappedApplication(app);
+ return canUnwrapApplication(app);
}
- /** returns true if the spec is for a wrapper app with no important settings, wrapping a single child.
- * for use when adding from a plan specifying multiple entities but nothing significant at the application level.
- * @see #WRAPPER_APP_MARKER */
- public static boolean canPromoteChildrenInWrappedApplication(EntitySpec<? extends Application> spec) {
+ /** Returns true if the spec is for a wrapper app with no important settings, wrapping a single child entity.
+ * for use when adding from a plan specifying multiple entities but there is nothing significant at the application level,
+ * and the context would like to flatten it to remove the wrapper yielding just a single entity.
+ * (but note the result is not necessarily an {@link Application};
+ * see {@link #canUnwrapApplication(EntitySpec)} if that is required).
+ * <p>
+ * Note callers will normally use one of {@link #unwrapEntity(EntitySpec)} or {@link #unwrapApplication(EntitySpec)}.
+ *
+ * @see #WRAPPER_APP_MARKER for an overview */
+ public static boolean canUnwrapEntity(EntitySpec<? extends Entity> spec) {
return isWrapperApp(spec) && hasSingleChild(spec) &&
- //equivalent to no keys starting with "brooklyn."
- spec.getEnrichers().isEmpty() &&
- spec.getEnricherSpecs().isEmpty() &&
- spec.getInitializers().isEmpty() &&
- spec.getPolicies().isEmpty() &&
- spec.getPolicySpecs().isEmpty();
+ // these "brooklyn.*" items on the app rather than the child absolutely prevent unwrapping
+ // as their semantics could well be different whether they are on the parent or the child
+ spec.getEnrichers().isEmpty() &&
+ spec.getEnricherSpecs().isEmpty() &&
+ spec.getInitializers().isEmpty() &&
+ spec.getPolicies().isEmpty() &&
+ spec.getPolicySpecs().isEmpty() &&
+ // these items prevent merge only if they are defined at both levels
+ (spec.getLocations().isEmpty() || Iterables.getOnlyElement(spec.getChildren()).getLocations().isEmpty())
+ // TODO what should we do with parameters? currently clobbers due to EntitySpec.parameters(...) behaviour.
+// && (spec.getParameters().isEmpty() || Iterables.getOnlyElement(spec.getChildren()).getParameters().isEmpty())
+ ;
+ }
+ /** @deprecated since 0.9.0 use {@link #canUnwrapEntity(EntitySpec)} */ @Deprecated
+ public static boolean canPromoteChildrenInWrappedApplication(EntitySpec<? extends Application> spec) {
+ return canUnwrapEntity(spec);
}
public static boolean isWrapperApp(EntitySpec<?> spec) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
index d41e059..da9fcae 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractManagementContext.java
@@ -372,10 +372,15 @@
return configMap;
}
+ private final Object locationRegistrySemaphore = new Object();
+
@Override
- public synchronized LocationRegistry getLocationRegistry() {
- if (locationRegistry==null) locationRegistry = new BasicLocationRegistry(this);
- return locationRegistry;
+ public LocationRegistry getLocationRegistry() {
+ // NB: can deadlock if synched on whole LMC
+ synchronized (locationRegistrySemaphore) {
+ if (locationRegistry==null) locationRegistry = new BasicLocationRegistry(this);
+ return locationRegistry;
+ }
}
@Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java
index 677a4f6..91ca5dc 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/BrooklynShutdownHooks.java
@@ -187,7 +187,9 @@
}
entitiesToStop.addAll(entitiesToStopOnShutdown);
for (ManagementContext mgmt: managementContextsToStopAppsOnShutdown) {
- entitiesToStop.addAll(mgmt.getApplications());
+ if (mgmt.isRunning()) {
+ entitiesToStop.addAll(mgmt.getApplications());
+ }
}
if (entitiesToStop.isEmpty()) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java
index c6ec3ac..f8bb7cb 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/EffectorUtils.java
@@ -149,6 +149,9 @@
}
if (newArgsNeeded>0)
throw new IllegalArgumentException("Invalid arguments (missing "+newArgsNeeded+") for effector "+eff+": "+m);
+ if (!m.isEmpty()) {
+ log.warn("Unsupported parameter to "+eff+" (ignoring): "+m);
+ }
return newArgs.toArray(new Object[newArgs.size()]);
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
index d88a500..76500bc 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalManagementContext.java
@@ -30,6 +30,7 @@
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.api.entity.Application;
@@ -116,6 +117,7 @@
return closed;
}
+ private AtomicBoolean terminated = new AtomicBoolean(false);
private String managementPlaneId;
private String managementNodeId;
private BasicExecutionManager execution;
@@ -316,15 +318,26 @@
@Override
public void terminate() {
- INSTANCES.remove(this);
- super.terminate();
- if (osgiManager!=null) {
- osgiManager.stop();
- osgiManager = null;
+ synchronized (terminated) {
+ if (terminated.getAndSet(true)) {
+ log.trace("Already terminated management context "+this);
+ // no harm in doing it twice, but it makes logs ugly!
+ return;
+ }
+ log.debug("Terminating management context "+this);
+
+ INSTANCES.remove(this);
+ super.terminate();
+ if (osgiManager!=null) {
+ osgiManager.stop();
+ osgiManager = null;
+ }
+ if (usageManager != null) usageManager.terminate();
+ if (execution != null) execution.shutdownNow();
+ if (gc != null) gc.shutdownNow();
+
+ log.debug("Terminated management context "+this);
}
- if (usageManager != null) usageManager.terminate();
- if (execution != null) execution.shutdownNow();
- if (gc != null) gc.shutdownNow();
}
@Override
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 3b0795b..eb4ff10 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
@@ -55,6 +55,7 @@
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.javalang.AggregateClassLoader;
+import org.apache.brooklyn.util.javalang.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -99,6 +100,7 @@
interfaces.add(spec.getType());
} else {
log.warn("EntitySpec declared in terms of concrete type "+spec.getType()+"; should be supplied in terms of interface");
+ interfaces.addAll(Reflections.getAllInterfaces(spec.getType()));
}
interfaces.addAll(spec.getAdditionalInterfaces());
@@ -305,12 +307,16 @@
/* Marked transient so that the task is not needlessly kept around at the highest level.
* Note that the task is not normally visible in the GUI, because
- * (a) while it is running, the entity is parentless (and so not in the tree);
+ * (a) while it is running, the entity is often parentless (and so not in the tree);
* and (b) when it is completed it is GC'd, as it is transient.
* However task info is available via the API if you know its ID,
* and if better subtask querying is available it will be picked up as a background task
* of the parent entity creating this child entity
* (note however such subtasks are currently filtered based on parent entity so is excluded).
+ * <p>
+ * Some of these (initializers and enrichers) submit background scheduled tasks,
+ * which currently show up at the top level once the initializer task completes.
+ * TODO It would be nice if these schedule tasks were grouped in a bucket!
*/
((EntityInternal)entity).getExecutionContext().submit(Tasks.builder().dynamic(false).displayName("Entity initialization")
.tag(BrooklynTaskTags.tagForContextEntity(entity))
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
index ac4bef5..0c622c3 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/DependentConfiguration.java
@@ -248,7 +248,7 @@
// return immediately if either the ready predicate or the abort conditions hold
if (ready(value)) return postProcess(value);
-
+
final List<Exception> abortionExceptions = Lists.newCopyOnWriteArrayList();
long start = System.currentTimeMillis();
@@ -790,6 +790,7 @@
.displayName("waiting on "+sensor.getName())
.description("Waiting on sensor "+sensor.getName()+" from "+source)
.tag("attributeWhenReady")
+ .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
.body(new WaitInTaskForAttributeReady<T,V>(this))
.build();
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
index 8f671f2..5ba36b3 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/AbstractTypePlanTransformer.java
@@ -103,7 +103,8 @@
@Override protected Object visitSpec() {
try {
AbstractBrooklynObjectSpec<?, ?> result = createSpec(type, context);
- result.catalogItemId(type.getId());
+ // see notes on catalogItemIdIfNotNull
+ result.catalogItemIdIfNotNull(type.getId());
return result;
} catch (Exception e) { throw Exceptions.propagate(e); }
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
index 05f0773..e0e2305 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/typereg/BasicRegisteredType.java
@@ -64,6 +64,7 @@
@Override
public String getId() {
+ if (symbolicName==null) return null;
return symbolicName + (version!=null ? ":"+version : "");
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java
index aa9ca90..625d981 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroup.java
@@ -58,8 +58,12 @@
AttributeSensor<Entity> FIRST = Sensors.newSensor(Entity.class,
"cluster.first.entity", "The first member of the cluster");
+ /**
+ * @deprecated since 0.9.0, the UI no longer relies on the use of delegates to represent group membership (see #929)
+ */
+ @Deprecated
ConfigKey<Boolean> MEMBER_DELEGATE_CHILDREN = ConfigKeys.newBooleanConfigKey(
- "group.members.delegate", "Add delegate child entities for members of the group", Boolean.FALSE);
+ "group.members.delegate", "Deprecated: Add delegate child entities for members of the group", Boolean.FALSE);
ConfigKey<String> MEMBER_DELEGATE_NAME_FORMAT = ConfigKeys.newStringConfigKey(
"group.members.delegate.nameFormat", "Delegate members name format string (Use %s for the original entity display name)", "%s");
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java
index d8814bd..bfd366f 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/AbstractGroupImpl.java
@@ -146,6 +146,7 @@
sensors().emit(MEMBER_ADDED, member);
if (Boolean.TRUE.equals(getConfig(MEMBER_DELEGATE_CHILDREN))) {
+ log.warn("Use of deprecated ConfigKey {} in {} (as of 0.9.0)", MEMBER_DELEGATE_CHILDREN.getName(), this);
Optional<Entity> result = Iterables.tryFind(getChildren(), Predicates.equalTo(member));
if (!result.isPresent()) {
String nameFormat = Optional.fromNullable(getConfig(MEMBER_DELEGATE_NAME_FORMAT)).or("%s");
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 f528db7..781cb0c 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
@@ -48,6 +48,7 @@
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
+import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.reflect.TypeToken;
@@ -101,6 +102,15 @@
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);
+ @SetFromFlag("quarantineFilter")
+ ConfigKey<Predicate<? super Throwable>> QUARANTINE_FILTER = ConfigKeys.newConfigKey(
+ new TypeToken<Predicate<? super Throwable>>() {},
+ "dynamiccluster.quarantineFilter",
+ "Quarantine the failed nodes that pass this filter (given the exception thrown by the node). "
+ + "Default is those that did not fail with NoMachinesAvailableException "
+ + "(Config ignored if quarantineFailedEntities is false)",
+ null);
+
AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Attributes.SERVICE_STATE_ACTUAL;
BasicNotificationSensor<Entity> ENTITY_QUARANTINED = new BasicNotificationSensor<Entity>(Entity.class, "dynamiccluster.entityQuarantined", "Entity failed to start, and has been quarantined");
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 b8e5c63..f434fcf 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
@@ -21,6 +21,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -38,6 +39,7 @@
import org.apache.brooklyn.api.entity.Group;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.location.MachineProvisioningLocation;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.policy.Policy;
import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -50,6 +52,7 @@
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceProblemsLogic;
+import org.apache.brooklyn.core.entity.trait.Resizable;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.entity.trait.StartableMethods;
import org.apache.brooklyn.core.location.Locations;
@@ -80,6 +83,7 @@
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
@@ -308,15 +312,16 @@
setConfigEvenIfOwned(FACTORY, factory);
}
- private Location getLocation() {
+ private Location getLocation(boolean required) {
Collection<? extends Location> ll = Locations.getLocationsCheckingAncestors(getLocations(), this);
- try {
- return Iterables.getOnlyElement(ll);
- } catch (Exception e) {
- Exceptions.propagateIfFatal(e);
- if (ll.isEmpty()) throw new IllegalStateException("No location available for "+this);
- else throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
+ if (ll.isEmpty()) {
+ if (!required) return null;
+ throw new IllegalStateException("No location available for "+this);
}
+ if (ll.size()>1) {
+ throw new IllegalStateException("Ambiguous location for "+this+"; expected one but had "+ll);
+ }
+ return Iterables.getOnlyElement(ll);
}
protected boolean isAvailabilityZoneEnabled() {
@@ -331,6 +336,19 @@
return getAttribute(QUARANTINE_GROUP);
}
+ protected Predicate<? super Throwable> getQuarantineFilter() {
+ Predicate<? super Throwable> result = getConfig(QUARANTINE_FILTER);
+ if (result != null) {
+ return result;
+ } else {
+ return new Predicate<Throwable>() {
+ @Override public boolean apply(Throwable input) {
+ return Exceptions.getFirstThrowableOfType(input, NoMachinesAvailableException.class) == null;
+ }
+ };
+ }
+ }
+
protected int getInitialQuorumSize() {
int initialSize = getConfig(INITIAL_SIZE).intValue();
int initialQuorumSize = getConfig(INITIAL_QUORUM_SIZE).intValue();
@@ -344,18 +362,17 @@
@Override
public void start(Collection<? extends Location> locsO) {
- if (locsO!=null) {
- checkArgument(locsO.size() <= 1, "Wrong number of locations supplied to start %s: %s", this, locsO);
- addLocations(locsO);
- }
- Location loc = getLocation();
+ addLocations(locsO);
+ Location loc = getLocation(false);
EntitySpec<?> spec = getConfig(MEMBER_SPEC);
if (spec!=null) {
- setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +" ("+loc+")");
+ setDefaultDisplayName("Cluster of "+JavaClassNames.simpleClassName(spec.getType()) +
+ (loc!=null ? " ("+loc+")" : ""));
}
if (isAvailabilityZoneEnabled()) {
+ if (loc==null) throw new IllegalStateException("When using availability zones, a location must be specified on the cluster");
sensors().set(SUB_LOCATIONS, findSubLocations(loc));
}
@@ -518,7 +535,20 @@
} else {
if (LOG.isDebugEnabled()) LOG.debug("Resize no-op {} from {} to {}", new Object[] {this, originalSize, desiredSize});
}
- resizeByDelta(delta);
+ // If we managed to grow at all, then expect no exception.
+ // Otherwise, if failed because NoMachinesAvailable, then propagate as InsufficientCapacityException.
+ // This tells things like the AutoScalerPolicy to not keep retrying.
+ try {
+ resizeByDelta(delta);
+ } catch (Exception e) {
+ Exceptions.propagateIfFatal(e);
+ NoMachinesAvailableException nmae = Exceptions.getFirstThrowableOfType(e, NoMachinesAvailableException.class);
+ if (nmae != null) {
+ throw new Resizable.InsufficientCapacityException("Failed to resize", e);
+ } else {
+ throw Exceptions.propagate(e);
+ }
+ }
}
return getCurrentSize();
}
@@ -545,7 +575,7 @@
Location memberLoc = null;
if (isAvailabilityZoneEnabled()) {
// this member's location could be a machine provisioned by a sub-location, or the actual sub-location
- List<Location> subLocations = findSubLocations(getLocation());
+ List<Location> subLocations = findSubLocations(getLocation(true));
Collection<Location> actualMemberLocs = member.getLocations();
boolean foundMatch = false;
for (Iterator<Location> iter = actualMemberLocs.iterator(); !foundMatch && iter.hasNext();) {
@@ -576,7 +606,7 @@
// Replacing member, so new member should be in the same location as that being replaced.
// Expect this to agree with `getMemberSpec().getLocations()` (if set). If not, then
// presumably there was a reason this specific member was started somewhere else!
- memberLoc = getLocation();
+ memberLoc = getLocation(false);
}
Entity replacement = replaceMember(member, memberLoc, ImmutableMap.of());
@@ -587,7 +617,7 @@
/**
* @throws StopFailedRuntimeException If stop failed, after successfully starting replacement
*/
- protected Entity replaceMember(Entity member, Location memberLoc, Map<?, ?> extraFlags) {
+ protected Entity replaceMember(Entity member, @Nullable Location memberLoc, Map<?, ?> extraFlags) {
synchronized (mutex) {
ReferenceWithError<Optional<Entity>> added = addInSingleLocation(memberLoc, extraFlags);
@@ -624,7 +654,7 @@
protected List<Location> getNonFailedSubLocations() {
List<Location> result = Lists.newArrayList();
Set<Location> failed = Sets.newLinkedHashSet();
- List<Location> subLocations = findSubLocations(getLocation());
+ List<Location> subLocations = findSubLocations(getLocation(true));
Set<Location> oldFailedSubLocations = getAttribute(FAILED_SUB_LOCATIONS);
if (oldFailedSubLocations == null)
oldFailedSubLocations = ImmutableSet.<Location> of();
@@ -669,7 +699,7 @@
}
}
- /** <strong>Note</strong> for sub-clases; this method can be called while synchronized on {@link #mutex}. */
+ /** <strong>Note</strong> for sub-classes; this method can be called while synchronized on {@link #mutex}. */
protected Collection<Entity> grow(int delta) {
Preconditions.checkArgument(delta > 0, "Must call grow with positive delta.");
@@ -693,11 +723,13 @@
+ ", when expected delta " + delta + " in " + this);
}
} else {
- chosenLocations = Collections.nCopies(delta, getLocation());
+ chosenLocations = Collections.nCopies(delta, getLocation(false));
}
- // create and start the entities
- return addInEachLocation(chosenLocations, ImmutableMap.of()).getWithError();
+ // create and start the entities.
+ // if any fail, then propagate the error.
+ ReferenceWithError<Collection<Entity>> result = addInEachLocation(chosenLocations, ImmutableMap.of());
+ return result.getWithError();
}
/** <strong>Note</strong> for sub-clases; this method can be called while synchronized on {@link #mutex}. */
@@ -728,8 +760,8 @@
}
}
- protected ReferenceWithError<Optional<Entity>> addInSingleLocation(Location location, Map<?,?> flags) {
- ReferenceWithError<Collection<Entity>> added = addInEachLocation(ImmutableList.of(location), flags);
+ protected ReferenceWithError<Optional<Entity>> addInSingleLocation(@Nullable Location location, Map<?,?> flags) {
+ ReferenceWithError<Collection<Entity>> added = addInEachLocation(Arrays.asList(location), flags);
Optional<Entity> result = Iterables.isEmpty(added.getWithoutError()) ? Optional.<Entity>absent() : Optional.of(Iterables.getOnlyElement(added.get()));
if (!added.hasError()) {
@@ -786,7 +818,7 @@
// quarantine/cleanup as necessary
if (!errors.isEmpty()) {
if (isQuarantineEnabled()) {
- quarantineFailedNodes(errors.keySet());
+ quarantineFailedNodes(errors);
} else {
cleanupFailedNodes(errors.keySet());
}
@@ -796,11 +828,18 @@
return ReferenceWithError.newInstanceWithoutError(result);
}
- protected void quarantineFailedNodes(Collection<Entity> failedEntities) {
- for (Entity entity : failedEntities) {
- sensors().emit(ENTITY_QUARANTINED, entity);
- getQuarantineGroup().addMember(entity);
- removeMember(entity);
+ protected void quarantineFailedNodes(Map<Entity, Throwable> failedEntities) {
+ for (Map.Entry<Entity, Throwable> entry : failedEntities.entrySet()) {
+ Entity entity = entry.getKey();
+ Throwable cause = entry.getValue();
+ if (cause == null || getQuarantineFilter().apply(cause)) {
+ sensors().emit(ENTITY_QUARANTINED, entity);
+ getQuarantineGroup().addMember(entity);
+ removeMember(entity);
+ } else {
+ LOG.info("Cluster {} discarding failed node {}, rather than quarantining", this, entity);
+ discardNode(entity);
+ }
}
}
@@ -847,7 +886,7 @@
}
@Override
- public Entity addNode(Location loc, Map<?, ?> extraFlags) {
+ public Entity addNode(@Nullable Location loc, Map<?, ?> extraFlags) {
// In case subclasses are foolish and do not call super.init() when overriding.
initialiseMemberId();
Map<?, ?> createFlags = MutableMap.builder()
@@ -879,7 +918,9 @@
if (memberSpec == null) memberSpec = getMemberSpec();
if (memberSpec != null) {
- return addChild(EntitySpec.create(memberSpec).configure(flags).location(loc));
+ EntitySpec<?> specConfigured = EntitySpec.create(memberSpec).configure(flags);
+ if (loc!=null) specConfigured.location(loc);
+ return addChild(specConfigured);
}
EntityFactory<?> factory = getFactory();
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
index 67c4c79..34051c5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/DynamicFabricImpl.java
@@ -41,6 +41,7 @@
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.trait.Changeable;
import org.apache.brooklyn.core.entity.trait.Startable;
+import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.enricher.stock.Enrichers;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -109,15 +110,11 @@
@Override
public void start(Collection<? extends Location> locsO) {
- if (locsO!=null) {
- addLocations(locsO);
- }
+ addLocations(locsO);
+ List<Location> locationsToStart = MutableList.copyOf(Locations.getLocationsCheckingAncestors(locsO, this));
- List<Location> newLocations = MutableList.copyOf(locsO);
- if (newLocations.isEmpty()) newLocations.addAll(getLocations());
-
- Preconditions.checkNotNull(newLocations, "locations must be supplied");
- Preconditions.checkArgument(newLocations.size() >= 1, "One or more locations must be supplied");
+ Preconditions.checkNotNull(locationsToStart, "locations must be supplied");
+ Preconditions.checkArgument(locationsToStart.size() >= 1, "One or more locations must be supplied");
int locIndex = 0;
@@ -133,8 +130,8 @@
Location it = null;
if (child.getLocations().isEmpty())
// give him any of these locations if he has none, allowing round robin here
- if (!newLocations.isEmpty()) {
- it = newLocations.get(locIndex++ % newLocations.size());
+ if (!locationsToStart.isEmpty()) {
+ it = locationsToStart.get(locIndex++ % locationsToStart.size());
((EntityInternal)child).addLocations(Arrays.asList(it));
}
@@ -144,12 +141,12 @@
}
}
// remove all the locations we applied to existing nodes
- while (locIndex-->0 && !newLocations.isEmpty())
- newLocations.remove(0);
+ while (locIndex-->0 && !locationsToStart.isEmpty())
+ locationsToStart.remove(0);
// finally (and usually) we create new entities for locations passed in
// (unless they were consumed by pre-existing children which didn't have locations)
- for (Location it : newLocations) {
+ for (Location it : locationsToStart) {
Entity e = addCluster(it);
((EntityInternal)e).addLocations(Arrays.asList(it));
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java
index e6f2aab..c13d914 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/group/QuarantineGroup.java
@@ -27,8 +27,6 @@
@ImplementedBy(QuarantineGroupImpl.class)
public interface QuarantineGroup extends AbstractGroup {
- ConfigKey<Boolean> MEMBER_DELEGATE_CHILDREN = ConfigKeys.newConfigKeyWithDefault(AbstractGroup.MEMBER_DELEGATE_CHILDREN, Boolean.TRUE);
-
@Effector(description="Removes all members of the quarantined group, unmanaging them")
void expungeMembers(
@EffectorParam(name="firstStop", description="Whether to first call stop() on those members that are stoppable") boolean stopFirst);
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
index b5a8616..54be703 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/BasicStartableImpl.java
@@ -48,11 +48,12 @@
@Override
public void start(Collection<? extends Location> locations) {
- log.info("Starting entity "+this+" at "+locations);
try {
ServiceStateLogic.setExpectedState(this, Lifecycle.STARTING);
addLocations(locations);
+ locations = Locations.getLocationsCheckingAncestors(locations, this);
+ log.info("Starting entity "+this+" at "+locations);
// essentially does StartableMethods.start(this, locations),
// but optionally filters locations for each child
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
index 9ee3b28..cc6d8d7 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/entity/stock/DataEntityImpl.java
@@ -38,6 +38,7 @@
@Override
public void start(Collection<? extends Location> locations) {
+ addLocations(locations);
connectSensors();
sensors().set(SERVICE_UP, Boolean.TRUE);
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
index 5e74e7c..b059858 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/location/ssh/SshMachineLocation.java
@@ -89,6 +89,7 @@
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException;
import org.apache.brooklyn.util.guava.KeyTransformingLoadingCache.KeyTransformingSameTypeLoadingCache;
+import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.pool.BasicPool;
import org.apache.brooklyn.util.pool.Pool;
import org.apache.brooklyn.util.ssh.BashCommands;
@@ -389,7 +390,7 @@
private BasicPool<SshTool> buildPool(final Map<String, ?> properties) {
return BasicPool.<SshTool>builder()
.name(getDisplayName()+"@"+address+":"+getPort()+
- (config().getRaw(SSH_HOST).isPresent() ? "("+getConfig(SSH_HOST)+":"+getConfig(SSH_PORT)+")" : "")+
+ (config().getRaw(SSH_HOST).isPresent() ? "("+getConfig(SSH_HOST)+":"+getPort()+")" : "")+
":hash"+System.identityHashCode(this))
.supplier(new Supplier<SshTool>() {
@Override public SshTool get() {
@@ -550,7 +551,21 @@
/** port for SSHing */
public int getPort() {
- return getConfig(SshTool.PROP_PORT);
+ // Prefer PROP_PORT (i.e. "port"). However if that is explicitly null or is not set, then see if
+ // SSH_PORT (i.e. "brooklyn.ssh.config.port") has been set and use that.
+ // If neither is set (or is explicitly set to null), then use the default PROP_PORT value.
+ //
+ // Note we don't just rely on config().get(PROP_PORT) returning the default, because we hit a rebind
+ // error where the location's port configuration had been explicitly set to null.
+ // See https://issues.apache.org/jira/browse/BROOKLYN-215
+
+ Maybe<Object> raw = config().getRaw(SshTool.PROP_PORT);
+ if (raw.orNull() == null && config().getRaw(SSH_PORT).orNull() != null) {
+ return config().get(SSH_PORT);
+ } else {
+ Integer result = config().get(SshTool.PROP_PORT);
+ return (result != null) ? result : SshTool.PROP_PORT.getDefaultValue();
+ }
}
protected <T> T execSsh(final Map<String, ?> props, final Function<ShellTool, T> task) {
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java
index 13dda46..74e0ddd 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionContext.java
@@ -175,18 +175,18 @@
}
final Object startCallback = properties.get("newTaskStartCallback");
- properties.put("newTaskStartCallback", new Function<Object,Void>() {
- public Void apply(Object it) {
+ properties.put("newTaskStartCallback", new Function<Task<?>,Void>() {
+ public Void apply(Task<?> it) {
registerPerThreadExecutionContext();
- if (startCallback!=null) ExecutionUtils.invoke(startCallback, it);
+ if (startCallback!=null) BasicExecutionManager.invokeCallback(startCallback, it);
return null;
}});
final Object endCallback = properties.get("newTaskEndCallback");
- properties.put("newTaskEndCallback", new Function<Object,Void>() {
- public Void apply(Object it) {
+ properties.put("newTaskEndCallback", new Function<Task<?>,Void>() {
+ public Void apply(Task<?> it) {
try {
- if (endCallback!=null) ExecutionUtils.invoke(endCallback, it);
+ if (endCallback!=null) BasicExecutionManager.invokeCallback(endCallback, it);
} finally {
clearPerThreadExecutionContext();
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
index d90b1a1..0aab7d5 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
@@ -19,6 +19,7 @@
package org.apache.brooklyn.util.core.task;
import static com.google.common.base.Preconditions.checkNotNull;
+import groovy.lang.Closure;
import java.util.Collection;
import java.util.Collections;
@@ -51,16 +52,22 @@
import org.apache.brooklyn.api.mgmt.TaskAdaptable;
import org.apache.brooklyn.core.BrooklynFeatureEnablement;
import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException;
import org.apache.brooklyn.util.text.Identifiers;
+import org.apache.brooklyn.util.text.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.Beta;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.CaseFormat;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ExecutionList;
@@ -368,7 +375,6 @@
return submitSubsequentScheduledTask(flags, task);
}
- @SuppressWarnings("unchecked")
protected Task<?> submitSubsequentScheduledTask(final Map<?,?> flags, final ScheduledTask task) {
if (!task.isDone()) {
task.internalFuture = delayedRunner.schedule(new ScheduledTaskCallable(task, flags),
@@ -508,9 +514,15 @@
*/
if (log.isDebugEnabled()) {
// debug only here, because most submitters will handle failures
- log.debug("Exception running task "+task+" (rethrowing): "+error.getMessage(), error);
- if (log.isTraceEnabled())
- log.trace("Trace for exception running task "+task+" (rethrowing): "+error.getMessage(), error);
+ if (error instanceof InterruptedException || error instanceof RuntimeInterruptedException) {
+ log.debug("Detected interruption on task "+task+" (rethrowing)" +
+ (Strings.isNonBlank(error.getMessage()) ? ": "+error.getMessage() : ""));
+ } else {
+ log.debug("Exception running task "+task+" (rethrowing): "+error);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Trace for exception running task "+task+" (rethrowing): "+error, error);
+ }
}
throw Exceptions.propagate(error);
}
@@ -526,19 +538,64 @@
}
}
- private final static class ListenableForwardingFutureForTask<T> extends ListenableForwardingFuture<T> {
+ @SuppressWarnings("deprecation")
+ // TODO do we even need a listenable future here? possibly if someone wants to interrogate the future it might
+ // be interesting, so possibly it is useful that we implement ListenableFuture...
+ private final static class CancellingListenableForwardingFutureForTask<T> extends ListenableForwardingFuture<T> {
private final Task<T> task;
+ private BasicExecutionManager execMgmt;
- private ListenableForwardingFutureForTask(Future<T> delegate, ExecutionList list, Task<T> task) {
+ private CancellingListenableForwardingFutureForTask(BasicExecutionManager execMgmt, Future<T> delegate, ExecutionList list, Task<T> task) {
super(delegate, list);
+ this.execMgmt = execMgmt;
this.task = task;
}
@Override
- public boolean cancel(boolean mayInterruptIfRunning) {
+ public boolean cancel(TaskCancellationMode mode) {
boolean result = false;
- if (!task.isCancelled()) result |= task.cancel(mayInterruptIfRunning);
- result |= super.cancel(mayInterruptIfRunning);
+ if (log.isTraceEnabled()) {
+ log.trace("CLFFT cancelling "+task+" mode "+mode);
+ }
+ if (!task.isCancelled()) result |= ((TaskInternal<T>)task).cancel(mode);
+ result |= delegate().cancel(mode.isAllowedToInterruptTask());
+
+ if (mode.isAllowedToInterruptAllSubmittedTasks() || mode.isAllowedToInterruptDependentSubmittedTasks()) {
+ int subtasksFound=0;
+ int subtasksReallyCancelled=0;
+
+ if (task instanceof HasTaskChildren) {
+ for (Task<?> child: ((HasTaskChildren)task).getChildren()) {
+ if (log.isTraceEnabled()) {
+ log.trace("Cancelling "+child+" on recursive cancellation of "+task);
+ }
+ subtasksFound++;
+ if (((TaskInternal<?>)child).cancel(mode)) {
+ result = true;
+ subtasksReallyCancelled++;
+ }
+ }
+ }
+ // TODO this is inefficient; might want to keep an index on submitted-by
+ for (Task<?> t: execMgmt.getAllTasks()) {
+ if (task.equals(t.getSubmittedByTask())) {
+ if (mode.isAllowedToInterruptAllSubmittedTasks() || BrooklynTaskTags.isTransient(t)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Cancelling "+t+" on recursive cancellation of "+task);
+ }
+ subtasksFound++;
+ if (((TaskInternal<?>)t).cancel(mode)) {
+ result = true;
+ subtasksReallyCancelled++;
+ }
+ }
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("On cancel of "+task+", applicable subtask count "+subtasksFound+", of which "+subtasksReallyCancelled+" were actively cancelled");
+ }
+ }
+
((TaskInternal<?>)task).runListeners();
return result;
}
@@ -571,9 +628,15 @@
@SuppressWarnings("unchecked")
protected <T> Task<T> submitNewTask(final Map<?,?> flags, final Task<T> task) {
- if (log.isTraceEnabled()) log.trace("Submitting task {} ({}), with flags {}, and tags {}, job {}",
+ if (log.isTraceEnabled()) {
+ log.trace("Submitting task {} ({}), with flags {}, and tags {}, job {}; caller {}",
new Object[] {task.getId(), task, Sanitizer.sanitize(flags), task.getTags(),
- (task instanceof TaskInternal ? ((TaskInternal<T>)task).getJob() : "<unavailable>")});
+ (task instanceof TaskInternal ? ((TaskInternal<T>)task).getJob() : "<unavailable>"),
+ Tasks.current() });
+ if (Tasks.current()==null && BrooklynTaskTags.isTransient(task)) {
+ log.trace("Stack trace for unparented submission of transient "+task, new Throwable("trace only (not an error)"));
+ }
+ }
if (task instanceof ScheduledTask)
return (Task<T>) submitNewScheduledTask(flags, (ScheduledTask)task);
@@ -604,15 +667,16 @@
} else {
future = runner.submit(job);
}
- // on completion, listeners get triggered above; here, below we ensure they get triggered on cancel
- // (and we make sure the same ExecutionList is used in the future as in the task)
- ListenableFuture<T> listenableFuture = new ListenableForwardingFutureForTask<T>(future, ((TaskInternal<T>)task).getListeners(), task);
- // doesn't matter whether the listener is added to the listenableFuture or the task,
- // except that for the task we can more easily wrap it so that it only logs debug if the executor is shutdown
- // (avoid a bunch of ugly warnings in tests which start and stop things a lot!)
- // [probably even nicer to run this in the same thread, it doesn't do much; but that is messier to implement]
+ // SubmissionCallable (above) invokes the listeners on completion;
+ // this future allows a caller to add custom listeners
+ // (it does not notify the listeners; that's our job);
+ // except on cancel we want to listen
+ ListenableFuture<T> listenableFuture = new CancellingListenableForwardingFutureForTask<T>(this, future, ((TaskInternal<T>)task).getListeners(), task);
+ // and we want to make sure *our* (manager) listeners are given suitable callback
((TaskInternal<T>)task).addListener(new SubmissionListenerToCallOtherListeners<T>(task), runner);
+ // NB: can the above mean multiple callbacks to TaskInternal#runListeners?
+ // finally expose the future to callers
((TaskInternal<T>)task).initInternalFuture(listenableFuture);
return task;
@@ -665,9 +729,27 @@
PerThreadCurrentTaskHolder.perThreadCurrentTask.set(task);
((TaskInternal<?>)task).setStartTimeUtc(System.currentTimeMillis());
}
- ExecutionUtils.invoke(flags.get("newTaskStartCallback"), task);
+ invokeCallback(flags.get("newTaskStartCallback"), task);
}
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ // not ideal, such loose typing on the callback -- should prefer Function<Task,Object>
+ // but at least it's package-private
+ static Object invokeCallback(Object callable, Task<?> task) {
+ if (callable instanceof Closure) return ((Closure<?>)callable).call(task);
+ if (callable instanceof Callable) {
+ try {
+ return ((Callable<?>)callable).call();
+ } catch (Throwable t) {
+ throw Exceptions.propagate(t);
+ }
+ }
+ if (callable instanceof Runnable) { ((Runnable)callable).run(); return null; }
+ if (callable instanceof Function) { return ((Function)callable).apply(task); }
+ if (callable==null) return null;
+ throw new IllegalArgumentException("Cannot invoke unexpected callback object "+callable+" of type "+callable.getClass()+" on "+task);
+ }
+
/** normally (if not interrupted) called once for each call to {@link #beforeSubmitScheduledTaskAllIterations(Map, Task)} */
protected void afterEndScheduledTaskAllIterations(Map<?,?> flags, Task<?> task) {
internalAfterEnd(flags, task, false, true);
@@ -693,7 +775,7 @@
}
if (isEndingAllIterations) {
incompleteTaskIds.remove(task.getId());
- ExecutionUtils.invoke(flags.get("newTaskEndCallback"), task);
+ invokeCallback(flags.get("newTaskEndCallback"), task);
((TaskInternal<?>)task).setEndTimeUtc(System.currentTimeMillis());
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
index 0c26dd1..7c29bba 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/BasicTask.java
@@ -155,7 +155,7 @@
(Strings.isNonEmpty(displayName) ?
displayName :
(job + (tags!=null && !tags.isEmpty() ? ";"+tags : "")) ) +
- ":"+getId()+"]";
+ "]@"+getId();
}
@Override
@@ -196,7 +196,7 @@
protected Maybe<Task<?>> submittedByTask;
protected volatile Thread thread = null;
- private volatile boolean cancelled = false;
+ protected volatile boolean cancelled = false;
/** normally a {@link ListenableFuture}, except for scheduled tasks when it may be a {@link ScheduledFuture} */
protected volatile Future<T> internalFuture = null;
@@ -288,15 +288,34 @@
}
@Override
- public synchronized boolean cancel(boolean mayInterruptIfRunning) {
+ public final synchronized boolean cancel(boolean mayInterruptIfRunning) {
+ // semantics changed in 2016-01, previously "true" was INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS
+ return cancel(mayInterruptIfRunning ? TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS
+ : TaskCancellationMode.DO_NOT_INTERRUPT);
+ }
+
+ @Override @Beta
+ public synchronized boolean cancel(TaskCancellationMode mode) {
if (isDone()) return false;
- boolean cancel = true;
- cancelled = true;
- if (internalFuture!=null) {
- cancel = internalFuture.cancel(mayInterruptIfRunning);
+ if (log.isTraceEnabled()) {
+ log.trace("BT cancelling "+this+" mode "+mode);
}
+ cancelled = true;
+ doCancel(mode);
notifyAll();
- return cancel;
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ protected boolean doCancel(TaskCancellationMode mode) {
+ if (internalFuture!=null) {
+ if (internalFuture instanceof ListenableForwardingFuture) {
+ return ((ListenableForwardingFuture<?>)internalFuture).cancel(mode);
+ } else {
+ return internalFuture.cancel(mode.isAllowedToInterruptTask());
+ }
+ }
+ return true;
}
@Override
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java
index b7985c8..51a4e34 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/DynamicSequentialTask.java
@@ -158,27 +158,44 @@
}
@Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return cancel(mayInterruptIfRunning, mayInterruptIfRunning, true);
- }
- public boolean cancel(boolean mayInterruptTask, boolean interruptPrimaryThread, boolean alsoCancelChildren) {
- if (isDone()) return false;
- if (log.isTraceEnabled()) log.trace("cancelling {}", this);
- boolean cancel = super.cancel(mayInterruptTask);
- if (alsoCancelChildren) {
+ protected boolean doCancel(TaskCancellationMode mode) {
+ boolean result = false;
+ if (mode.isAllowedToInterruptDependentSubmittedTasks() || mode.isAllowedToInterruptAllSubmittedTasks()) {
for (Task<?> t: secondaryJobsAll)
- cancel |= t.cancel(mayInterruptTask);
+ result = ((TaskInternal<?>)t).cancel(mode) || result;
}
+ return super.doCancel(mode) || result;
+ // returns true if anything is successfully cancelled
+ }
+
+ public boolean cancel(TaskCancellationMode mode) {
+ return cancel(mode, null);
+ }
+
+ protected boolean cancel(TaskCancellationMode mode, Boolean interruptPrimaryThreadOverride) {
+ if (isDone()) return false;
+ if (log.isTraceEnabled()) log.trace("cancelling DST {}", this);
+
+ // first do the super's cancel, setting cancelled, and calling doCancel to cancel children
+ boolean result = super.cancel(mode);
+ // then come back and ensure our primary thread is cancelled if needed
+
+ if (interruptPrimaryThreadOverride==null) interruptPrimaryThreadOverride = mode.isAllowedToInterruptTask();
+ if (log.isTraceEnabled()) {
+ log.trace("DST cancelling "+this+" mode "+mode+", interruptPrimary "+interruptPrimaryThreadOverride);
+ }
+
synchronized (jobTransitionLock) {
if (primaryThread!=null) {
- if (interruptPrimaryThread) {
+ if (interruptPrimaryThreadOverride) {
if (log.isTraceEnabled()) log.trace("cancelling {} - interrupting", this);
primaryThread.interrupt();
}
- cancel = true;
+ result = true;
}
}
- return cancel;
+
+ return result;
}
@Override
@@ -309,7 +326,7 @@
}
if (!primaryFinished && failureHandlingConfig.cancelPrimaryOnSecondaryFailure) {
- cancel(true, false, false);
+ cancel(TaskCancellationMode.INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS, false);
}
result.add(Tasks.getError(secondaryJob));
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ExecutionUtils.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ExecutionUtils.java
deleted file mode 100644
index 72a5ae4..0000000
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ExecutionUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.util.core.task;
-
-import groovy.lang.Closure;
-
-import java.util.concurrent.Callable;
-
-import com.google.common.base.Function;
-import com.google.common.base.Throwables;
-
-public class ExecutionUtils {
- /**
- * Attempts to run/call the given object, with the given arguments if possible, preserving the return value if there is one (null otherwise);
- * throws exception if the callable is a non-null object which cannot be invoked (not a callable or runnable)
- * @deprecated since 0.7.0 ; this super-loose typing should be avoided; if it is needed, let's move it to one of the Groovy compatibility classes
- */
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public static Object invoke(Object callable, Object ...args) {
- if (callable instanceof Closure) return ((Closure<?>)callable).call(args);
- if (callable instanceof Callable) {
- try {
- return ((Callable<?>)callable).call();
- } catch (Throwable t) {
- throw Throwables.propagate(t);
- }
- }
- if (callable instanceof Runnable) { ((Runnable)callable).run(); return null; }
- if (callable instanceof Function && args.length == 1) { return ((Function)callable).apply(args[0]); }
- if (callable==null) return null;
- throw new IllegalArgumentException("Cannot invoke unexpected object "+callable+" of type "+callable.getClass()+", with "+args.length+" args");
- }
-}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java
index 4ce56d1..cbc474c 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ListenableForwardingFuture.java
@@ -21,15 +21,26 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
+import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.google.common.util.concurrent.ExecutionList;
import com.google.common.util.concurrent.ForwardingFuture.SimpleForwardingFuture;
import com.google.common.util.concurrent.ListenableFuture;
-/** Wraps a Future, making it a ListenableForwardingFuture, but with the caller having the resposibility to:
+/** Wraps a Future, making it a ListenableForwardingFuture, but with the caller having the responsibility to:
* <li> invoke the listeners on job completion (success or error)
- * <li> invoke the listeners on cancel */
+ * <li> invoke the listeners on cancel
+ *
+ * @deprecated since 0.9.0 likely to leave the public API */
+@Deprecated // TODO just one subclass, it can hold the behaviour we need from this,
+// and the methods here are surprising as they expect the caller to notify the list
public abstract class ListenableForwardingFuture<T> extends SimpleForwardingFuture<T> implements ListenableFuture<T> {
+ private static final Logger log = LoggerFactory.getLogger(ListenableForwardingFuture.class);
+
+ // TODO these are never accessed or used
final ExecutionList listeners;
protected ListenableForwardingFuture(Future<T> delegate) {
@@ -42,9 +53,22 @@
this.listeners = list;
}
+ private static boolean warned = false;
+
@Override
public void addListener(Runnable listener, Executor executor) {
+ if (!warned) {
+ log.warn("Use of deprecated ListenableForwardingFuture.addListener at "+this+" (future calls will not be logged)", new Throwable("stack trace"));
+ warned = true;
+ }
+
listeners.add(listener, executor);
}
+ public abstract boolean cancel(TaskCancellationMode mode);
+
+ public final boolean cancel(boolean mayInterrupt) {
+ return cancel(TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS);
+ }
+
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java
index c1ad4f8..219f4f8 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/ScheduledTask.java
@@ -43,7 +43,7 @@
*/
// TODO ScheduledTask is a very pragmatic implementation; would be nice to tighten,
// reduce external assumptions about internal structure, and clarify "done" semantics
-public class ScheduledTask extends BasicTask {
+public class ScheduledTask extends BasicTask<Object> {
final Callable<Task<?>> taskFactory;
@@ -84,7 +84,7 @@
this(MutableMap.of(), task);
}
- public ScheduledTask(Map flags, final Task<?> task){
+ public ScheduledTask(Map<?,?> flags, final Task<?> task){
this(flags, new Callable<Task<?>>(){
@Override
public Task<?> call() throws Exception {
@@ -92,7 +92,7 @@
}});
}
- public ScheduledTask(Map flags, Callable<Task<?>> taskFactory) {
+ public ScheduledTask(Map<?,?> flags, Callable<Task<?>> taskFactory) {
super(flags);
this.taskFactory = taskFactory;
@@ -194,13 +194,11 @@
}
@Override
- public synchronized boolean cancel(boolean mayInterrupt) {
- boolean result = super.cancel(mayInterrupt);
+ protected boolean doCancel(org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode mode) {
if (nextRun!=null) {
- nextRun.cancel(mayInterrupt);
- notifyAll();
+ ((TaskInternal<?>)nextRun).cancel(mode);
}
- return result;
+ return super.doCancel(mode);
}
/**
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java
index 2bf0fec..99c2773 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskInternal.java
@@ -29,6 +29,7 @@
import com.google.common.annotations.Beta;
import com.google.common.base.Function;
+import com.google.common.base.Objects;
import com.google.common.util.concurrent.ExecutionList;
import com.google.common.util.concurrent.ListenableFuture;
@@ -95,6 +96,8 @@
Object getExtraStatusText();
+ /** On task completion (or cancellation) runs the listeners which have been registered using
+ * {@link #addListener(Runnable, java.util.concurrent.Executor)}. */
void runListeners();
void setEndTimeUtc(long val);
@@ -120,5 +123,41 @@
/** if a task is a proxy for another one (used mainly for internal tasks),
* this returns the "real" task represented by this one */
Task<?> getProxyTarget();
+
+ /** clearer semantics around cancellation; may be promoted to {@link Task} if we */
+ @Beta
+ public boolean cancel(TaskCancellationMode mode);
+
+ @Beta
+ public static class TaskCancellationMode {
+ public static final TaskCancellationMode DO_NOT_INTERRUPT = new TaskCancellationMode(false, false, false);
+ public static final TaskCancellationMode INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS = new TaskCancellationMode(true, false, false);
+ public static final TaskCancellationMode INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS = new TaskCancellationMode(true, true, false);
+ public static final TaskCancellationMode INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS = new TaskCancellationMode(true, true, true);
+
+ private final boolean allowedToInterruptTask,
+ allowedToInterruptDependentSubmittedTasks,
+ allowedToInterruptAllSubmittedTasks;
+
+ private TaskCancellationMode(boolean mayInterruptIfRunning, boolean interruptSubmittedTransients, boolean interruptAllSubmitted) {
+ this.allowedToInterruptTask = mayInterruptIfRunning;
+ this.allowedToInterruptDependentSubmittedTasks = interruptSubmittedTransients;
+ this.allowedToInterruptAllSubmittedTasks = interruptAllSubmitted;
+ }
+
+ public boolean isAllowedToInterruptTask() { return allowedToInterruptTask; }
+ /** Implementation-dependent what "dependent" means in this context,
+ * e.g. may be linked to a "transient" tag (that's what Brooklyn does) */
+ public boolean isAllowedToInterruptDependentSubmittedTasks() { return allowedToInterruptDependentSubmittedTasks; }
+ public boolean isAllowedToInterruptAllSubmittedTasks() { return allowedToInterruptAllSubmittedTasks; }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this).add("interruptTask", allowedToInterruptTask)
+ .add("interruptDependentSubmitted", allowedToInterruptDependentSubmittedTasks)
+ .add("interruptAllSubmitted", allowedToInterruptAllSubmittedTasks)
+ .toString();
+ }
+ }
}
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java
index 8e46002..d8d3764 100644
--- a/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/util/core/task/TaskPredicates.java
@@ -60,4 +60,20 @@
return "displayNameMatches("+matcher+")";
}
}
+
+ public static Predicate<Task<?>> isDone() {
+ return new IsDone();
+ }
+
+ private static class IsDone implements Predicate<Task<?>> {
+ @Override
+ public boolean apply(Task<?> input) {
+ return input.isDone();
+ }
+ @Override
+ public String toString() {
+ return "isDone()";
+ }
+ }
+
}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java
index 532eb4d..7845326 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntity.java
@@ -60,7 +60,7 @@
ConfigKey<Predicate<? super FailingEntity>> FAIL_ON_RESTART_CONDITION = (ConfigKey) ConfigKeys.newConfigKey(Predicate.class, "failOnRestartCondition", "Whether to throw exception on call to restart", null);
@SetFromFlag("exceptionClazz")
- ConfigKey<Class<? extends RuntimeException>> EXCEPTION_CLAZZ = (ConfigKey) ConfigKeys.newConfigKey(Class.class, "exceptionClazz", "Type of exception to throw", IllegalStateException.class);
+ ConfigKey<Class<? extends Exception>> EXCEPTION_CLAZZ = (ConfigKey) ConfigKeys.newConfigKey(Class.class, "exceptionClazz", "Type of exception to throw", IllegalStateException.class);
@SetFromFlag("execOnFailure")
ConfigKey<Function<? super FailingEntity,?>> EXEC_ON_FAILURE = (ConfigKey) ConfigKeys.newConfigKey(Function.class, "execOnFailure", "Callback to execute before throwing an exception, on any failure", Functions.identity());
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
index a675c78..423bdd3 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/entity/trait/FailingEntityImpl.java
@@ -79,7 +79,12 @@
private RuntimeException newException(String msg) {
try {
- return getConfig(EXCEPTION_CLAZZ).getConstructor(String.class).newInstance("Simulating entity stop failure for test");
+ Exception result = getConfig(EXCEPTION_CLAZZ).getConstructor(String.class).newInstance("Simulating entity stop failure for test");
+ if (!(result instanceof RuntimeException)) {
+ return new RuntimeException("wrapping", result);
+ } else {
+ return (RuntimeException)result;
+ }
} catch (Exception e) {
throw Exceptions.propagate(e);
}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java
index 92fdb55..ec8cacb 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindSshMachineLocationTest.java
@@ -80,5 +80,23 @@
assertEquals(newChildLoc.execScript(Collections.<String,Object>emptyMap(), "mysummary", ImmutableList.of("true")), 0);
}
-
+
+ // See https://issues.apache.org/jira/browse/BROOKLYN-215
+ @Test(groups="Integration")
+ public void testRebindWhenPortNull() throws Exception {
+ SshMachineLocation machine = origManagementContext.getLocationManager().createLocation(LocationSpec.create(SshMachineLocation.class)
+ .configure("address", "localhost")
+ .configure("port", null));
+ FixedListMachineProvisioningLocation<?> byon = origManagementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class)
+ .configure("machines", ImmutableList.of(machine)));
+ origApp.start(ImmutableList.of(byon));
+ LOG.info("Before rebind, machine="+machine.toString());
+
+ newApp = (TestApplication) rebind();
+
+ FixedListMachineProvisioningLocation<?> newByon = (FixedListMachineProvisioningLocation<?>) Iterables.getOnlyElement(newApp.getLocations(), 0);
+ SshMachineLocation newMachine = (SshMachineLocation) Iterables.get(newByon.getChildren(), 0);
+
+ LOG.info("After rebind, machine="+newMachine.toString());
+ }
}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java
index 3ff61f0..9593203 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestCluster.java
@@ -18,8 +18,12 @@
*/
package org.apache.brooklyn.core.test.entity;
+import java.util.List;
+
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.entity.group.DynamicCluster;
/**
@@ -27,4 +31,10 @@
*/
@ImplementedBy(TestClusterImpl.class)
public interface TestCluster extends DynamicCluster, EntityLocal {
+
+ ConfigKey<Integer> MAX_SIZE = ConfigKeys.newIntegerConfigKey("testCluster.maxSize", "Size after which it will throw InsufficientCapacityException", Integer.MAX_VALUE);
+
+ List<Integer> getSizeHistory();
+
+ List<Integer> getDesiredSizeHistory();
}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java
index bf8d0cb..0edea8f 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/test/entity/TestClusterImpl.java
@@ -18,22 +18,32 @@
*/
package org.apache.brooklyn.core.test.entity;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.entity.group.DynamicClusterImpl;
import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
+import com.google.common.collect.ImmutableList;
+
/**
* Mock cluster entity for testing.
*/
public class TestClusterImpl extends DynamicClusterImpl implements TestCluster {
private volatile int size;
+ private final List<Integer> desiredSizeHistory = Collections.synchronizedList(new ArrayList<Integer>());
+ private final List<Integer> sizeHistory = Collections.synchronizedList(new ArrayList<Integer>());
+
public TestClusterImpl() {
}
@Override
public void init() {
super.init();
+ sizeHistory.add(size);
size = getConfig(INITIAL_SIZE);
sensors().set(Startable.SERVICE_UP, true);
}
@@ -48,11 +58,36 @@
@Override
public Integer resize(Integer desiredSize) {
- this.size = desiredSize;
+ desiredSizeHistory.add(desiredSize);
+ int achievableSize = Math.min(desiredSize, getConfig(MAX_SIZE));
+
+ if (achievableSize != size) {
+ this.sizeHistory.add(achievableSize);
+ this.size = achievableSize;
+ }
+
+ if (desiredSize > achievableSize) {
+ throw new InsufficientCapacityException("Simulating insufficient capacity (desiredSize="+desiredSize+"; maxSize="+getConfig(MAX_SIZE)+"; newSize="+size+")");
+ }
+
return size;
}
@Override
+ public List<Integer> getSizeHistory() {
+ synchronized (sizeHistory) {
+ return ImmutableList.copyOf(sizeHistory);
+ }
+ }
+
+ @Override
+ public List<Integer> getDesiredSizeHistory() {
+ synchronized (desiredSizeHistory) {
+ return ImmutableList.copyOf(desiredSizeHistory);
+ }
+ }
+
+ @Override
public void stop() {
size = 0;
super.stop();
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
index dd37e58..b58c630 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/entity/group/DynamicClusterTest.java
@@ -44,6 +44,7 @@
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.location.NoMachinesAvailableException;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.sensor.SensorEvent;
import org.apache.brooklyn.core.entity.Attributes;
@@ -54,6 +55,7 @@
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.trait.Changeable;
import org.apache.brooklyn.core.entity.trait.FailingEntity;
+import org.apache.brooklyn.core.entity.trait.Resizable;
import org.apache.brooklyn.core.location.SimulatedLocation;
import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
@@ -72,13 +74,13 @@
import org.testng.annotations.Test;
import com.google.common.base.Function;
+import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Atomics;
@@ -108,9 +110,9 @@
try {
app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
.configure("factory", "error"));
- fail();
+ Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
- if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
+ Asserts.expectedFailure(e);
}
}
@@ -119,9 +121,9 @@
DynamicCluster c = app.createAndManageChild(EntitySpec.create(DynamicCluster.class));
try {
c.start(ImmutableList.of(loc));
- fail();
+ Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
- if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
+ Asserts.expectedFailureOfType(e, IllegalStateException.class);
}
}
@@ -133,9 +135,9 @@
cluster.start(ImmutableList.of(loc));
cluster.stop();
cluster.start(ImmutableList.of(loc2));
- fail();
+ Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
- if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) throw e;
+ Asserts.expectedFailureOfType(e, IllegalStateException.class);
}
}
@@ -145,9 +147,9 @@
.configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class)));
try {
cluster.start(ImmutableList.of(loc, loc2));
- fail();
+ Asserts.shouldHaveFailedPreviously();
} catch (Exception e) {
- if (Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class) == null) throw e;
+ Asserts.expectedFailureContainsIgnoreCase(e, "ambiguous");
}
}
@@ -202,6 +204,65 @@
assertEquals(entity.getApplication(), app);
}
+ @Test
+ public void testResizeWhereChildThrowsNoMachineAvailableExceptionIsPropagatedAsInsufficientCapacityException() throws Exception {
+ final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+ .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class)
+ .configure(FailingEntity.FAIL_ON_START, true)
+ .configure(FailingEntity.EXCEPTION_CLAZZ, NoMachinesAvailableException.class))
+ .configure(DynamicCluster.INITIAL_SIZE, 0));
+ cluster.start(ImmutableList.of(loc));
+
+ try {
+ cluster.resize(1);
+ Asserts.shouldHaveFailedPreviously();
+ } catch (Exception e) {
+ Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+ }
+ }
+
+ @Test
+ public void testResizeWhereSubsetOfChildrenThrowsNoMachineAvailableExceptionIsPropagatedAsInsuffientCapacityException() throws Exception {
+ final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+ .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(FailingEntity.class)
+ .configure(FailingEntity.FAIL_ON_START_CONDITION, new Predicate<FailingEntity>() {
+ final AtomicInteger counter = new AtomicInteger();
+ @Override public boolean apply(FailingEntity input) {
+ // Only second and subsequent entities fail
+ int index = counter.getAndIncrement();
+ return (index >= 1);
+ }})
+ .configure(FailingEntity.EXCEPTION_CLAZZ, NoMachinesAvailableException.class))
+ .configure(DynamicCluster.INITIAL_SIZE, 0));
+ cluster.start(ImmutableList.of(loc));
+
+ // Managed to partially resize, but will still throw exception.
+ // The getCurrentSize will report how big we managed to get.
+ // The children that failed due to NoMachinesAvailableException will have been unmanaged automatically.
+ try {
+ cluster.resize(2);
+ Asserts.shouldHaveFailedPreviously();
+ } catch (Exception e) {
+ Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+ }
+ assertEquals(cluster.getCurrentSize(), (Integer)1);
+ Iterable<FailingEntity> children1 = Iterables.filter(cluster.getChildren(), FailingEntity.class);
+ assertEquals(Iterables.size(children1), 1);
+ assertEquals(Iterables.getOnlyElement(children1).sensors().get(TestEntity.SERVICE_UP), Boolean.TRUE);
+
+ // This attempt will also fail, because all new children will fail
+ try {
+ cluster.resize(2);
+ Asserts.shouldHaveFailedPreviously();
+ } catch (Exception e) {
+ Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+ }
+ assertEquals(cluster.getCurrentSize(), (Integer)1);
+ Iterable<FailingEntity> children2 = Iterables.filter(cluster.getChildren(), FailingEntity.class);
+ assertEquals(Iterables.size(children2), 1);
+ assertEquals(Iterables.getOnlyElement(children2), Iterables.getOnlyElement(children1));
+ }
+
/** This can be sensitive to order, e.g. if TestEntity set expected RUNNING before setting SERVICE_UP,
* there would be a point when TestEntity is ON_FIRE.
* <p>
@@ -249,6 +310,7 @@
assertEquals(Iterables.size(Entities.descendants(cluster, TestEntity.class)), 0);
}
+
@Test
public void currentSizePropertyReflectsActualClusterSize() throws Exception {
DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
@@ -583,6 +645,62 @@
}
@Test
+ public void testQuarantineFailedEntitiesRespectsCustomFilter() throws Exception {
+ Predicate<Throwable> filter = new Predicate<Throwable>() {
+ @Override public boolean apply(Throwable input) {
+ return Exceptions.getFirstThrowableOfType(input, AllowedException.class) != null;
+ }
+ };
+ runQuarantineFailedEntitiesRespectsFilter(AllowedException.class, DisallowedException.class, filter);
+ }
+ @SuppressWarnings("serial")
+ public static class AllowedException extends RuntimeException {
+ public AllowedException(String message) {
+ super(message);
+ }
+ }
+ @SuppressWarnings("serial")
+ public static class DisallowedException extends RuntimeException {
+ public DisallowedException(String message) {
+ super(message);
+ }
+ }
+
+ @Test
+ public void testQuarantineFailedEntitiesRespectsDefaultFilter() throws Exception {
+ Predicate<Throwable> filter = null;
+ runQuarantineFailedEntitiesRespectsFilter(AllowedException.class, NoMachinesAvailableException.class, filter);
+ }
+
+ protected void runQuarantineFailedEntitiesRespectsFilter(Class<? extends Exception> allowedException,
+ Class<? extends Exception> disallowedException, Predicate<Throwable> quarantineFilter) throws Exception {
+ final List<Class<? extends Exception>> failureCauses = ImmutableList.<Class<? extends Exception>>of(allowedException, disallowedException);
+ final AtomicInteger counter = new AtomicInteger(0);
+ DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+ .configure("quarantineFailedEntities", true)
+ .configure("initialSize", 0)
+ .configure("quarantineFilter", quarantineFilter)
+ .configure("factory", new EntityFactory() {
+ @Override public Entity newEntity(Map flags, Entity parent) {
+ int num = counter.getAndIncrement();
+ return app.getManagementContext().getEntityManager().createEntity(EntitySpec.create(FailingEntity.class)
+ .configure(flags)
+ .configure(FailingEntity.FAIL_ON_START, true)
+ .configure(FailingEntity.EXCEPTION_CLAZZ, failureCauses.get(num))
+ .parent(parent));
+ }}));
+
+ cluster.start(ImmutableList.of(loc));
+ resizeExpectingError(cluster, 2);
+ Iterable<FailingEntity> children = Iterables.filter(cluster.getChildren(), FailingEntity.class);
+ Collection<Entity> quarantineMembers = cluster.sensors().get(DynamicCluster.QUARANTINE_GROUP).getMembers();
+
+ assertEquals(cluster.getCurrentSize(), (Integer)0);
+ assertEquals(Iterables.getOnlyElement(children).config().get(FailingEntity.EXCEPTION_CLAZZ), allowedException);
+ assertEquals(Iterables.getOnlyElement(quarantineMembers), Iterables.getOnlyElement(children));
+ }
+
+ @Test
public void defaultRemovalStrategyShutsDownNewestFirstWhenResizing() throws Exception {
final List<Entity> creationOrder = Lists.newArrayList();
DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java
index a291c53..e8e7890 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/BasicTaskExecutionPerformanceTest.java
@@ -70,7 +70,6 @@
if (em != null) em.shutdownNow();
}
- @SuppressWarnings("unchecked")
@Test
public void testScheduledTaskExecutedAfterDelay() throws Exception {
int delay = 100;
@@ -95,7 +94,6 @@
assertTrue(actualDelay < (delay+MAX_OVERHEAD_MS), "actualDelay="+actualDelay+"; delay="+delay);
}
- @SuppressWarnings("unchecked")
@Test
public void testScheduledTaskExecutedAtRegularPeriod() throws Exception {
final int period = 100;
@@ -127,7 +125,6 @@
}
}
- @SuppressWarnings("unchecked")
@Test
public void testCanCancelScheduledTask() throws Exception {
final int period = 1;
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
index ceff29f..364870a 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
@@ -30,10 +30,13 @@
import org.apache.brooklyn.api.mgmt.HasTaskChildren;
import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.CollectionFunctionals;
import org.apache.brooklyn.util.collections.MutableList;
import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.math.MathPredicates;
import org.apache.brooklyn.util.time.CountdownTimer;
@@ -49,6 +52,8 @@
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Stopwatch;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -154,7 +159,17 @@
}
public Task<String> sayTask(String message, Duration duration, String message2) {
- return Tasks.<String>builder().body(sayCallable(message, duration, message2)).build();
+ return Tasks.<String>builder().displayName("say:"+message).body(sayCallable(message, duration, message2)).build();
+ }
+
+ public <T> Task<T> submitting(final Task<T> task) {
+ return Tasks.<T>builder().displayName("submitting:"+task.getId()).body(new Callable<T>() {
+ @Override
+ public T call() throws Exception {
+ ec.submit(task);
+ return task.get();
+ }
+ }).build();
}
@Test
@@ -207,6 +222,85 @@
// but we do _not_ get a mutex from task3 as it does not run (is not interrupted)
Assert.assertEquals(cancellations.availablePermits(), 0);
}
+
+ @Test
+ public void testCancellationModeAndSubmitted() throws Exception {
+ doTestCancellationModeAndSubmitted(true, TaskCancellationMode.DO_NOT_INTERRUPT, false, false);
+
+ doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS, true, true);
+ doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS, true, true);
+ doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_BUT_NOT_SUBMITTED_TASKS, true, false);
+
+ // if it's not transient, it should only be cancelled on "all submitted"
+ doTestCancellationModeAndSubmitted(false, TaskCancellationMode.INTERRUPT_TASK_AND_DEPENDENT_SUBMITTED_TASKS, true, false);
+ doTestCancellationModeAndSubmitted(false, TaskCancellationMode.INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS, true, true);
+
+ // cancellation mode left off should be the same as TASK_AND_DEPENDENT, i.e. don't cancel non-transient bg submitted
+ doTestCancellationModeAndSubmitted(true, null, true, true);
+ doTestCancellationModeAndSubmitted(false, null, true, false);
+ // and 'true' should be the same
+ doTestCancellationModeAndSubmitted(true, true, true, true);
+ doTestCancellationModeAndSubmitted(false, true, true, false);
+
+ // cancellation mode false should be the same as DO_NOT_INTERRUPT
+ doTestCancellationModeAndSubmitted(true, false, false, false);
+ }
+
+ public void doTestCancellationModeAndSubmitted(
+ boolean isSubtaskTransient,
+ Object cancellationMode,
+ boolean expectedTaskInterrupted,
+ boolean expectedSubtaskCancelled
+ ) throws Exception {
+ tearDown(); setUp();
+
+ final Task<String> t1 = sayTask("1-wait", Duration.minutes(10), "1-done");
+ if (isSubtaskTransient) {
+ BrooklynTaskTags.addTagDynamically(t1, BrooklynTaskTags.TRANSIENT_TASK_TAG);
+ }
+
+ final Task<List<?>> t = Tasks.parallel(
+ submitting(t1),
+ sayTask("2-wait", Duration.minutes(10), "2-done"));
+ ec.submit(t);
+
+ waitForMessages(Predicates.compose(MathPredicates.greaterThanOrEqual(2), CollectionFunctionals.sizeFunction()), TIMEOUT);
+ Asserts.assertEquals(MutableSet.copyOf(messages), MutableSet.of("1-wait", "2-wait"));
+
+ if (cancellationMode==null) {
+ ((TaskInternal<?>)t).cancel();
+ } else if (cancellationMode instanceof Boolean) {
+ t.cancel((Boolean)cancellationMode);
+ } else if (cancellationMode instanceof TaskCancellationMode) {
+ ((TaskInternal<?>)t).cancel((TaskCancellationMode)cancellationMode);
+ } else {
+ throw new IllegalStateException("Invalid cancellationMode: "+cancellationMode);
+ }
+
+ // the cancelled task always reports cancelled and done
+ Assert.assertEquals(t.isDone(), true);
+ Assert.assertEquals(t.isCancelled(), true);
+ // end time might not be set for another fraction of a second
+ if (expectedTaskInterrupted) {
+ Asserts.eventually(new Supplier<Number>() {
+ @Override public Number get() { return t.getEndTimeUtc(); }},
+ MathPredicates.<Number>greaterThanOrEqual(0));
+ } else {
+ Assert.assertTrue(t.getEndTimeUtc() < 0, "Wrong end time: "+t.getEndTimeUtc());
+ }
+
+ if (expectedSubtaskCancelled) {
+ Asserts.eventually(Suppliers.ofInstance(t1), TaskPredicates.isDone());
+ Assert.assertTrue(t1.isCancelled());
+ Asserts.eventually(new Supplier<Number>() {
+ @Override public Number get() { return t1.getEndTimeUtc(); }},
+ MathPredicates.<Number>greaterThanOrEqual(0));
+ } else {
+ Time.sleep(Duration.millis(5));
+ Assert.assertFalse(t1.isCancelled());
+ Assert.assertFalse(t1.isDone());
+ }
+ }
protected void waitForMessages(Predicate<? super List<String>> predicate, Duration timeout) throws Exception {
long endtime = System.currentTimeMillis() + timeout.toMilliseconds();
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java
index 1d1c3af..6fd3021 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/NonBasicTaskExecutionTest.java
@@ -64,6 +64,11 @@
protected TaskInternal<T> delegate() {
return delegate;
}
+
+ @Override
+ public boolean cancel(TaskCancellationMode mode) {
+ return delegate.cancel(mode);
+ }
}
private BasicExecutionManager em;
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java
index 1d551e8..5c11355 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/ScheduledExecutionTest.java
@@ -45,7 +45,7 @@
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
-@SuppressWarnings({"unchecked","rawtypes"})
+@SuppressWarnings({"rawtypes"})
public class ScheduledExecutionTest {
public static final Logger log = LoggerFactory.getLogger(ScheduledExecutionTest.class);
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java
index fce6f0f..8a25361 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/TaskPredicatesTest.java
@@ -57,8 +57,8 @@
.body(Callables.<Object>returning("val"))
.displayName("myname")
.build());
- assertTrue(TaskPredicates.displayNameMatches(Predicates.equalTo("myname")).apply(task));
- assertFalse(TaskPredicates.displayNameMatches(Predicates.equalTo("wrong")).apply(task));
+ assertTrue(TaskPredicates.displayNameSatisfies(Predicates.equalTo("myname")).apply(task));
+ assertFalse(TaskPredicates.displayNameSatisfies(Predicates.equalTo("wrong")).apply(task));
}
@Test
diff --git a/brooklyn-server/karaf/features/src/main/feature/feature.xml b/brooklyn-server/karaf/features/src/main/feature/feature.xml
index 384bc7d..49a90e6 100644
--- a/brooklyn-server/karaf/features/src/main/feature/feature.xml
+++ b/brooklyn-server/karaf/features/src/main/feature/feature.xml
@@ -197,4 +197,22 @@
<!--<feature version="${project.version}">brooklyn-core</feature>-->
</feature>
+ <feature name="brooklyn-software-winrm" version="${project.version}" description="Brooklyn WinRM Software Entities">
+ <bundle>mvn:org.apache.brooklyn/brooklyn-software-winrm/${project.version}</bundle>
+ <feature>brooklyn-core</feature>
+ <bundle dependency="true">wrap:mvn:io.cloudsoft.windows/winrm4j/${winrm4j.version}</bundle>
+ </feature>
+
+ <feature name="brooklyn-policy" version="${project.version}" description="Brooklyn Policies">
+ <bundle>mvn:org.apache.brooklyn/brooklyn-policy/${project.version}</bundle>
+ <feature>brooklyn-core</feature>
+ </feature>
+
+ <feature name="brooklyn-software-base" version="${project.version}" description="Brooklyn Software Base">
+ <bundle>mvn:org.apache.brooklyn/brooklyn-software-base/${project.version}</bundle>
+ <feature>brooklyn-software-winrm</feature>
+ <feature>brooklyn-policy</feature>
+ <!-- python dependency required but not supported since karaf cannot handle its runtime bindings -->
+ </feature>
+
</features>
diff --git a/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
index 20897d9..05972a2 100644
--- a/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
+++ b/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java
@@ -862,7 +862,7 @@
String scriptContent = ResourceUtils.create(this).getResourceAsString(setupScriptItem);
String script = TemplateProcessor.processTemplateContents(scriptContent, getManagementContext(), substitutions);
if (windows) {
- ((WinRmMachineLocation)machineLocation).executeScript(ImmutableList.copyOf((script.replace("\r", "").split("\n"))));
+ ((WinRmMachineLocation)machineLocation).executeCommand(ImmutableList.copyOf((script.replace("\r", "").split("\n"))));
} else {
((SshMachineLocation)machineLocation).execCommands("Customizing node " + this, ImmutableList.of(script));
}
@@ -2664,7 +2664,7 @@
public Boolean call() {
for (Map.Entry<WinRmMachineLocation, LoginCredentials> entry : machinesToTry.entrySet()) {
WinRmMachineLocation machine = entry.getKey();
- WinRmToolResponse response = machine.executeScript(
+ WinRmToolResponse response = machine.executeCommand(
ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES.getName(), 1),
ImmutableList.of("echo testing"));
boolean success = (response.getStatusCode() == 0);
diff --git a/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java b/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
index d315260..b484359 100644
--- a/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
+++ b/brooklyn-server/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
@@ -20,7 +20,6 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth;
-import groovy.lang.Closure;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -30,8 +29,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.apache.brooklyn.api.catalog.Catalog;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntityLocal;
@@ -55,6 +52,8 @@
import org.apache.brooklyn.util.core.flags.TypeCoercions;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
@@ -62,6 +61,8 @@
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import groovy.lang.Closure;
+
/**
* Policy that is attached to a {@link Resizable} entity and dynamically adjusts its size in response to
@@ -290,6 +291,7 @@
.defaultValue(1)
.reconfigurable(true)
.build();
+
@SetFromFlag("resizeUpIterationMax")
public static final ConfigKey<Integer> RESIZE_UP_ITERATION_MAX = BasicConfigKey.builder(Integer.class)
.name("autoscaler.resizeUpIterationMax")
@@ -297,6 +299,7 @@
.description("Maximum change to the size on a single iteration when scaling up")
.reconfigurable(true)
.build();
+
@SetFromFlag("resizeDownIterationIncrement")
public static final ConfigKey<Integer> RESIZE_DOWN_ITERATION_INCREMENT = BasicConfigKey.builder(Integer.class)
.name("autoscaler.resizeDownIterationIncrement")
@@ -304,6 +307,7 @@
.defaultValue(1)
.reconfigurable(true)
.build();
+
@SetFromFlag("resizeDownIterationMax")
public static final ConfigKey<Integer> RESIZE_DOWN_ITERATION_MAX = BasicConfigKey.builder(Integer.class)
.name("autoscaler.resizeDownIterationMax")
@@ -346,6 +350,12 @@
.reconfigurable(true)
.build();
+ public static final ConfigKey<Integer> INSUFFICIENT_CAPACITY_HIGH_WATER_MARK = BasicConfigKey.builder(Integer.class)
+ .name("autoscaler.insufficientCapacityHighWaterMark")
+ .defaultValue(null)
+ .reconfigurable(true)
+ .build();
+
@SetFromFlag("resizeOperator")
public static final ConfigKey<ResizeOperator> RESIZE_OPERATOR = BasicConfigKey.builder(ResizeOperator.class)
.name("autoscaler.resizeOperator")
@@ -564,6 +574,10 @@
return getConfig(MAX_POOL_SIZE);
}
+ private Integer getInsufficientCapacityHighWaterMark() {
+ return getConfig(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK);
+ }
+
private ResizeOperator getResizeOperator() {
return getConfig(RESIZE_OPERATOR);
}
@@ -620,6 +634,14 @@
throw new IllegalArgumentException("Min pool size "+val+" must not be greater than max pool size "+getConfig(MAX_POOL_SIZE));
}
onPoolSizeLimitsChanged(getConfig(MIN_POOL_SIZE), newMax);
+ } else if (key.equals(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK)) {
+ Integer newVal = (Integer) val;
+ Integer oldVal = config().get(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK);
+ if (oldVal != null && (newVal == null || newVal > oldVal)) {
+ LOG.info("{} resetting {} to {}, which will enable resizing above previous level of {}",
+ new Object[] {AutoScalerPolicy.this, INSUFFICIENT_CAPACITY_HIGH_WATER_MARK.getName(), newVal, oldVal});
+ // TODO see above about changing metricLowerBound; not triggering resize now
+ }
} else {
throw new UnsupportedOperationException("reconfiguring "+key+" unsupported for "+this);
}
@@ -848,9 +870,17 @@
onNewUnboundedPoolSize(desiredSizeUnconstrained);
}
+ private int applyMinMaxConstraints(long desiredSize) {
+ return applyMinMaxConstraints(desiredSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)desiredSize);
+ }
+
private int applyMinMaxConstraints(int desiredSize) {
- desiredSize = Math.max(getMinPoolSize(), desiredSize);
- desiredSize = Math.min(getMaxPoolSize(), desiredSize);
+ int minSize = getMinPoolSize();
+ int maxSize = getMaxPoolSize();
+ Integer insufficientCapacityHighWaterMark = getInsufficientCapacityHighWaterMark();
+ desiredSize = Math.max(minSize, desiredSize);
+ desiredSize = Math.min(maxSize, desiredSize);
+ if (insufficientCapacityHighWaterMark != null) desiredSize = Math.min(insufficientCapacityHighWaterMark, desiredSize);
return desiredSize;
}
@@ -989,36 +1019,47 @@
}
private void resizeNow() {
- long currentPoolSize = getCurrentSizeOperator().apply(poolEntity);
+ final int currentPoolSize = getCurrentSizeOperator().apply(poolEntity);
CalculatedDesiredPoolSize calculatedDesiredPoolSize = calculateDesiredPoolSize(currentPoolSize);
- final long desiredPoolSize = calculatedDesiredPoolSize.size;
+ long desiredPoolSize = calculatedDesiredPoolSize.size;
boolean stable = calculatedDesiredPoolSize.stable;
+ final int targetPoolSize = applyMinMaxConstraints(desiredPoolSize);
+
if (!stable) {
// the desired size fluctuations are not stable; ensure we check again later (due to time-window)
// even if no additional events have been received
// (note we continue now with as "good" a resize as we can given the instability)
if (LOG.isTraceEnabled()) LOG.trace("{} re-scheduling resize check for {}, as desired size not stable (current {}, desired {}); continuing with resize...",
- new Object[] {this, poolEntity, currentPoolSize, desiredPoolSize});
+ new Object[] {this, poolEntity, currentPoolSize, targetPoolSize});
scheduleResize();
}
- if (currentPoolSize == desiredPoolSize) {
+ if (currentPoolSize == targetPoolSize) {
if (LOG.isTraceEnabled()) LOG.trace("{} not resizing pool {} from {} to {}",
- new Object[] {this, poolEntity, currentPoolSize, desiredPoolSize});
+ new Object[] {this, poolEntity, currentPoolSize, targetPoolSize});
return;
}
if (LOG.isDebugEnabled()) LOG.debug("{} requesting resize to {}; current {}, min {}, max {}",
- new Object[] {this, desiredPoolSize, currentPoolSize, getMinPoolSize(), getMaxPoolSize()});
+ new Object[] {this, targetPoolSize, currentPoolSize, getMinPoolSize(), getMaxPoolSize()});
Entities.submit(entity, Tasks.<Void>builder().displayName("Auto-scaler")
- .description("Auto-scaler recommending resize from "+currentPoolSize+" to "+desiredPoolSize)
+ .description("Auto-scaler recommending resize from "+currentPoolSize+" to "+targetPoolSize)
.tag(BrooklynTaskTags.NON_TRANSIENT_TASK_TAG)
.body(new Callable<Void>() {
@Override
public Void call() throws Exception {
// TODO Should we use int throughout, rather than casting here?
- getResizeOperator().resize(poolEntity, (int) desiredPoolSize);
+ try {
+ getResizeOperator().resize(poolEntity, (int) targetPoolSize);
+ } catch (Resizable.InsufficientCapacityException e) {
+ // cannot resize beyond this; set the high-water mark
+ int insufficientCapacityHighWaterMark = getCurrentSizeOperator().apply(poolEntity);
+ LOG.warn("{} failed to resize {} due to insufficient capacity; setting high-water mark to {}, "
+ + "and will not attempt to resize above that level again",
+ new Object[] {AutoScalerPolicy.this, poolEntity, insufficientCapacityHighWaterMark});
+ config().set(INSUFFICIENT_CAPACITY_HIGH_WATER_MARK, insufficientCapacityHighWaterMark);
+ }
return null;
}
}).build())
diff --git a/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java b/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java
index e04f714..ad67b75 100644
--- a/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java
+++ b/brooklyn-server/policy/src/test/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicyMetricTest.java
@@ -36,17 +36,18 @@
import org.apache.brooklyn.core.test.entity.TestCluster;
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.time.Duration;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
public class AutoScalerPolicyMetricTest {
- private static long TIMEOUT_MS = 10000;
- private static long SHORT_WAIT_MS = 50;
+ private static long SHORT_WAIT_MS = 250;
private static final AttributeSensor<Integer> MY_ATTRIBUTE = Sensors.newIntegerSensor("autoscaler.test.intAttrib");
TestApplication app;
@@ -75,7 +76,7 @@
Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 1));
tc.sensors().set(MY_ATTRIBUTE, 101);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
}
@Test
@@ -89,7 +90,7 @@
Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
tc.sensors().set(MY_ATTRIBUTE, 49);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 1));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 1));
}
@Test(groups="Integration")
@@ -101,11 +102,11 @@
// workload 200 so requires doubling size to 10 to handle: (200*5)/100 = 10
tc.sensors().set(MY_ATTRIBUTE, 200);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 10));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 10));
// workload 5, requires 1 entity: (10*110)/100 = 11
tc.sensors().set(MY_ATTRIBUTE, 110);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 11));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 11));
}
@Test(groups="Integration")
@@ -117,14 +118,14 @@
// workload can be handled by 4 servers, within its valid range: (49*5)/50 = 4.9
tc.sensors().set(MY_ATTRIBUTE, 49);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 4));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 4));
// workload can be handled by 4 servers, within its valid range: (25*4)/50 = 2
tc.sensors().set(MY_ATTRIBUTE, 25);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
tc.sensors().set(MY_ATTRIBUTE, 0);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 1));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 1));
}
@Test(groups="Integration")
@@ -139,11 +140,11 @@
// Decreases to min-size only
tc.sensors().set(MY_ATTRIBUTE, 0);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
// Increases to max-size only
tc.sensors().set(MY_ATTRIBUTE, 100000);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 6));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 6));
}
@Test(groups="Integration",invocationCount=20)
@@ -167,14 +168,14 @@
// workload can be handled by 6 servers, so no need to notify: 6 <= (100*6)/50
tc.sensors().set(MY_ATTRIBUTE, 600);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 6));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 6));
assertTrue(maxReachedEvents.isEmpty());
// Increases to above max capacity: would require (100000*6)/100 = 6000
tc.sensors().set(MY_ATTRIBUTE, 100000);
// Assert our listener gets notified (once)
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() {
+ Asserts.succeedsEventually(new Runnable() {
public void run() {
assertEquals(maxReachedEvents.size(), 1);
assertEquals(maxReachedEvents.get(0).getMaxAllowed(), 6);
@@ -245,7 +246,7 @@
policy.suspend();
policy.resume();
tc.sensors().set(MY_ATTRIBUTE, 101);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
}
@Test
@@ -268,6 +269,84 @@
// Then confirm we listen to the correct "entityWithMetric"
entityWithMetric.sensors().set(TestEntity.SEQUENCE, 101);
- Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), currentSizeAsserter(tc, 2));
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
+ }
+
+ @Test(groups="Integration")
+ public void testOnFailedGrowWillSetHighwaterMarkAndNotResizeAboveThatAgain() {
+ tc = app.createAndManageChild(EntitySpec.create(TestCluster.class)
+ .configure("initialSize", 0)
+ .configure(TestCluster.MAX_SIZE, 2));
+
+ tc.resize(1);
+
+ tc.policies().add(AutoScalerPolicy.builder()
+ .metric(MY_ATTRIBUTE)
+ .metricLowerBound(50)
+ .metricUpperBound(100)
+ .buildSpec());
+
+ // workload 200 so requires doubling size to 2 to handle: (200*1)/100 = 2
+ tc.sensors().set(MY_ATTRIBUTE, 200);
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
+ assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2), "desired="+tc.getDesiredSizeHistory());
+ assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
+ tc.sensors().set(MY_ATTRIBUTE, 100);
+
+ // workload 110, requires 1 more entity: (2*110)/100 = 2.1
+ // But max size is 2, so resize will fail.
+ tc.sensors().set(MY_ATTRIBUTE, 110);
+ Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
+ assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3), "desired="+tc.getDesiredSizeHistory()); // TODO succeeds eventually?
+ assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
+
+ // another attempt to resize will not cause it to ask
+ tc.sensors().set(MY_ATTRIBUTE, 110);
+ Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
+ assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3), "desired="+tc.getDesiredSizeHistory());
+ assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
+
+ // but if we shrink down again, then we'll still be able to go up to the previous level.
+ // We'll only try to go as high as the previous high-water mark though.
+ tc.sensors().set(MY_ATTRIBUTE, 1);
+ Asserts.succeedsEventually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 1));
+ assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3, 1), "desired="+tc.getDesiredSizeHistory());
+ assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2, 1), "sizes="+tc.getSizeHistory());
+
+ tc.sensors().set(MY_ATTRIBUTE, 10000);
+ Asserts.succeedsEventually(ImmutableMap.of("timeout", SHORT_WAIT_MS), currentSizeAsserter(tc, 2));
+ assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3, 1, 2), "desired="+tc.getDesiredSizeHistory());
+ assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2, 1, 2), "sizes="+tc.getSizeHistory());
+ }
+
+ // When there is a resizeUpStabilizationDelay, it remembers all the previously requested sizes (in the recent history)
+ // and then looks at those in the stabilization-delay to determine the sustained desired. This test checks that
+ // we apply the highwater-mark even when the desired size had been recorded prior to the highwater mark being
+ // discovered.
+ @Test(groups="Integration")
+ public void testOnFailedGrowWithStabilizationDelayWillSetHighwaterMarkAndNotResizeAboveThatAgain() throws Exception {
+ tc = app.createAndManageChild(EntitySpec.create(TestCluster.class)
+ .configure("initialSize", 0)
+ .configure(TestCluster.MAX_SIZE, 2));
+
+ tc.resize(1);
+
+ tc.policies().add(AutoScalerPolicy.builder()
+ .metric(MY_ATTRIBUTE)
+ .metricLowerBound(50)
+ .metricUpperBound(100)
+ .resizeUpStabilizationDelay(Duration.ONE_SECOND)
+ .buildSpec());
+
+ // workload 200 so requires doubling size to 2 to handle: (200*1)/100 = 2
+ for (int i = 0; i < 10; i++) {
+ tc.sensors().set(MY_ATTRIBUTE, 200 + (i*100));
+ Thread.sleep(100);
+ }
+
+ Asserts.succeedsEventually(currentSizeAsserter(tc, 2));
+ Asserts.succeedsContinually(currentSizeAsserter(tc, 2));
+ assertEquals(tc.getDesiredSizeHistory(), ImmutableList.of(1, 2, 3), "desired="+tc.getDesiredSizeHistory());
+ assertEquals(tc.getSizeHistory(), ImmutableList.of(0, 1, 2), "sizes="+tc.getSizeHistory());
}
}
diff --git a/brooklyn-server/pom.xml b/brooklyn-server/pom.xml
index 59bf6b2..4e6012b 100644
--- a/brooklyn-server/pom.xml
+++ b/brooklyn-server/pom.xml
@@ -90,7 +90,7 @@
<coverage.target>${working.dir}</coverage.target>
<!-- Dependency Versions -->
- <jclouds.version>1.9.1</jclouds.version> <!-- JCLOUDS_VERSION -->
+ <jclouds.version>1.9.2</jclouds.version> <!-- JCLOUDS_VERSION -->
<logback.version>1.0.7</logback.version>
<slf4j.version>1.6.6</slf4j.version> <!-- used for java.util.logging jul-to-slf4j interception -->
<guava.version>17.0</guava.version>
@@ -146,7 +146,7 @@
<jsr311-api.version>1.1.1</jsr311-api.version>
<maxmind.version>0.8.1</maxmind.version>
<jna.version>4.0.0</jna.version>
- <winrm4j.version>0.1.0</winrm4j.version>
+ <winrm4j.version>0.2.0</winrm4j.version>
<!-- Transitive dependencies, declared explicitly to avoid version mismatch -->
<clojure.version>1.4.0</clojure.version>
diff --git a/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java b/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java
index fb43c4c..d011172 100644
--- a/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java
+++ b/brooklyn-server/rest/rest-client/src/main/java/org/apache/brooklyn/rest/client/util/http/BuiltResponsePreservingError.java
@@ -70,7 +70,9 @@
@Override
public Object getEntity() {
- if (error!=null) Exceptions.propagate(error);
+ if (error!=null) {
+ throw new IllegalStateException("getEntity called on BuiltResponsePreservingError, where an Error had been preserved", error);
+ }
return super.getEntity();
}
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index 01bf992..82bac22 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@ -124,7 +124,12 @@
Map<String,Object> result = MutableMap.of();
for (CatalogItem<?,?> item: items) {
- result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item));
+ try {
+ result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item));
+ } catch (Throwable t) {
+ log.warn("Error loading catalog item '"+item+"' (rethrowing): "+t);
+ throw Exceptions.propagate(t);
+ }
}
return Response.status(Status.CREATED).entity(result).build();
}
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
index e483d0b..a6d3b8e 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/EntityConfigResource.java
@@ -22,6 +22,7 @@
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Callable;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.config.ConfigKey;
@@ -35,6 +36,7 @@
import org.apache.brooklyn.rest.transform.EntityTransformer;
import org.apache.brooklyn.rest.util.WebResourceUtils;
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.text.Strings;
import org.apache.brooklyn.util.time.Duration;
@@ -71,14 +73,32 @@
public Map<String, Object> batchConfigRead(String application, String entityToken, Boolean raw) {
// TODO: add test
Entity entity = brooklyn().getEntity(application, entityToken);
- Map<ConfigKey<?>, ?> source = ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
- Map<String, Object> result = Maps.newLinkedHashMap();
- for (Map.Entry<ConfigKey<?>, ?> ek : source.entrySet()) {
- Object value = ek.getValue();
- result.put(ek.getKey().getName(),
- resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(ek.getKey()).resolve());
+ // wrap in a task for better runtime view
+ return Entities.submit(entity, Tasks.<Map<String,Object>>builder().displayName("REST API batch config read").body(new BatchConfigRead(this, entity, raw)).build()).getUnchecked();
+ }
+
+ private static class BatchConfigRead implements Callable<Map<String,Object>> {
+ private EntityConfigResource resource;
+ private Entity entity;
+ private Boolean raw;
+
+ public BatchConfigRead(EntityConfigResource resource, Entity entity, Boolean raw) {
+ this.resource = resource;
+ this.entity = entity;
+ this.raw = raw;
}
- return result;
+
+ @Override
+ public Map<String, Object> call() throws Exception {
+ Map<ConfigKey<?>, ?> source = ((EntityInternal) entity).config().getBag().getAllConfigAsConfigKeyMap();
+ Map<String, Object> result = Maps.newLinkedHashMap();
+ for (Map.Entry<ConfigKey<?>, ?> ek : source.entrySet()) {
+ Object value = ek.getValue();
+ result.put(ek.getKey().getName(),
+ resource.resolving(value).preferJson(true).asJerseyOutermostReturnValue(false).raw(raw).context(entity).timeout(Duration.ZERO).renderAs(ek.getKey()).resolve());
+ }
+ return result;
+ }
}
@Override
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
index a9fa4dc..c029bd3 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/resources/ServerResource.java
@@ -218,6 +218,7 @@
if (shutdownHandler != null) {
shutdownHandler.onShutdownRequest();
} else {
+ // should normally be set, as @Context is required by jersey injection
log.warn("ShutdownHandler not set, exiting process");
System.exit(0);
}
diff --git a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
index 38a38eb..978755b 100644
--- a/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
+++ b/brooklyn-server/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
@@ -93,9 +93,12 @@
}
}
- Builder rb = ApiError.builderFromThrowable(Exceptions.collapse(throwable2));
+ Throwable throwable3 = Exceptions.collapse(throwable2);
+ Builder rb = ApiError.builderFromThrowable(throwable3);
if (Strings.isBlank(rb.getMessage()))
rb.message("Internal error. Contact server administrator to consult logs for more details.");
+ if (Exceptions.isPrefixImportant(throwable3))
+ rb.message(Exceptions.getPrefixText(throwable3)+": "+rb.getMessage());
return rb.build().asResponse(Status.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON_TYPE);
}
}
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
index f641267..92ac8f7 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -48,8 +48,8 @@
import org.apache.brooklyn.rest.security.provider.SecurityProvider;
import org.apache.brooklyn.rest.util.ManagementContextProvider;
import org.apache.brooklyn.rest.util.OsgiCompat;
+import org.apache.brooklyn.rest.util.ServerStoppingShutdownHandler;
import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
-import org.apache.brooklyn.rest.util.TestShutdownHandler;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.net.Networking;
@@ -94,7 +94,7 @@
public static final String SCANNING_CATALOG_BOM_URL = "classpath://brooklyn/scanning.catalog.bom";
enum StartMode {
- FILTER, SERVLET, WEB_XML
+ FILTER, SERVLET, /** web-xml is not fully supported */ @Beta WEB_XML
}
public static final List<Class<? extends Filter>> DEFAULT_FILTERS = ImmutableList.of(
@@ -112,7 +112,7 @@
private ContextHandler customContext;
private boolean deployJsgui = true;
private boolean disableHighAvailability = true;
- private final TestShutdownHandler shutdownListener = new TestShutdownHandler();
+ private ServerStoppingShutdownHandler shutdownListener;
protected BrooklynRestApiLauncher() {}
@@ -215,7 +215,12 @@
((BrooklynProperties) mgmt.getConfig()).put(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL);
}
- return startServer(mgmt, context, summary, disableHighAvailability);
+ Server server = startServer(mgmt, context, summary, disableHighAvailability);
+ if (shutdownListener!=null) {
+ // not available in some modes, eg webapp
+ shutdownListener.setServer(server);
+ }
+ return server;
}
private ContextHandler filterContextHandler(ManagementContext mgmt) {
@@ -241,7 +246,7 @@
ResourceConfig config = new DefaultResourceConfig();
for (Object r: BrooklynRestApi.getAllResources())
config.getSingletons().add(r);
- config.getSingletons().add(new ShutdownHandlerProvider(shutdownListener));
+ addShutdownListener(config, mgmt);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT, managementContext);
@@ -253,6 +258,7 @@
return context;
}
+ /** NB: not fully supported; use one of the other {@link StartMode}s */
private ContextHandler webXmlContextHandler(ManagementContext mgmt) {
// TODO add security to web.xml
WebAppContext context;
@@ -266,12 +272,15 @@
throw new IllegalStateException("Cannot find WAR for REST API. Expected in target/*.war, Maven repo, or in source directories.");
}
context.setAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT, mgmt);
-
+ // TODO shutdown hook
+
return context;
}
/** starts a server, on all NICs if security is configured,
- * otherwise (no security) only on loopback interface */
+ * otherwise (no security) only on loopback interface
+ * @deprecated since 0.9.0 becoming private */
+ @Deprecated
public static Server startServer(ManagementContext mgmt, ContextHandler context, String summary, boolean disableHighAvailability) {
// TODO this repeats code in BrooklynLauncher / WebServer. should merge the two paths.
boolean secure = mgmt != null && !BrooklynWebConfig.hasNoSecurityOptions(mgmt.getConfig());
@@ -292,8 +301,11 @@
return startServer(context, summary, bindLocation);
}
+ /** @deprecated since 0.9.0 becoming private */
+ @Deprecated
public static Server startServer(ContextHandler context, String summary, InetSocketAddress bindLocation) {
Server server = new Server(bindLocation);
+
server.setHandler(context);
try {
server.start();
@@ -361,6 +373,12 @@
ManagementContext mgmt = OsgiCompat.getManagementContext(context);
config.getSingletons().add(new ManagementContextProvider(mgmt));
+ addShutdownListener(config, mgmt);
+ }
+
+ protected synchronized void addShutdownListener(ResourceConfig config, ManagementContext mgmt) {
+ if (shutdownListener!=null) throw new IllegalStateException("Can only retrieve one shutdown listener");
+ shutdownListener = new ServerStoppingShutdownHandler(mgmt);
config.getSingletons().add(new ShutdownHandlerProvider(shutdownListener));
}
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
index dea8bca..e63a239 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java
@@ -47,7 +47,7 @@
import org.apache.brooklyn.rest.util.NullHttpServletRequestProvider;
import org.apache.brooklyn.rest.util.NullServletConfigProvider;
import org.apache.brooklyn.rest.util.ShutdownHandlerProvider;
-import org.apache.brooklyn.rest.util.TestShutdownHandler;
+import org.apache.brooklyn.rest.util.NoOpRecordingShutdownHandler;
import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
import org.apache.brooklyn.util.exceptions.Exceptions;
@@ -56,7 +56,7 @@
protected ManagementContext manager;
protected boolean useLocalScannedCatalog = false;
- protected TestShutdownHandler shutdownListener = createShutdownHandler();
+ protected NoOpRecordingShutdownHandler shutdownListener = createShutdownHandler();
@BeforeMethod(alwaysRun = true)
public void setUpMethod() {
@@ -69,8 +69,8 @@
useLocalScannedCatalog = true;
}
- private TestShutdownHandler createShutdownHandler() {
- return new TestShutdownHandler();
+ private NoOpRecordingShutdownHandler createShutdownHandler() {
+ return new NoOpRecordingShutdownHandler();
}
protected synchronized ManagementContext getManagementContext() {
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/NoOpRecordingShutdownHandler.java
similarity index 94%
rename from brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
rename to brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/NoOpRecordingShutdownHandler.java
index 87fbb24..a99d3d9 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/NoOpRecordingShutdownHandler.java
@@ -20,7 +20,7 @@
import org.apache.brooklyn.rest.util.ShutdownHandler;
-public class TestShutdownHandler implements ShutdownHandler {
+public class NoOpRecordingShutdownHandler implements ShutdownHandler {
private volatile boolean isRequested;
@Override
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/ServerStoppingShutdownHandler.java b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/ServerStoppingShutdownHandler.java
new file mode 100644
index 0000000..fc3bbc4
--- /dev/null
+++ b/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/ServerStoppingShutdownHandler.java
@@ -0,0 +1,75 @@
+/*
+ * 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.rest.util;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.eclipse.jetty.server.Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/* not the cleanest way to enforce a clean shutdown, but a simple and effective way;
+ * usage is restricted to BrooklynRestApiLauncher and subclasses, to stop it inline.
+ * the main app stops the server in a more principled way using callbacks. */
+public class ServerStoppingShutdownHandler implements ShutdownHandler {
+
+ private static final Logger log = LoggerFactory.getLogger(ServerStoppingShutdownHandler.class);
+
+ private final ManagementContext mgmt;
+ private Server server;
+
+ public ServerStoppingShutdownHandler(ManagementContext mgmt) {
+ this.mgmt = mgmt;
+ }
+
+ @Override
+ public void onShutdownRequest() {
+ log.info("Shutting down server (when running in rest-api dev mode, using background thread)");
+
+ // essentially same as BrooklynLauncher.terminate() but cut down ...
+ // NB: this is only used in dev mode use of BrooklynJavascriptGuiLauncher
+ new Thread(new Runnable() {
+ public void run() {
+ Time.sleep(Duration.millis(250));
+ log.debug("Shutting down server in background thread, closing "+server+" and "+mgmt);
+ if (server!=null) {
+ try {
+ server.stop();
+ server.join();
+ } catch (Exception e) {
+ log.debug("Stopping server gave an error (not usually a concern): "+e);
+ /* NPE may be thrown e.g. if threadpool not started */
+ }
+ }
+
+ if (mgmt instanceof ManagementContextInternal) {
+ ((ManagementContextInternal)mgmt).terminate();
+ }
+ }
+ }).start();
+ }
+
+ /** Expect this to be injected; typically it is not known when this is created, but we need it to trigger shutdown. */
+ public void setServer(Server server) {
+ this.server = server;
+ }
+
+}
diff --git a/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java b/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
index 1c66a04..03be76e 100644
--- a/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
+++ b/brooklyn-server/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
@@ -19,6 +19,12 @@
package org.apache.brooklyn.cli;
import static com.google.common.base.Preconditions.checkNotNull;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyShell;
+import io.airlift.command.Cli;
+import io.airlift.command.Cli.CliBuilder;
+import io.airlift.command.Command;
+import io.airlift.command.Option;
import java.io.Console;
import java.io.IOException;
@@ -32,17 +38,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.Beta;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Stopwatch;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
import org.apache.brooklyn.api.catalog.BrooklynCatalog;
import org.apache.brooklyn.api.catalog.CatalogItem;
import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
@@ -89,16 +84,19 @@
import org.apache.brooklyn.util.javalang.Enums;
import org.apache.brooklyn.util.net.Networking;
import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import groovy.lang.GroovyClassLoader;
-import groovy.lang.GroovyShell;
-import io.airlift.command.Cli;
-import io.airlift.command.Cli.CliBuilder;
-import io.airlift.command.Command;
-import io.airlift.command.Option;
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
/**
* This class is the primary CLI for brooklyn.
@@ -474,17 +472,23 @@
}
BrooklynServerDetails server = launcher.getServerDetails();
- ManagementContext ctx = server.getManagementContext();
+ ManagementContext mgmt = server.getManagementContext();
if (verbose) {
Entities.dumpInfo(launcher.getApplications());
}
if (!exitAndLeaveAppsRunningAfterStarting) {
- waitAfterLaunch(ctx, shutdownHandler);
+ waitAfterLaunch(mgmt, shutdownHandler);
}
- // will call mgmt.terminate() in BrooklynShutdownHookJob
+ // do not shutdown servers here here --
+ // the BrooklynShutdownHookJob will invoke that and others on System.exit()
+ // which happens immediately after.
+ // might be nice to do it explicitly here,
+ // but the server shutdown process has some special "shutdown apps" options
+ // so we'd want to refactor BrooklynShutdownHookJob to share code
+
return null;
}
diff --git a/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom b/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom
index 483f04d..97dc8a7 100644
--- a/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom
+++ b/brooklyn-server/server-cli/src/main/resources/brooklyn/default.catalog.bom
@@ -181,6 +181,8 @@
brooklyn.config:
my.message: $brooklyn:formatString("connected to Riak at %s",
$brooklyn:entity("riak-cluster").attributeWhenReady("main.uri"))
+ # and clear the location defined there so it is taken from this template
+ locations: []
# use the off-the-shelf Riak cluster
- type: org.apache.brooklyn.entity.nosql.riak.RiakCluster
@@ -240,6 +242,8 @@
# and a lot of sensors defined
type: 2-bash-web-server-template
name: My Bash Web Server VM with Sensors
+ # and clear the location defined there so it is taken from this template
+ locations: []
brooklyn.config:
my.message: "part of the cluster"
@@ -332,6 +336,8 @@
# point this load balancer at the cluster, specifying port to forward to
loadbalancer.serverpool: $brooklyn:entity("my-web-cluster")
member.sensor.portNumber: app.port
+ # disable sticky sessions to allow easy validation of balancing via browser refresh
+ nginx.sticky: false
brooklyn.enrichers:
# publish a few useful info sensors and KPI's to the root of the app
diff --git a/brooklyn-server/software/base/pom.xml b/brooklyn-server/software/base/pom.xml
index 556e982..51ae966 100644
--- a/brooklyn-server/software/base/pom.xml
+++ b/brooklyn-server/software/base/pom.xml
@@ -163,6 +163,10 @@
<configuration>
<instructions>
<Include-Resource>{maven-resources}, target/classes/brooklyn-jmxmp-agent-shaded-${project.version}.jar, target/classes/brooklyn-jmxrmi-agent-${project.version}.jar</Include-Resource>
+ <Import-Package>
+ !org.python.core,
+ *
+ </Import-Package>
</instructions>
</configuration>
</plugin>
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
index 59dac4f..d243833 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessDriver.java
@@ -107,23 +107,22 @@
skipStart = entityStarted.or(false);
}
if (!skipStart) {
- DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
- waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
- copyPreInstallResources();
- }});
-
- DynamicTasks.queue("pre-install", new Runnable() { public void run() {
- preInstall();
- }});
-
- DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
- runPreInstallCommand();
- }});
-
Optional<Boolean> locationInstalled = Optional.fromNullable(getLocation().getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
Optional<Boolean> entityInstalled = Optional.fromNullable(entity.getConfig(BrooklynConfigKeys.SKIP_ENTITY_INSTALLATION));
boolean skipInstall = locationInstalled.or(entityInstalled).or(false);
if (!skipInstall) {
+ DynamicTasks.queue("copy-pre-install-resources", new Runnable() { public void run() {
+ waitForConfigKey(BrooklynConfigKeys.PRE_INSTALL_RESOURCES_LATCH);
+ copyPreInstallResources();
+ }});
+
+ DynamicTasks.queue("pre-install", new Runnable() { public void run() {
+ preInstall();
+ }});
+
+ DynamicTasks.queue("pre-install-command", new Runnable() { public void run() {
+ runPreInstallCommand();
+ }});
DynamicTasks.queue("setup", new Runnable() { public void run() {
waitForConfigKey(BrooklynConfigKeys.SETUP_LATCH);
setup();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index 6a39cc2..de88cba 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -48,7 +48,6 @@
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.time.Duration;
-import org.python.core.PyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -239,7 +238,7 @@
if (Strings.isBlank(regularCommand)) {
response = getLocation().executePsScript(ImmutableList.of(powerShellCommand));
} else {
- response = getLocation().executeScript(ImmutableList.of(regularCommand));
+ response = getLocation().executeCommand(ImmutableList.of(regularCommand));
}
if (currentTask != null) {
@@ -259,7 +258,7 @@
}
public int execute(List<String> script) {
- return getLocation().executeScript(script).getStatusCode();
+ return getLocation().executeCommand(script).getStatusCode();
}
public int executePsScriptNoRetry(List<String> psScript) {
@@ -281,8 +280,9 @@
public void rebootAndWait() {
try {
executePsScriptNoRetry(ImmutableList.of("Restart-Computer -Force"));
- } catch (PyException e) {
+ } catch (Exception e) {
// Restarting the computer will cause the command to fail; ignore the exception and continue
+ Exceptions.propagateIfFatal(e);
}
waitForWinRmStatus(false, entity.getConfig(VanillaWindowsProcess.REBOOT_BEGUN_TIMEOUT));
waitForWinRmStatus(true, entity.getConfig(VanillaWindowsProcess.REBOOT_COMPLETED_TIMEOUT)).getWithError();
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java
index 98a97c0..3fd694c 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcess.java
@@ -19,10 +19,14 @@
package org.apache.brooklyn.entity.software.base;
import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
@ImplementedBy(EmptySoftwareProcessImpl.class)
public interface EmptySoftwareProcess extends SoftwareProcess {
+ ConfigKey<Boolean> USE_SSH_MONITORING = ConfigKeys.newConfigKey("sshMonitoring.enabled", "SSH monitoring enabled", Boolean.TRUE);
+
public SoftwareProcessDriver getDriver();
}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java
index 154a08a..7330461 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptySoftwareProcessImpl.java
@@ -18,6 +18,8 @@
*/
package org.apache.brooklyn.entity.software.base;
+import org.apache.brooklyn.core.entity.Attributes;
+
public class EmptySoftwareProcessImpl extends SoftwareProcessImpl implements EmptySoftwareProcess {
@Override
@@ -28,7 +30,11 @@
@Override
protected void connectSensors() {
super.connectSensors();
- connectServiceUpIsRunning();
+ if (isSshMonitoringEnabled()) {
+ connectServiceUpIsRunning();
+ } else {
+ sensors().set(Attributes.SERVICE_UP, true);
+ }
}
@Override
@@ -36,4 +42,8 @@
disconnectServiceUpIsRunning();
super.disconnectSensors();
}
+
+ protected boolean isSshMonitoringEnabled() {
+ return Boolean.TRUE.equals(getConfig(USE_SSH_MONITORING));
+ }
}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcess.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcess.java
new file mode 100644
index 0000000..770ba0d
--- /dev/null
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcess.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.entity.software.base;
+
+import java.util.Collection;
+
+import org.apache.brooklyn.api.entity.ImplementedBy;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+
+import com.google.common.collect.ImmutableSet;
+
+@ImplementedBy(EmptyWindowsProcessImpl.class)
+public interface EmptyWindowsProcess extends SoftwareProcess {
+
+ // 3389 is RDP; 5985 is WinRM (3389 isn't used by Brooklyn, but useful for the end-user subsequently)
+ ConfigKey<Collection<Integer>> REQUIRED_OPEN_LOGIN_PORTS = ConfigKeys.newConfigKeyWithDefault(
+ SoftwareProcess.REQUIRED_OPEN_LOGIN_PORTS,
+ ImmutableSet.of(5985, 3389));
+
+ ConfigKey<Boolean> USE_WINRM_MONITORING = ConfigKeys.newConfigKey("winrmMonitoring.enabled", "WinRM monitoring enabled", Boolean.TRUE);
+}
diff --git a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessDriver.java
similarity index 65%
copy from brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
copy to brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessDriver.java
index 87fbb24..d6e1c99 100644
--- a/brooklyn-server/rest/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessDriver.java
@@ -16,24 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.brooklyn.rest.util;
+package org.apache.brooklyn.entity.software.base;
-import org.apache.brooklyn.rest.util.ShutdownHandler;
-
-public class TestShutdownHandler implements ShutdownHandler {
- private volatile boolean isRequested;
-
- @Override
- public void onShutdownRequest() {
- isRequested = true;
- }
-
- public boolean isRequested() {
- return isRequested;
- }
-
- public void reset() {
- isRequested = false;
- }
-
+public interface EmptyWindowsProcessDriver extends SoftwareProcessDriver {
}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessImpl.java
new file mode 100644
index 0000000..ae87774
--- /dev/null
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessImpl.java
@@ -0,0 +1,49 @@
+/*
+ * 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.entity.software.base;
+
+import org.apache.brooklyn.core.entity.Attributes;
+
+public class EmptyWindowsProcessImpl extends SoftwareProcessImpl implements EmptyWindowsProcess {
+
+ @Override
+ public Class<?> getDriverInterface() {
+ return EmptyWindowsProcessDriver.class;
+ }
+
+ @Override
+ protected void connectSensors() {
+ super.connectSensors();
+ if (isWinrmMonitoringEnabled()) {
+ connectServiceUpIsRunning();
+ } else {
+ sensors().set(Attributes.SERVICE_UP, true);
+ }
+ }
+
+ @Override
+ protected void disconnectSensors() {
+ disconnectServiceUpIsRunning();
+ super.disconnectSensors();
+ }
+
+ protected boolean isWinrmMonitoringEnabled() {
+ return Boolean.TRUE.equals(getConfig(USE_WINRM_MONITORING));
+ }
+}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessWinRmDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessWinRmDriver.java
new file mode 100644
index 0000000..4a9a054
--- /dev/null
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/EmptyWindowsProcessWinRmDriver.java
@@ -0,0 +1,97 @@
+/*
+ * 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.entity.software.base;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.net.UserAndHostAndPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EmptyWindowsProcessWinRmDriver extends AbstractSoftwareProcessWinRmDriver implements VanillaWindowsProcessDriver {
+ @SuppressWarnings("unused")
+ private static final Logger LOG = LoggerFactory.getLogger(EmptyWindowsProcessWinRmDriver.class);
+
+ private final AtomicBoolean running = new AtomicBoolean();
+
+ public EmptyWindowsProcessWinRmDriver(EmptyWindowsProcessImpl entity, WinRmMachineLocation machine) {
+ super(entity, machine);
+ }
+
+ @Override
+ public void start() {
+ WinRmMachineLocation machine = (WinRmMachineLocation) location;
+ UserAndHostAndPort winrmAddress = UserAndHostAndPort.fromParts(machine.getUser(), machine.getAddress().getHostName(), machine.config().get(WinRmMachineLocation.WINRM_PORT));
+ getEntity().sensors().set(Attributes.WINRM_ADDRESS, winrmAddress);
+
+ super.start();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return running.get();
+ }
+
+ @Override
+ public void install() { }
+
+ @Override
+ public void customize() { }
+
+ @Override
+ public void copyInstallResources() {
+ Map<String, String> installFiles = entity.getConfig(SoftwareProcess.INSTALL_FILES);
+ Map<String, String> installTemplates = entity.getConfig(SoftwareProcess.INSTALL_TEMPLATES);
+ if ((installFiles!=null && !installFiles.isEmpty()) || (installTemplates!=null && !installTemplates.isEmpty())) {
+ // only do this if there are files, to prevent unnecessary `mkdir`
+ super.copyInstallResources();
+ }
+ }
+
+ @Override
+ public void copyRuntimeResources() {
+ Map<String, String> runtimeFiles = entity.getConfig(SoftwareProcess.RUNTIME_FILES);
+ Map<String, String> runtimeTemplates = entity.getConfig(SoftwareProcess.RUNTIME_TEMPLATES);
+ if ((runtimeFiles!=null && !runtimeFiles.isEmpty()) || (runtimeTemplates!=null && !runtimeTemplates.isEmpty())) {
+ // only do this if there are files, to prevent unnecessary `mkdir`
+ super.copyRuntimeResources();
+ }
+ }
+
+ @Override
+ public void launch() {
+ running.set(true);
+ }
+
+ @Override
+ public void rebind() {
+ super.rebind();
+ /* TODO not necessarily, but there is not yet an easy way to persist state without
+ * using config/sensors which we might not want do. */
+ running.set(true);
+ }
+
+ @Override
+ public void stop() {
+ running.set(false);
+ }
+}
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
index c24201a..8da252f 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SameServerEntityImpl.java
@@ -21,6 +21,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
+import java.util.List;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.mgmt.Task;
@@ -28,6 +29,7 @@
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
+import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.entity.software.base.lifecycle.MachineLifecycleEffectorTasks;
import org.apache.brooklyn.util.collections.QuorumCheck;
import org.apache.brooklyn.util.core.config.ConfigBag;
@@ -68,7 +70,10 @@
* Subclasses should override {@link #doStart} to customise behaviour.
*/
@Override
- public final void start(final Collection<? extends Location> locations) {
+ public final void start(Collection<? extends Location> locsO) {
+ addLocations(locsO);
+ final Collection<? extends Location> locations = Locations.getLocationsCheckingAncestors(locsO, this);
+
checkNotNull(locations, "locations");
if (DynamicTasks.getTaskQueuingContext() != null) {
doStart(locations);
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
new file mode 100644
index 0000000..77175d2
--- /dev/null
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.entity.software.base.test.autoscaling;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.core.entity.trait.Resizable;
+import org.apache.brooklyn.core.mgmt.internal.CollectionChangeListener;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestCluster;
+import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+
+public class AutoScalerPolicyNoMoreMachinesTest extends BrooklynAppUnitTestSupport {
+
+ @SuppressWarnings("unused")
+ private static final Logger log = LoggerFactory.getLogger(AutoScalerPolicyNoMoreMachinesTest.class);
+
+ private static long SHORT_WAIT_MS = 250;
+
+ DynamicCluster cluster;
+ Location loc;
+ AutoScalerPolicy policy;
+ Set<Entity> entitiesAdded;
+ Set<Entity> entitiesRemoved;
+
+ @BeforeMethod(alwaysRun=true)
+ public void setUp() throws Exception {
+ super.setUp();
+ cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class)
+ .configure(TestCluster.INITIAL_SIZE, 0)
+ .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(EmptySoftwareProcess.class)
+ .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true)));
+ loc = mgmt.getLocationRegistry().resolve("byon(hosts='1.1.1.1,1.1.1.2')");
+ app.start(ImmutableList.of(loc));
+
+ entitiesAdded = Sets.newLinkedHashSet();
+ entitiesRemoved = Sets.newLinkedHashSet();
+ mgmt.addEntitySetListener(new CollectionChangeListener<Entity>() {
+ @Override public void onItemAdded(Entity item) {
+ entitiesAdded.add(item);
+ }
+ @Override public void onItemRemoved(Entity item) {
+ entitiesRemoved.add(item);
+ }});
+ }
+
+ @Test
+ public void testResizeDirectly() throws Exception {
+ assertSize(0);
+
+ cluster.resize(2);
+ assertSize(2);
+
+ // Won't get a location to successfully resize (byon location only has 2 machines);
+ // so still left with 2 members (failed node not quarantined, because exception well understood)
+ try {
+ cluster.resize(3);
+ Asserts.shouldHaveFailedPreviously();
+ } catch (Exception e) {
+ Asserts.expectedFailureOfType(e, Resizable.InsufficientCapacityException.class);
+ }
+ assertSize(2, 0, 1);
+
+ // Resize down; will delete one of our nodes
+ cluster.resize(1);
+ assertSize(1, 0, 2);
+
+ // Resize back up to 2 should be allowed
+ cluster.resize(2);
+ assertSize(2, 0, 2);
+ }
+
+ @Test
+ public void testPoolHotSensorResizingBeyondMaxMachines() throws Exception {
+ cluster.resize(1);
+ policy = cluster.policies().add(PolicySpec.create(AutoScalerPolicy.class)
+ .configure(AutoScalerPolicy.MIN_PERIOD_BETWEEN_EXECS, Duration.millis(10)));
+
+ // Single node trying to handle a load of 21; too high, so will add one more node
+ cluster.sensors().emit(AutoScalerPolicy.DEFAULT_POOL_HOT_SENSOR, message(21L, 10L, 20L));
+ assertSizeEventually(2);
+
+ // Two nodes handing an aggregated load of 41; too high for 2 nodes so tries to scale to 3.
+ // But byon location only has 2 nodes so will fail.
+ cluster.sensors().emit(AutoScalerPolicy.DEFAULT_POOL_HOT_SENSOR, message(21L, 10L, 20L));
+ assertSizeEventually(2, 0, 1);
+
+ // Should not repeatedly retry
+ assertSizeContinually(2, 0, 1);
+
+ // If there is another indication of too much load, should not retry yet again.
+ cluster.sensors().emit(AutoScalerPolicy.DEFAULT_POOL_HOT_SENSOR, message(42L, 10L, 20L));
+ assertSizeContinually(2, 0, 1);
+ }
+
+ @Test
+ public void testMetricResizingBeyondMaxMachines() throws Exception {
+ AttributeSensor<Integer> metric = Sensors.newIntegerSensor("test.aggregatedLoad");
+
+ cluster.resize(1);
+ policy = cluster.policies().add(PolicySpec.create(AutoScalerPolicy.class)
+ .configure(AutoScalerPolicy.METRIC, metric)
+ .configure(AutoScalerPolicy.METRIC_LOWER_BOUND, 10)
+ .configure(AutoScalerPolicy.METRIC_UPPER_BOUND, 20)
+ .configure(AutoScalerPolicy.MIN_PERIOD_BETWEEN_EXECS, Duration.millis(10)));
+
+ // Single node trying to handle a load of 21; too high, so will add one more node.
+ // That takes the load back to within acceptable limits
+ cluster.sensors().set(metric, 21);
+ assertSizeEventually(2);
+ cluster.sensors().set(metric, 19);
+
+ // With two nodes, load is now too high, so will try (and fail) to add one more node.
+ // Trigger another attempt to resize.
+ // Any nodes that fail with NoMachinesAvailableException will be immediately deleted.
+ cluster.sensors().set(metric, 22);
+ assertSizeEventually(2, 0, 1);
+ assertSizeContinually(2, 0, 1);
+
+ // Metric is re-published; should not keep retrying
+ cluster.sensors().set(metric, 21);
+ assertSizeContinually(2, 0, 1);
+ }
+
+ protected Map<String, Object> message(double currentWorkrate, double lowThreshold, double highThreshold) {
+ return message(cluster.getCurrentSize(), currentWorkrate, lowThreshold, highThreshold);
+ }
+
+ protected Map<String, Object> message(int currentSize, double currentWorkrate, double lowThreshold, double highThreshold) {
+ return ImmutableMap.<String,Object>of(
+ AutoScalerPolicy.POOL_CURRENT_SIZE_KEY, currentSize,
+ AutoScalerPolicy.POOL_CURRENT_WORKRATE_KEY, currentWorkrate,
+ AutoScalerPolicy.POOL_LOW_THRESHOLD_KEY, lowThreshold,
+ AutoScalerPolicy.POOL_HIGH_THRESHOLD_KEY, highThreshold);
+ }
+
+ protected void assertSize(Integer targetSize) {
+ assertSize(targetSize, 0);
+ }
+
+ protected void assertSize(int targetSize, int quarantineSize, final int deletedSize) {
+ assertSize(targetSize, quarantineSize);
+ assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+ }
+
+ protected void assertSize(int targetSize, int quarantineSize) {
+ assertEquals(cluster.getCurrentSize(), (Integer) targetSize, "cluster.currentSize");
+ assertEquals(cluster.getMembers().size(), targetSize, "cluster.members.size");
+ assertEquals(cluster.sensors().get(DynamicCluster.QUARANTINE_GROUP).getMembers().size(), quarantineSize, "cluster.quarantine.size");
+ assertEquals(mgmt.getEntityManager().findEntities(Predicates.instanceOf(EmptySoftwareProcess.class)).size(), targetSize + quarantineSize, "instanceCount(EmptySoftwareProcess)");
+ }
+
+ protected void assertSizeEventually(int targetSize) {
+ assertSizeEventually(targetSize, 0, 0);
+ }
+
+ protected void assertSizeEventually(final int targetSize, final int quarantineSize, final int deletedSize) {
+ Asserts.succeedsEventually(new Runnable() {
+ public void run() {
+ assertSize(targetSize, quarantineSize);
+ assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+ }});
+ }
+
+ protected void assertSizeContinually(final int targetSize, final int quarantineSize, final int deletedSize) {
+ Asserts.succeedsContinually(ImmutableMap.of("timeout", SHORT_WAIT_MS), new Runnable() {
+ public void run() {
+ assertSize(targetSize, quarantineSize);
+ assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+ }});
+ }
+}
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
index 884da19..2e34ef7 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
@@ -63,7 +63,7 @@
* Tests execution of commands (batch and powershell) on Windows over WinRM, and of
* file upload.
*
- * There are limitations with what is supported by PyWinRM. These are highlighted in
+ * There are limitations with what is supported by winrm4j. These are highlighted in
* tests marked as "WIP" (see individual tests).
*
* These limitations are documented in docs/guide/yaml/winrm/index.md.
@@ -146,7 +146,7 @@
String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt";
machine.copyTo(new ByteArrayInputStream(contents.getBytes()), remotePath);
- WinRmToolResponse response = machine.executeScript("type "+remotePath);
+ WinRmToolResponse response = machine.executeCommand("type "+remotePath);
String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
assertEquals(response.getStatusCode(), 0, msg);
assertEquals(response.getStdOut().trim(), contents, msg);
@@ -159,7 +159,7 @@
String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt";
machine.copyTo(localFile, remotePath);
- WinRmToolResponse response = machine.executeScript("type "+remotePath);
+ WinRmToolResponse response = machine.executeCommand("type "+remotePath);
String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr();
assertEquals(response.getStatusCode(), 0, msg);
assertEquals(response.getStdOut().trim(), contents, msg);
@@ -174,26 +174,18 @@
}
/*
- * TODO Not supported in PyWinRM.
+ * TODO Not supported in winrm4j (or PyWinRM).
*
- * Executing (in python):
- * import winrm
- * s = winrm.Session('52.12.211.247', auth=('Administrator', 'pa55w0rd'))
- * r = s.run_cmd("echo first \r\n echo second")
- * gives just "first".
+ * Just gives "first", and exit code 1.
*/
- @Test(groups={"Live", "WIP"})
+ @Test(groups={"Live", "WIP"}, enabled=false)
public void testExecMultiLineScript() throws Exception {
assertExecSucceeds("echo first" + "\r\n" + "echo second", "first"+"\r\n"+"second", "");
}
- /*
- * TODO Not supported in PyWinRM. Under the covers, we just concatenate the commands.
- * See {@link #testExecMultiLineScript()}.
- */
- @Test(groups={"Live", "WIP"})
+ @Test(groups={"Live"})
public void testExecMultiPartScript() throws Exception {
- assertExecSucceeds(ImmutableList.of("echo first", "echo second"), "first"+"\r\n"+"second", "");
+ assertExecSucceeds(ImmutableList.of("echo first", "echo second"), "first "+"\r\n"+"second", "");
}
@Test(groups="Live")
@@ -212,7 +204,7 @@
}
/*
- * TODO Not supported in PyWinRM.
+ * TODO Not supported in winrm4j (or PyWinRM).
*
* Executing (in python):
* import winrm
@@ -443,7 +435,7 @@
}
/*
- * TODO Not supported in PyWinRM - single line .ps1 file with "exit 1" gives an
+ * TODO Not supported in winrm4j - single line .ps1 file with "exit 1" gives an
* exit code 0 over PyWinRM, but an exit code 1 when executed locally!
*
* Executing (in python):
@@ -554,12 +546,12 @@
private void assertExecFails(String cmd) {
Stopwatch stopwatch = Stopwatch.createStarted();
- assertFailed(cmd, machine.executeScript(cmd), stopwatch);
+ assertFailed(cmd, machine.executeCommand(cmd), stopwatch);
}
private void assertExecFails(List<String> cmds) {
Stopwatch stopwatch = Stopwatch.createStarted();
- assertFailed(cmds, machine.executeScript(cmds), stopwatch);
+ assertFailed(cmds, machine.executeCommand(cmds), stopwatch);
}
private void assertExecPsFails(String cmd) {
@@ -574,12 +566,12 @@
private void assertExecSucceeds(String cmd, String stdout, String stderr) {
Stopwatch stopwatch = Stopwatch.createStarted();
- assertSucceeded(cmd, machine.executeScript(cmd), stdout, stderr, stopwatch);
+ assertSucceeded(cmd, machine.executeCommand(cmd), stdout, stderr, stopwatch);
}
private void assertExecSucceeds(List<String> cmds, String stdout, String stderr) {
Stopwatch stopwatch = Stopwatch.createStarted();
- assertSucceeded(cmds, machine.executeScript(cmds), stdout, stderr, stopwatch);
+ assertSucceeded(cmds, machine.executeCommand(cmds), stdout, stderr, stopwatch);
}
private void assertExecPsSucceeds(String cmd, String stdout, String stderr) {
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
index b6f8684..1939ed0 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
@@ -116,8 +116,8 @@
private String uniqueTag;
private volatile boolean built;
- public Builder entity(EntityLocal val) {
- this.entity = checkNotNull(val, "entity");
+ public Builder entity(Entity val) {
+ this.entity = (EntityLocal) checkNotNull(val, "entity");
return this;
}
public Builder addSensor(WindowsPerformanceCounterPollConfig<?> config) {
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
index 8a2a786..d04c366 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
@@ -44,7 +44,7 @@
import org.apache.brooklyn.util.core.internal.ssh.SshTool;
import org.apache.brooklyn.util.core.internal.winrm.WinRmTool;
import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
-import org.apache.brooklyn.util.core.internal.winrm.pywinrm.Winrm4jTool;
+import org.apache.brooklyn.util.core.internal.winrm.winrm4j.Winrm4jTool;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.text.Strings;
@@ -212,17 +212,50 @@
return (result == null) ? ImmutableSet.<String>of() : ImmutableSet.copyOf(result);
}
+ /**
+ * @deprecated since 0.9.0; use {@link #executeCommand(String)}
+ */
+ @Deprecated
public WinRmToolResponse executeScript(String script) {
- return executeScript(ImmutableList.of(script));
+ return executeCommand(script);
}
+ /**
+ * @deprecated since 0.9.0; use {@link #executeCommand(List)}
+ */
+ @Deprecated
public WinRmToolResponse executeScript(List<String> script) {
- return executeScript(ImmutableMap.of(), script);
+ return executeCommand(script);
}
+ /**
+ * @deprecated since 0.9.0; use {@link #executeCommand(Map, List)}
+ */
+ @Deprecated
public WinRmToolResponse executeScript(Map<?,?> props, List<String> script) {
+ return executeCommand(props, script);
+ }
+
+ /**
+ * @since 0.9.0 (previously was {@code executeScript(String)}
+ */
+ public WinRmToolResponse executeCommand(String script) {
+ return executeCommand(ImmutableList.of(script));
+ }
+
+ /**
+ * @since 0.9.0 (previously was {@code executeScript(List)}
+ */
+ public WinRmToolResponse executeCommand(List<String> script) {
+ return executeCommand(ImmutableMap.of(), script);
+ }
+
+ /**
+ * @since 0.9.0 (previously was {@code executeScript(Map, List)}
+ */
+ public WinRmToolResponse executeCommand(Map<?,?> props, List<String> script) {
WinRmTool tool = newWinRmTool(props);
- return tool.executeScript(script);
+ return tool.executeCommand(script);
}
public WinRmToolResponse executePsScript(String psScript) {
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
index 6d2fb06..5515ec5 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
@@ -66,8 +66,17 @@
"Size of file chunks (in bytes) to be used when copying a file to the remote server",
1024);
+ /**
+ * @deprecated since 0.9.0; use {@link #executeCommand(List)} to avoid ambiguity between native command and power shell.
+ */
+ @Deprecated
WinRmToolResponse executeScript(List<String> commands);
+ /**
+ * @since 0.9.0
+ */
+ WinRmToolResponse executeCommand(List<String> commands);
+
WinRmToolResponse executePs(List<String> commands);
WinRmToolResponse copyToServer(InputStream source, String destination);
diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
similarity index 96%
rename from brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
rename to brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
index 0bdfa14..e023cfb 100644
--- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
+++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/winrm4j/Winrm4jTool.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.brooklyn.util.core.internal.winrm.pywinrm;
+package org.apache.brooklyn.util.core.internal.winrm.winrm4j;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -84,15 +84,21 @@
}
@Override
- public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List<String> commands) {
+ public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeCommand(final List<String> commands) {
return exec(new Function<io.cloudsoft.winrm4j.winrm.WinRmTool, io.cloudsoft.winrm4j.winrm.WinRmToolResponse>() {
@Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) {
- return tool.executeScript(commands);
+ return tool.executeCommand(commands);
}
});
}
@Override
+ @Deprecated
+ public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List<String> commands) {
+ return executeCommand(commands);
+ }
+
+ @Override
public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executePs(final List<String> commands) {
return exec(new Function<io.cloudsoft.winrm4j.winrm.WinRmTool, io.cloudsoft.winrm4j.winrm.WinRmToolResponse>() {
@Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) {
diff --git a/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java b/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java
index de7d481..412a732 100644
--- a/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java
+++ b/brooklyn-server/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedLiveTest.java
@@ -20,16 +20,15 @@
import java.util.Map;
-import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.location.Location;
-import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.MachineProvisioningLocation;
import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.EntityAsserts;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
import org.apache.brooklyn.core.test.entity.TestEntity;
-import org.apache.brooklyn.test.EntityTestUtils;
import org.apache.brooklyn.util.collections.MutableMap;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -51,8 +50,8 @@
* <pre>
* {@code brooklyn.location.named.WindowsLiveTest=byon:(hosts="ec2-xx-xxx-xxx-xx.eu-west-1.compute.amazonaws.com")
* brooklyn.location.named.WindowsLiveTest.user=Administrator
- * brooklyn.location.named.WindowsLiveTest.privateKeyFile = ~/.ssh/id_rsa
- * brooklyn.location.named.WindowsLiveTest.publicKeyFile = ~/.ssh/id_rsa.pub
+ * brooklyn.location.named.WindowsLiveTest.password=pa55word
+ * brooklyn.location.named.WindowsLiveTest.osFamily=windows
* }</pre>
* The location must by {@code byon} or another primitive type. Unfortunately, it's not possible to
* use a jclouds location, as adding a dependency on brooklyn-locations-jclouds would cause a
@@ -66,7 +65,7 @@
private static final String LOCATION_SPEC = "named:WindowsLiveTest";
private Location loc;
- private EntityLocal entity;
+ private Entity entity;
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
@@ -75,16 +74,15 @@
Map<String,?> allFlags = MutableMap.<String,Object>builder()
.put("tags", ImmutableList.of(getClass().getName()))
.build();
- MachineProvisioningLocation<? extends MachineLocation> provisioningLocation =
- (MachineProvisioningLocation<? extends MachineLocation>)
- mgmt.getLocationRegistry().resolve(LOCATION_SPEC, allFlags);
+ MachineProvisioningLocation<?> provisioningLocation = (MachineProvisioningLocation<?>)
+ mgmt.getLocationRegistry().resolve(LOCATION_SPEC, allFlags);
loc = provisioningLocation.obtain(ImmutableMap.of());
entity = app.createAndManageChild(EntitySpec.create(TestEntity.class));
app.start(ImmutableList.of(loc));
}
- @Test(groups={"Live","Disabled"}, enabled=false)
+ @Test(groups={"Live","Disabled"})
public void testRetrievesPerformanceCounters() throws Exception {
// We can be pretty sure that a Windows instance in the cloud will have zero telephone lines...
entity.sensors().set(TELEPHONE_LINES, 42);
@@ -94,7 +92,7 @@
.addSensor("\\Telephony\\Lines", TELEPHONE_LINES)
.build();
try {
- EntityTestUtils.assertAttributeEqualsEventually(entity, TELEPHONE_LINES, 0);
+ EntityAsserts.assertAttributeEqualsEventually(entity, TELEPHONE_LINES, 0);
} finally {
feed.stop();
}
diff --git a/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java b/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
index 469bc3d..3ded474 100644
--- a/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
+++ b/brooklyn-server/test-framework/src/main/java/org/apache/brooklyn/test/framework/ParallelTestCaseImpl.java
@@ -49,7 +49,7 @@
try {
// Get an unsubmitted task for starting all the children of this entity in parallel,
// at the same location as this entity.
- final TaskAdaptable<?> taskAdaptable = StartableMethods.startingChildren(this);
+ final TaskAdaptable<?> taskAdaptable = StartableMethods.startingChildren(this, locations);
logger.trace("{}, TaskAdaptable: {}", this, taskAdaptable);
// Submit the task to the ExecutionManager so that they actually get started
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/InfrastructureDeploymentTestCaseTest.java
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/LoopOverGroupMembersTestCaseTest.java
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructure.java
diff --git a/usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java b/brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java
similarity index 100%
rename from usage/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java
rename to brooklyn-server/test-framework/src/test/java/org/apache/brooklyn/test/framework/entity/TestInfrastructureImpl.java
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
index 2bd7b5c..15aa76e 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
@@ -1115,22 +1115,29 @@
if (e instanceof ShouldHaveFailedPreviouslyAssertionError) throw (Error)e;
}
- /** Tests that an exception is not {@link ShouldHaveFailedPreviouslyAssertionError}
- * and is one of the given types.
+ /**
+ * Tests that an exception is not {@link ShouldHaveFailedPreviouslyAssertionError}
+ * and is either one of the given types, or has a caused-by of one of the given types.
+ *
+ * If you want *just* the instanceof (without checking the caused-by), then just catch
+ * those exception types, treat that as success, and let any other exception be propagated.
*
* @return If the test is satisfied, this method returns normally.
* The caller can decide whether anything more should be done with the exception.
* If the test fails, then either it is propagated,
* if the {@link Throwable} is a fatal ({@link Exceptions#propagateIfFatal(Throwable)}) other than an {@link AssertionError},
* or more usually the test failure of this method is thrown,
- * with detail of the original {@link Throwable} logged. */
+ * with detail of the original {@link Throwable} logged and included in the caused-by.
+ */
public static void expectedFailureOfType(Throwable e, Class<?> ...permittedSupertypes) {
if (e instanceof ShouldHaveFailedPreviouslyAssertionError) throw (Error)e;
- for (Class<?> t: permittedSupertypes) {
- if (t.isInstance(e)) return;
+ for (Class<?> clazz: permittedSupertypes) {
+ @SuppressWarnings("unchecked")
+ Throwable match = Exceptions.getFirstThrowableOfType(e, (Class<? extends Throwable>)clazz);
+ if (match != null) return;
}
rethrowPreferredException(e,
- new AssertionError("Error "+JavaClassNames.simpleClassName(e)+" is not any of the expected types: " + Arrays.asList(permittedSupertypes)));
+ new AssertionError("Error "+JavaClassNames.simpleClassName(e)+" is not any of the expected types: " + Arrays.asList(permittedSupertypes), e));
}
/** Tests {@link #expectedFailure(Throwable)} and that the <code>toString</code>
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
index 9643018..314961a 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/exceptions/Exceptions.java
@@ -33,6 +33,7 @@
import javax.annotation.Nullable;
import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.javalang.JavaClassNames;
import org.apache.brooklyn.util.text.Strings;
import com.google.common.base.Predicate;
@@ -324,7 +325,23 @@
return new CompoundRuntimeException(prefix, exceptions);
}
if (Strings.isBlank(prefix)) return new CompoundRuntimeException(exceptions.size()+" errors, including: " + Exceptions.collapseText(exceptions.iterator().next()), exceptions);
- return new CompoundRuntimeException(prefix+", "+exceptions.size()+" errors including: " + Exceptions.collapseText(exceptions.iterator().next()), exceptions);
+ return new CompoundRuntimeException(prefix+"; "+exceptions.size()+" errors including: " + Exceptions.collapseText(exceptions.iterator().next()), exceptions);
+ }
+
+ /** Some throwables require a prefix for the message to make sense,
+ * for instance NoClassDefFoundError's message is often just the type.
+ */
+ public static boolean isPrefixImportant(Throwable t) {
+ if (t instanceof NoClassDefFoundError) return true;
+ return false;
+ }
+
+ /** For {@link Throwable} instances where know {@link #isPrefixImportant(Throwable)},
+ * this returns a nice message for use as a prefix; otherwise this returns the class name.
+ * Callers should typically suppress the prefix if {@link #isPrefixBoring(Throwable)} is true. */
+ public static String getPrefixText(Throwable t) {
+ if (t instanceof NoClassDefFoundError) return "Type not found";
+ return JavaClassNames.cleanSimpleClassName(t);
}
}
diff --git a/brooklyn-ui/src/main/license/files/LICENSE b/brooklyn-ui/src/main/license/files/LICENSE
index 4a1f38b..3b3b73d 100644
--- a/brooklyn-ui/src/main/license/files/LICENSE
+++ b/brooklyn-ui/src/main/license/files/LICENSE
@@ -208,13 +208,6 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Jeremy Ashkenas, DocumentCloud Inc. (2010-2013)
-This project includes the software: backbone.js
- Available at: http://backbonejs.org
- Developed by: DocumentCloud Inc. (http://www.documentcloud.org/)
- Version used: 1.1.2
- Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
- Copyright (c) Jeremy Ashkenas, DocumentCloud (2010-2014)
-
This project includes the software: bootstrap.js
Available at: http://twitter.github.com/bootstrap/javascript.html#transitions
Version used: 2.0.4
@@ -229,14 +222,6 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Yehuda Katz (2012)
-This project includes the software: handlebars.js
- Available at: https://github.com/wycats/handlebars.js
- Developed by: Yehuda Katz (https://github.com/wycats/)
- Inclusive of: handlebars*.js
- Version used: 2.0.0
- Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
- Copyright (C) by Yehuda Katz (2011-2014)
-
This project includes the software: jQuery JavaScript Library
Available at: http://jquery.com/
Developed by: The jQuery Foundation (http://jquery.org/)
@@ -249,18 +234,6 @@
Available at http://sizzlejs.com
Used under the MIT license
-This project includes the software: jQuery JavaScript Library
- Available at: http://jquery.com/
- Developed by: The jQuery Foundation (http://jquery.org/)
- Inclusive of: jquery.js
- Version used: 1.8.0
- Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
- Copyright (c) jQuery Foundation and other contributors (2012)
- Includes code fragments from sizzle.js:
- Copyright (c) The Dojo Foundation
- Available at http://sizzlejs.com
- Used under the MIT license
-
This project includes the software: jQuery BBQ: Back Button & Query Library
Available at: http://benalman.com/projects/jquery-bbq-plugin/
Developed by: "Cowboy" Ben Alman (http://benalman.com/)
@@ -309,6 +282,13 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Vitaly Puzrin (2011-2015)
+This project includes the software: marked.js
+ Available at: https://github.com/chjj/marked
+ Developed by: Christopher Jeffrey (https://github.com/chjj)
+ Version used: 0.3.1
+ Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
+ Copyright (c) Christopher Jeffrey (2011-2014)
+
This project includes the software: moment.js
Available at: http://momentjs.com
Developed by: Tim Wood (http://momentjs.com)
@@ -350,19 +330,11 @@
Arpad Borsos (2012)
Used under the BSD 2-Clause license.
-This project includes the software: Swagger JS
- Available at: https://github.com/swagger-api/swagger-js
- Inclusive of: swagger.js
- Version used: 2.1.6
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- Copyright (c) SmartBear Software (2015)
+This project includes the software: swagger
+ Used under the following license: <no license info>
-This project includes the software: Swagger UI
- Available at: https://github.com/swagger-api/swagger-ui
- Inclusive of: swagger-ui.js
- Version used: 2.1.3
- Used under the following license: Apache License, version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- Copyright (c) SmartBear Software (2015)
+This project includes the software: swagger-ui
+ Used under the following license: <no license info>
This project includes the software: underscore.js
Available at: http://underscorejs.org
@@ -372,14 +344,6 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Jeremy Ashkenas, DocumentCloud Inc. (2009-2013)
-This project includes the software: underscore.js
- Available at: http://underscorejs.org
- Developed by: DocumentCloud Inc. (http://www.documentcloud.org/)
- Inclusive of: underscore*.{js,map}
- Version used: 1.7.0
- Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
- Copyright (c) Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors (2009-2014)
-
This project includes the software: ZeroClipboard
Available at: http://zeroclipboard.org/
Developed by: ZeroClipboard contributors (https://github.com/zeroclipboard)
@@ -388,13 +352,6 @@
Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
Copyright (c) Jon Rohan, James M. Greene (2014)
-This project includes the software: marked.js
- Available at: https://github.com/chjj/marked
- Developed by: Christopher Jeffrey (https://github.com/chjj)
- Inclusive of: marked.js
- Version used: 0.3.1
- Used under the following license: The MIT License (http://opensource.org/licenses/MIT)
- Copyright (c) Christopher Jeffrey (2011-2014)
---------------------------------------------------