[SPLITPREP] move more PR items to the right place
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/8a3c17b9 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/8a3c17b9 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/8a3c17b9 Branch: refs/heads/master Commit: 8a3c17b963576ec75b66d1e7b20422f86a0df11d Parents: 018a0e1 Author: Alex Heneveld <[email protected]> Authored: Mon Dec 21 12:37:58 2015 +0000 Committer: Alex Heneveld <[email protected]> Committed: Mon Dec 21 16:43:29 2015 +0000 ---------------------------------------------------------------------- .../downstreamparent/DownstreamParentTest.java | 64 +++ .../test/projects/downstream-parent-test/README | 5 + .../projects/downstream-parent-test/pom.xml | 120 +++++ .../src/main/java/com/example/HelloEntity.java | 26 + .../main/java/com/example/HelloEntityImpl.java | 31 ++ .../src/main/resources/blueprint.yaml | 19 + .../src/main/resources/catalog.bom | 33 ++ .../dns/geoscaling/GeoDnsServiceYamlTest.java | 45 ++ .../brooklyn/entity/dns/geoscaling/geodns.yaml | 42 ++ .../ExternalConfigBrooklynPropertiesTest.java | 146 ++++++ .../core/internal/BrooklynPropertiesImpl.java | 477 +++++++++++++++++++ .../core/mgmt/internal/CampYamlParser.java | 34 ++ .../internal/DeferredBrooklynProperties.java | 370 ++++++++++++++ .../brooklyn/util/text/VersionComparator.java | 199 ++++++++ .../util/text/VersionComparatorTest.java | 102 ++++ .../core/internal/BrooklynPropertiesImpl.java | 477 ------------------- .../core/mgmt/internal/CampYamlParser.java | 34 -- .../internal/DeferredBrooklynProperties.java | 370 -------------- .../ExternalConfigBrooklynPropertiesTest.java | 146 ------ .../camp/brooklyn/GeoDnsServiceYamlTest.java | 45 -- .../apache/brooklyn/camp/brooklyn/geodns.yaml | 42 -- .../downstreamparent/DownstreamParentTest.java | 64 --- .../test/projects/downstream-parent-test/README | 5 - .../projects/downstream-parent-test/pom.xml | 120 ----- .../src/main/java/com/example/HelloEntity.java | 26 - .../main/java/com/example/HelloEntityImpl.java | 31 -- .../src/main/resources/blueprint.yaml | 19 - .../src/main/resources/catalog.bom | 33 -- .../brooklyn/util/text/VersionComparator.java | 199 -------- .../util/text/VersionComparatorTest.java | 102 ---- 30 files changed, 1713 insertions(+), 1713 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/java/org/apache/brooklyn/qa/downstreamparent/DownstreamParentTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/java/org/apache/brooklyn/qa/downstreamparent/DownstreamParentTest.java b/brooklyn-library/qa/src/test/java/org/apache/brooklyn/qa/downstreamparent/DownstreamParentTest.java new file mode 100644 index 0000000..33c4c42 --- /dev/null +++ b/brooklyn-library/qa/src/test/java/org/apache/brooklyn/qa/downstreamparent/DownstreamParentTest.java @@ -0,0 +1,64 @@ +/* + * 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.qa.downstreamparent; + +import static org.testng.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; + +import org.apache.brooklyn.util.net.Networking; +import org.apache.maven.it.Verifier; +import org.apache.maven.shared.utils.io.FileUtils; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +public class DownstreamParentTest { + + private static final String PROJECTS_DIR = "src/test/projects"; + private static final String WORK_DIR = "target/ut/"; + + /** + * Asserts that a trivial project using brooklyn-downstream-parent can be + * loaded into Brooklyn's catalogue and its entities deployed. + */ + @Test(groups = "Integration") + public void testDownstreamProjectsCanBeLoadedIntoBrooklynCatalogByDefault() throws Exception { + int port = Networking.nextAvailablePort(57000); + File dir = getBasedir("downstream-parent-test"); + Verifier verifier = new Verifier(dir.getAbsolutePath()); + verifier.setMavenDebug(true); + verifier.executeGoal("post-integration-test", ImmutableMap.of( + "bindPort", String.valueOf(port))); + verifier.verifyErrorFreeLog(); + verifier.verifyTextInLog("Hello from the init method of the HelloEntity"); + } + + /** Replicates the behaviour of getBasedir in JUnit's TestResources class */ + public File getBasedir(String project) throws IOException { + File src = (new File(PROJECTS_DIR, project)).getCanonicalFile(); + assertTrue(src.isDirectory(), "Test project directory does not exist: " + src.getPath()); + File basedir = (new File(WORK_DIR, getClass().getSimpleName() + "_" + project)).getCanonicalFile(); + FileUtils.deleteDirectory(basedir); + assertTrue(basedir.mkdirs(), "Test project working directory created"); + FileUtils.copyDirectoryStructure(src, basedir); + return basedir; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/projects/downstream-parent-test/README ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/projects/downstream-parent-test/README b/brooklyn-library/qa/src/test/projects/downstream-parent-test/README new file mode 100644 index 0000000..3f2f574 --- /dev/null +++ b/brooklyn-library/qa/src/test/projects/downstream-parent-test/README @@ -0,0 +1,5 @@ +A successful build of this project (`mvn clean verify`) means that projects that +use brooklyn-downstream-parent can be loaded into Brooklyn's catalogue by default. + +If the build fails there is almost certainly something wrong with the parent and +the wider consumers of Brooklyn will probably face similar problems. http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/projects/downstream-parent-test/pom.xml ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/projects/downstream-parent-test/pom.xml b/brooklyn-library/qa/src/test/projects/downstream-parent-test/pom.xml new file mode 100644 index 0000000..7e3c0e0 --- /dev/null +++ b/brooklyn-library/qa/src/test/projects/downstream-parent-test/pom.xml @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.apache.brooklyn.downstream-parent-test</groupId> + <artifactId>catalogue-load-test</artifactId> + <version>0.9.0-SNAPSHOT</version> <!-- BROOKLYN_VERSION --> + <packaging>jar</packaging> + + <name>Downstream parent catalogue load test test</name> + + <parent> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-downstream-parent</artifactId> + <version>0.9.0-SNAPSHOT</version> <!-- BROOKLYN_VERSION --> + </parent> + + <repositories> + <repository> + <id>apache-snapshots</id> + <url>https://repository.apache.org/content/repositories/snapshots/</url> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> + + <pluginRepositories> + <pluginRepository> + <id>sonatype-nexus-snapshots</id> + <name>Sonatype Nexus Snapshots</name> + <url>https://oss.sonatype.org/content/repositories/snapshots</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </pluginRepository> + </pluginRepositories> + + <dependencies> + <dependency> + <groupId>org.apache.brooklyn</groupId> + <artifactId>brooklyn-all</artifactId> + <version>0.9.0-SNAPSHOT</version> <!-- BROOKLYN_VERSION --> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>${basedir}/src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + <plugins> + <plugin> + <groupId>io.brooklyn.maven</groupId> + <artifactId>brooklyn-maven-plugin</artifactId> + <version>0.3.0-SNAPSHOT</version> + <executions> + <execution> + <id>Run and deploy Brooklyn</id> + <goals> + <goal>start-server</goal> + </goals> + <configuration> + <bindPort>${bindPort}</bindPort> + <!-- + Make sure that the test entities aren't already on the classpath. + --> + <outputDirOnClasspath>false</outputDirOnClasspath> + <arguments> + <argument>--catalogInitial</argument> + <argument>${project.build.outputDirectory}/catalog.bom</argument> + </arguments> + </configuration> + </execution> + <execution> + <id>Deploy entity from catalogue</id> + <phase>integration-test</phase> + <goals> + <goal>deploy</goal> + </goals> + <configuration> + <blueprint>${project.build.outputDirectory}/blueprint.yaml</blueprint> + </configuration> + </execution> + <execution> + <id>Stop Brooklyn</id> + <goals> + <goal>stop-server</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntity.java ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntity.java b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntity.java new file mode 100644 index 0000000..242708b --- /dev/null +++ b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntity.java @@ -0,0 +1,26 @@ +/* + * 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 com.example; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.ImplementedBy; + +@ImplementedBy(HelloEntityImpl.class) +public interface HelloEntity extends Entity { +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntityImpl.java ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntityImpl.java b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntityImpl.java new file mode 100644 index 0000000..76d9ffd --- /dev/null +++ b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/java/com/example/HelloEntityImpl.java @@ -0,0 +1,31 @@ +/* + * 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 com.example; + +import org.apache.brooklyn.core.entity.AbstractEntity; + +public class HelloEntityImpl extends AbstractEntity implements HelloEntity { + + @Override + public void init() { + super.init(); + System.out.println("Hello from the init method of the HelloEntity"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/blueprint.yaml ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/blueprint.yaml b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/blueprint.yaml new file mode 100644 index 0000000..76cc82e --- /dev/null +++ b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/blueprint.yaml @@ -0,0 +1,19 @@ +# 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. +# +services: +- type: downstream-project http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/catalog.bom ---------------------------------------------------------------------- diff --git a/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/catalog.bom b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/catalog.bom new file mode 100644 index 0000000..c168c72 --- /dev/null +++ b/brooklyn-library/qa/src/test/projects/downstream-parent-test/src/main/resources/catalog.bom @@ -0,0 +1,33 @@ +# 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. +# +brooklyn.catalog: + version: 1.0 + + brooklyn.libraries: + - "file://${project.build.directory}/${project.build.finalName}.${project.packaging}" + + items: + + - id: downstream-project + name: Downstream project + itemType: template + description: | + A downstream project + item: + services: + - type: com.example.HelloEntity http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoDnsServiceYamlTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoDnsServiceYamlTest.java b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoDnsServiceYamlTest.java new file mode 100644 index 0000000..3f41e8d --- /dev/null +++ b/brooklyn-library/software/webapp/src/test/java/org/apache/brooklyn/entity/dns/geoscaling/GeoDnsServiceYamlTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.brooklyn.camp.brooklyn; + +import static org.testng.Assert.assertEquals; + +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.mgmt.EntityManagementUtils; +import org.apache.brooklyn.entity.dns.AbstractGeoDnsService; +import org.apache.brooklyn.entity.dns.geoscaling.GeoscalingDnsService; +import org.apache.brooklyn.entity.group.DynamicFabric; +import org.apache.brooklyn.util.stream.Streams; +import org.testng.annotations.Test; + +public class GeoDnsServiceYamlTest extends AbstractYamlTest { + + @Test + public void testTargetGroupCanBeSetInYaml() throws Exception { + final String resourceName = "classpath:/" + getClass().getPackage().getName().replace('.', '/') + "/geodns.yaml"; + final String blueprint = Streams.readFully(loadYaml(resourceName)); + Application app = EntityManagementUtils.createUnstarted(mgmt(), blueprint); + GeoscalingDnsService geodns = Entities.descendants(app, GeoscalingDnsService.class).iterator().next(); + DynamicFabric fabric = Entities.descendants(app, DynamicFabric.class).iterator().next(); + assertEquals(geodns.config().get(AbstractGeoDnsService.ENTITY_PROVIDER), fabric); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-library/software/webapp/src/test/resources/org/apache/brooklyn/entity/dns/geoscaling/geodns.yaml ---------------------------------------------------------------------- diff --git a/brooklyn-library/software/webapp/src/test/resources/org/apache/brooklyn/entity/dns/geoscaling/geodns.yaml b/brooklyn-library/software/webapp/src/test/resources/org/apache/brooklyn/entity/dns/geoscaling/geodns.yaml new file mode 100644 index 0000000..3fdc7b7 --- /dev/null +++ b/brooklyn-library/software/webapp/src/test/resources/org/apache/brooklyn/entity/dns/geoscaling/geodns.yaml @@ -0,0 +1,42 @@ +# +# 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. +# +services: + +- name: Web cluster + type: org.apache.brooklyn.entity.group.DynamicRegionsFabric + id: web-fabric + + # Location required but test should not do any provisioning. + locations: + - localhost + + memberSpec: + $brooklyn:entitySpec: + type: org.apache.brooklyn.entity.webapp.ControlledDynamicWebAppCluster + brooklyn.config: + initialSize: 0 + +- name: Geo DNS + type: org.apache.brooklyn.entity.dns.geoscaling.GeoscalingDnsService + brooklyn.config: + provider: $brooklyn:component("web-fabric") + username: madeUp + password: madeUp + primaryDomainName: example.com + smartSubdomainName: test http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java new file mode 100644 index 0000000..39b444d --- /dev/null +++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ExternalConfigBrooklynPropertiesTest.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.camp.brooklyn; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import org.apache.brooklyn.api.mgmt.ExecutionContext; +import org.apache.brooklyn.camp.brooklyn.ExternalConfigYamlTest.MyExternalConfigSupplier; +import org.apache.brooklyn.camp.brooklyn.ExternalConfigYamlTest.MyExternalConfigSupplierWithoutMapArg; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.config.ConfigPredicates; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.core.task.DeferredSupplier; +import org.apache.brooklyn.util.core.task.Tasks; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +@Test +public class ExternalConfigBrooklynPropertiesTest extends AbstractYamlTest { + + @Override + protected LocalManagementContext newTestManagementContext() { + BrooklynProperties props = BrooklynProperties.Factory.newEmpty(); + props.put("brooklyn.external.myprovider", MyExternalConfigSupplier.class.getName()); + props.put("brooklyn.external.myprovider.mykey", "myval"); + props.put("brooklyn.external.myprovider.mykey2", "myval2"); + props.put("brooklyn.external.myproviderWithoutMapArg", MyExternalConfigSupplierWithoutMapArg.class.getName()); + props.put("myproperty", "$brooklyn:external(\"myprovider\", \"mykey\")"); + + return LocalManagementContextForTests.builder(true) + .useProperties(props) + .build(); + } + + // Yaml parsing support is more generic than just external-config. + // Test other parsing here, even though it's not directly related to external-config. + @Test + public void testYamlLiteralFromPropertiesInLocation() throws Exception { + ((ManagementContextInternal)mgmt()).getBrooklynProperties().put( + ConfigKeys.newStringConfigKey("myDynamicProperty"), "$brooklyn:literal(\"myliteral\")"); + + String val = mgmt().getConfig().getConfig(ConfigKeys.newStringConfigKey("myDynamicProperty")); + assertEquals(val, "myliteral"); + } + + @Test + public void testInvalidYamlExpression() throws Exception { + ((ManagementContextInternal)mgmt()).getBrooklynProperties().put( + ConfigKeys.newStringConfigKey("myInvalidExternal"), "$brooklyn:external"); + + try { + String val = mgmt().getConfig().getConfig(ConfigKeys.newStringConfigKey("myInvalidExternal")); + Asserts.shouldHaveFailedPreviously("val="+val); + } catch (IllegalArgumentException e) { + Asserts.expectedFailureContains(e, "Error evaluating node"); + } + } + + @Test + public void testExternalisedConfigFromPropertiesInLocation() throws Exception { + BrooklynProperties props = ((ManagementContextInternal)mgmt()).getBrooklynProperties(); + props.put("brooklyn.location.jclouds.aws-ec2.identity", "$brooklyn:external(\"myprovider\", \"mykey\")"); + props.put("brooklyn.location.jclouds.aws-ec2.credential", "$brooklyn:external(\"myprovider\", \"mykey2\")"); + + JcloudsLocation loc = (JcloudsLocation) mgmt().getLocationRegistry().resolve("jclouds:aws-ec2:us-east-1"); + assertEquals(loc.getIdentity(), "myval"); + assertEquals(loc.getCredential(), "myval2"); + } + + @Test + public void testExternalisedConfigInProperties() throws Exception { + runExternalisedConfigGetters("myproperty", "myval"); + } + + @Test + public void testExternalisedConfigInAddedStringProperty() throws Exception { + ((ManagementContextInternal)mgmt()).getBrooklynProperties().put( + "myDynamicProperty", "$brooklyn:external(\"myprovider\", \"mykey\")"); + runExternalisedConfigGetters("myDynamicProperty", "myval"); + } + + @Test + public void testExternalisedConfigInAddedKeyProperty() throws Exception { + ((ManagementContextInternal)mgmt()).getBrooklynProperties().put( + ConfigKeys.newStringConfigKey("myDynamicProperty"), "$brooklyn:external(\"myprovider\", \"mykey\")"); + runExternalisedConfigGetters("myDynamicProperty", "myval"); + } + + @Test + public void testExternalisedConfigInAddedMapProperty() throws Exception { + ((ManagementContextInternal)mgmt()).getBrooklynProperties().addFromMap( + ImmutableMap.of("myDynamicProperty", "$brooklyn:external(\"myprovider\", \"mykey\")")); + runExternalisedConfigGetters("myDynamicProperty", "myval"); + } + + protected void runExternalisedConfigGetters(String property, String expectedVal) throws Exception { + runExternalisedConfigGetters(((ManagementContextInternal)mgmt()).getBrooklynProperties(), property, expectedVal, true); + } + + protected void runExternalisedConfigGetters(BrooklynProperties props, String property, String expectedVal, boolean testSubMap) throws Exception { + ExecutionContext exec = mgmt().getServerExecutionContext(); + + String val1 = props.getConfig(ConfigKeys.newStringConfigKey(property)); + assertEquals(val1, expectedVal); + + DeferredSupplier<?> val2 = (DeferredSupplier<?>) props.getRawConfig(ConfigKeys.newStringConfigKey(property)); + assertEquals(Tasks.resolveValue(val2, String.class, exec), expectedVal); + + DeferredSupplier<?> val3 = (DeferredSupplier<?>) props.getConfigRaw(ConfigKeys.newStringConfigKey(property), false).get(); + assertEquals(Tasks.resolveValue(val3, String.class, exec), expectedVal); + + DeferredSupplier<?> val4 = (DeferredSupplier<?>) props.getAllConfig().get(ConfigKeys.newStringConfigKey(property)); + assertEquals(Tasks.resolveValue(val4, String.class, exec), expectedVal); + + String val5 = props.getFirst(property); + assertTrue(val5.startsWith("$brooklyn:external"), "val="+val5); + + if (testSubMap) { + BrooklynProperties submap = props.submap(ConfigPredicates.nameEqualTo(property)); + runExternalisedConfigGetters(submap, property, expectedVal, false); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java new file mode 100644 index 0000000..023b3e3 --- /dev/null +++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/internal/BrooklynPropertiesImpl.java @@ -0,0 +1,477 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.internal; + +import static com.google.common.base.Preconditions.checkNotNull; +import groovy.lang.Closure; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Properties; + +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.config.ConfigKey.HasConfigKey; +import org.apache.brooklyn.core.config.BasicConfigKey; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.ResourceUtils; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.os.Os; +import org.apache.brooklyn.util.text.StringFunctions; +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.base.CharMatcher; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; +import com.google.common.collect.Maps; + +/** + * TODO methods in this class are not thread safe. + * intention is that they are set during startup and not modified thereafter. + */ +@SuppressWarnings("rawtypes") +public class BrooklynPropertiesImpl extends LinkedHashMap implements BrooklynProperties { + + private static final long serialVersionUID = -945875483083108978L; + private static final Logger LOG = LoggerFactory.getLogger(BrooklynPropertiesImpl.class); + + public static class Factory { + /** creates a new empty {@link BrooklynPropertiesImpl} */ + public static BrooklynPropertiesImpl newEmpty() { + return new BrooklynPropertiesImpl(); + } + + /** creates a new {@link BrooklynPropertiesImpl} with contents loaded + * from the usual places, including *.properties files and environment variables */ + public static BrooklynPropertiesImpl newDefault() { + return new Builder(true).build(); + } + + public static Builder builderDefault() { + return new Builder(true); + } + + public static Builder builderEmpty() { + return new Builder(false); + } + + public static class Builder { + private String defaultLocationMetadataUrl; + private String globalLocationMetadataFile = null; + private String globalPropertiesFile = null; + private String localPropertiesFile = null; + private BrooklynPropertiesImpl originalProperties = null; + + /** @deprecated since 0.7.0 use static methods in {@link Factory} to create */ + public Builder() { + this(true); + } + + private Builder(boolean setGlobalFileDefaults) { + resetDefaultLocationMetadataUrl(); + if (setGlobalFileDefaults) { + resetGlobalFiles(); + } + } + + public Builder resetDefaultLocationMetadataUrl() { + defaultLocationMetadataUrl = "classpath://brooklyn/location-metadata.properties"; + return this; + } + public Builder resetGlobalFiles() { + defaultLocationMetadataUrl = "classpath://brooklyn/location-metadata.properties"; + globalLocationMetadataFile = Os.mergePaths(Os.home(), ".brooklyn", "location-metadata.properties"); + globalPropertiesFile = Os.mergePaths(Os.home(), ".brooklyn", "brooklyn.properties"); + return this; + } + + /** + * Creates a Builder that when built, will return the BrooklynProperties passed to this constructor + */ + private Builder(BrooklynPropertiesImpl originalProperties) { + this.originalProperties = new BrooklynPropertiesImpl().addFromMap(originalProperties); + } + + /** + * The URL of a default location-metadata.properties (for meta-data about different locations, such as iso3166 and global lat/lon). + * Defaults to classpath://brooklyn/location-metadata.properties + */ + public Builder defaultLocationMetadataUrl(String val) { + defaultLocationMetadataUrl = checkNotNull(val, "file"); + return this; + } + + /** + * The URL of a location-metadata.properties file that appends to and overwrites values in the locationMetadataUrl. + * Defaults to ~/.brooklyn/location-metadata.properties + */ + public Builder globalLocationMetadataFile(String val) { + globalLocationMetadataFile = checkNotNull(val, "file"); + return this; + } + + /** + * The URL of a shared brooklyn.properties file. Defaults to ~/.brooklyn/brooklyn.properties. + * Can be null to disable. + */ + public Builder globalPropertiesFile(String val) { + globalPropertiesFile = val; + return this; + } + + @Beta + public boolean hasDelegateOriginalProperties() { + return this.originalProperties==null; + } + + /** + * The URL of a brooklyn.properties file specific to this launch. Appends to and overwrites values in globalPropertiesFile. + */ + public Builder localPropertiesFile(String val) { + localPropertiesFile = val; + return this; + } + + public BrooklynPropertiesImpl build() { + if (originalProperties != null) + return new BrooklynPropertiesImpl().addFromMap(originalProperties); + + BrooklynPropertiesImpl properties = new BrooklynPropertiesImpl(); + + // TODO Could also read from http://brooklyn.io, for up-to-date values? + // But might that make unit tests run very badly when developer is offline? + addPropertiesFromUrl(properties, defaultLocationMetadataUrl, false); + + addPropertiesFromFile(properties, globalLocationMetadataFile); + addPropertiesFromFile(properties, globalPropertiesFile); + addPropertiesFromFile(properties, localPropertiesFile); + + properties.addEnvironmentVars(); + properties.addSystemProperties(); + + return properties; + } + + public static Builder fromProperties(BrooklynPropertiesImpl brooklynProperties) { + return new Builder(brooklynProperties); + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .omitNullValues() + .add("originalProperties", originalProperties) + .add("defaultLocationMetadataUrl", defaultLocationMetadataUrl) + .add("globalLocationMetadataUrl", globalLocationMetadataFile) + .add("globalPropertiesFile", globalPropertiesFile) + .add("localPropertiesFile", localPropertiesFile) + .toString(); + } + } + + private static void addPropertiesFromUrl(BrooklynPropertiesImpl p, String url, boolean warnIfNotFound) { + if (url==null) return; + + try { + p.addFrom(ResourceUtils.create(BrooklynPropertiesImpl.class).getResourceFromUrl(url)); + } catch (Exception e) { + if (warnIfNotFound) + LOG.warn("Could not load {}; continuing", url); + if (LOG.isTraceEnabled()) LOG.trace("Could not load "+url+"; continuing", e); + } + } + + private static void addPropertiesFromFile(BrooklynPropertiesImpl p, String file) { + if (file==null) return; + + String fileTidied = Os.tidyPath(file); + File f = new File(fileTidied); + + if (f.exists()) { + p.addFrom(f); + } + } + } + + protected BrooklynPropertiesImpl() { + } + + public BrooklynPropertiesImpl addEnvironmentVars() { + addFrom(System.getenv()); + return this; + } + + public BrooklynPropertiesImpl addSystemProperties() { + addFrom(System.getProperties()); + return this; + } + + public BrooklynPropertiesImpl addFrom(ConfigBag cfg) { + addFrom(cfg.getAllConfig()); + return this; + } + + @SuppressWarnings("unchecked") + public BrooklynPropertiesImpl addFrom(Map map) { + putAll(Maps.transformValues(map, StringFunctions.trim())); + return this; + } + + public BrooklynPropertiesImpl addFrom(InputStream i) { + // Ugly way to load them in order, but Properties is a Hashtable so loses order otherwise. + @SuppressWarnings({ "serial" }) + Properties p = new Properties() { + @Override + public synchronized Object put(Object key, Object value) { + // Trim the string values to remove leading and trailing spaces + String s = (String) value; + if (Strings.isBlank(s)) { + s = Strings.EMPTY; + } else { + s = CharMatcher.BREAKING_WHITESPACE.trimFrom(s); + } + return BrooklynPropertiesImpl.this.put(key, s); + } + }; + try { + p.load(i); + } catch (IOException e) { + throw Throwables.propagate(e); + } + return this; + } + + public BrooklynPropertiesImpl addFrom(File f) { + if (!f.exists()) { + LOG.warn("Unable to find file '"+f.getAbsolutePath()+"' when loading properties; ignoring"); + return this; + } else { + try { + return addFrom(new FileInputStream(f)); + } catch (FileNotFoundException e) { + throw Throwables.propagate(e); + } + } + } + public BrooklynPropertiesImpl addFrom(URL u) { + try { + return addFrom(u.openStream()); + } catch (IOException e) { + throw new RuntimeException("Error reading properties from "+u+": "+e, e); + } + } + /** + * @see ResourceUtils#getResourceFromUrl(String) + * + * of the form form file:///home/... or http:// or classpath://xx ; + * for convenience if not starting with xxx: it is treated as a classpath reference or a file; + * throws if not found (but does nothing if argument is null) + */ + public BrooklynPropertiesImpl addFromUrl(String url) { + try { + if (url==null) return this; + return addFrom(ResourceUtils.create(this).getResourceFromUrl(url)); + } catch (Exception e) { + throw new RuntimeException("Error reading properties from "+url+": "+e, e); + } + } + + /** expects a property already set in scope, whose value is acceptable to {@link #addFromUrl(String)}; + * if property not set, does nothing */ + public BrooklynPropertiesImpl addFromUrlProperty(String urlProperty) { + String url = (String) get(urlProperty); + if (url==null) addFromUrl(url); + return this; + } + + /** + * adds the indicated properties + */ + public BrooklynPropertiesImpl addFromMap(Map properties) { + putAll(properties); + return this; + } + + /** inserts the value under the given key, if it was not present */ + public boolean putIfAbsent(String key, Object value) { + if (containsKey(key)) return false; + put(key, value); + return true; + } + + /** @deprecated attempts to call get with this syntax are probably mistakes; get(key, defaultValue) is fine but + * Map is unlikely the key, much more likely they meant getFirst(flags, key). + */ + @Deprecated + public String get(Map flags, String key) { + LOG.warn("Discouraged use of 'BrooklynProperties.get(Map,String)' (ambiguous); use getFirst(Map,String) or get(String) -- assuming the former"); + LOG.debug("Trace for discouraged use of 'BrooklynProperties.get(Map,String)'", + new Throwable("Arguments: "+flags+" "+key)); + return getFirst(flags, key); + } + + /** returns the value of the first key which is defined + * <p> + * takes the following flags: + * 'warnIfNone', 'failIfNone' (both taking a boolean (to use default message) or a string (which is the message)); + * and 'defaultIfNone' (a default value to return if there is no such property); defaults to no warning and null response */ + @Override + public String getFirst(String ...keys) { + return getFirst(MutableMap.of(), keys); + } + @Override + public String getFirst(Map flags, String ...keys) { + for (String k: keys) { + if (k!=null && containsKey(k)) return (String) get(k); + } + if (flags.get("warnIfNone")!=null && !Boolean.FALSE.equals(flags.get("warnIfNone"))) { + if (Boolean.TRUE.equals(flags.get("warnIfNone"))) + LOG.warn("Unable to find Brooklyn property "+keys); + else + LOG.warn(""+flags.get("warnIfNone")); + } + if (flags.get("failIfNone")!=null && !Boolean.FALSE.equals(flags.get("failIfNone"))) { + Object f = flags.get("failIfNone"); + if (f instanceof Closure) + ((Closure)f).call((Object[])keys); + if (Boolean.TRUE.equals(f)) + throw new NoSuchElementException("Brooklyn unable to find mandatory property "+keys[0]+ + (keys.length>1 ? " (or "+(keys.length-1)+" other possible names, full list is "+Arrays.asList(keys)+")" : "") ); + else + throw new NoSuchElementException(""+f); + } + if (flags.get("defaultIfNone")!=null) { + return (String) flags.get("defaultIfNone"); + } + return null; + } + + @Override + public String toString() { + return "BrooklynProperties["+size()+"]"; + } + + /** like normal map.put, except config keys are dereferenced on the way in */ + @SuppressWarnings("unchecked") + public Object put(Object key, Object value) { + if (key instanceof HasConfigKey) key = ((HasConfigKey)key).getConfigKey().getName(); + if (key instanceof ConfigKey) key = ((ConfigKey)key).getName(); + return super.put(key, value); + } + + /** like normal map.putAll, except config keys are dereferenced on the way in */ + @Override + public void putAll(Map vals) { + for (Map.Entry<?,?> entry : ((Map<?,?>)vals).entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @SuppressWarnings("unchecked") + public <T> Object put(HasConfigKey<T> key, T value) { + return super.put(key.getConfigKey().getName(), value); + } + + @SuppressWarnings("unchecked") + public <T> Object put(ConfigKey<T> key, T value) { + return super.put(key.getName(), value); + } + + public <T> boolean putIfAbsent(ConfigKey<T> key, T value) { + return putIfAbsent(key.getName(), value); + } + + @Override + public <T> T getConfig(ConfigKey<T> key) { + return getConfig(key, null); + } + + @Override + public <T> T getConfig(HasConfigKey<T> key) { + return getConfig(key.getConfigKey(), null); + } + + @Override + public <T> T getConfig(HasConfigKey<T> key, T defaultValue) { + return getConfig(key.getConfigKey(), defaultValue); + } + + @Override + public <T> T getConfig(ConfigKey<T> key, T defaultValue) { + // TODO does not support MapConfigKey etc where entries use subkey notation; for now, access using submap + if (!containsKey(key.getName())) { + if (defaultValue!=null) return defaultValue; + return key.getDefaultValue(); + } + Object value = get(key.getName()); + if (value==null) return null; + // no evaluation / key extraction here + return TypeCoercions.coerce(value, key.getTypeToken()); + } + + @Override + public Object getRawConfig(ConfigKey<?> key) { + return get(key.getName()); + } + + @Override + public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) { + if (containsKey(key.getName())) return Maybe.of(get(key.getName())); + return Maybe.absent(); + } + + @Override + public Map<ConfigKey<?>, Object> getAllConfig() { + Map<ConfigKey<?>, Object> result = new LinkedHashMap<ConfigKey<?>, Object>(); + for (Object entry: entrySet()) + result.put(new BasicConfigKey<Object>(Object.class, ""+((Map.Entry)entry).getKey()), ((Map.Entry)entry).getValue()); + return result; + } + + @Override + public BrooklynPropertiesImpl submap(Predicate<ConfigKey<?>> filter) { + BrooklynPropertiesImpl result = Factory.newEmpty(); + for (Object entry: entrySet()) { + ConfigKey<?> k = new BasicConfigKey<Object>(Object.class, ""+((Map.Entry)entry).getKey()); + if (filter.apply(k)) + result.put(((Map.Entry)entry).getKey(), ((Map.Entry)entry).getValue()); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + public Map<String, Object> asMapWithStringKeys() { + return this; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/CampYamlParser.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/CampYamlParser.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/CampYamlParser.java new file mode 100644 index 0000000..35841be --- /dev/null +++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/CampYamlParser.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.mgmt.internal; + +import java.util.Map; + +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; + +public interface CampYamlParser { + + ConfigKey<CampYamlParser> YAML_PARSER_KEY = ConfigKeys.newConfigKey(CampYamlParser.class, "brooklyn.camp.yamlParser"); + + Map<String, Object> parse(Map<String, Object> map); + + Object parse(String val); + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java new file mode 100644 index 0000000..ae0c7a5 --- /dev/null +++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/DeferredBrooklynProperties.java @@ -0,0 +1,370 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.mgmt.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import org.apache.brooklyn.api.mgmt.ExecutionContext; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.config.ConfigKey.HasConfigKey; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.apache.brooklyn.util.core.task.DeferredSupplier; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.guava.Maybe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; + +/** + * Delegates to another {@link BrooklynProperties} implementation, but intercepts all calls to get. + * The results are transformed: if they are in the external-config format then they are + * automatically converted to {@link DeferredSupplier}. + * + * The external-config format is that same as that for camp-yaml blueprints (i.e. + * {@code $brooklyn:external("myprovider", "mykey")}. + */ +public class DeferredBrooklynProperties implements BrooklynProperties { + + private static final Logger LOG = LoggerFactory.getLogger(DeferredBrooklynProperties.class); + + private static final String BROOKLYN_YAML_PREFIX = "$brooklyn:"; + + private final BrooklynProperties delegate; + private final ManagementContextInternal mgmt; + + public DeferredBrooklynProperties(BrooklynProperties delegate, ManagementContextInternal mgmt) { + this.delegate = checkNotNull(delegate, "delegate"); + this.mgmt = checkNotNull(mgmt, "mgmt"); + } + + private Object transform(ConfigKey<?> key, Object value) { + if (value instanceof CharSequence) { + String raw = value.toString(); + if (raw.startsWith(BROOKLYN_YAML_PREFIX)) { + CampYamlParser parser = mgmt.getConfig().getConfig(CampYamlParser.YAML_PARSER_KEY); + if (parser == null) { + // TODO Should we fail or return the untransformed value? + // Problem is this gets called during initialisation, e.g. by BrooklynFeatureEnablement calling asMapWithStringKeys() + // throw new IllegalStateException("Cannot parse external-config for "+key+" because no camp-yaml parser available"); + LOG.debug("Not transforming external-config {}, as no camp-yaml parser available", key); + return value; + } + return parser.parse(raw); + } + } + return value; + } + + private <T> T resolve(ConfigKey<T> key, Object value) { + Object transformed = transform(key, value); + + Object result; + if (transformed instanceof DeferredSupplier) { + ExecutionContext exec = mgmt.getServerExecutionContext(); + try { + result = Tasks.resolveValue(transformed, key.getType(), exec); + } catch (ExecutionException | InterruptedException e) { + throw Exceptions.propagate(e); + } + } else { + result = transformed; + } + + return TypeCoercions.coerce(result, key.getTypeToken()); + } + + @Override + public <T> T getConfig(ConfigKey<T> key) { + T raw = delegate.getConfig(key); + return resolve(key, raw); + } + + @Override + public <T> T getConfig(HasConfigKey<T> key) { + T raw = delegate.getConfig(key); + return resolve(key.getConfigKey(), raw); + } + + @Override + public <T> T getConfig(HasConfigKey<T> key, T defaultValue) { + T raw = delegate.getConfig(key, defaultValue); + return resolve(key.getConfigKey(), raw); + } + + @Override + public <T> T getConfig(ConfigKey<T> key, T defaultValue) { + T raw = delegate.getConfig(key, defaultValue); + return resolve(key, raw); + } + + @Deprecated + @Override + public Object getRawConfig(ConfigKey<?> key) { + return transform(key, delegate.getRawConfig(key)); + } + + @Override + public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) { + Maybe<Object> result = delegate.getConfigRaw(key, includeInherited); + return (result.isPresent()) ? Maybe.of(transform(key, result.get())) : Maybe.absent(); + } + + @Override + public Map<ConfigKey<?>, Object> getAllConfig() { + Map<ConfigKey<?>, Object> raw = delegate.getAllConfig(); + Map<ConfigKey<?>, Object> result = Maps.newLinkedHashMap(); + for (Map.Entry<ConfigKey<?>, Object> entry : raw.entrySet()) { + result.put(entry.getKey(), transform(entry.getKey(), entry.getValue())); + } + return result; + } + + @Override + public Map<String, Object> asMapWithStringKeys() { + Map<ConfigKey<?>, Object> raw = delegate.getAllConfig(); + Map<String, Object> result = Maps.newLinkedHashMap(); + for (Map.Entry<ConfigKey<?>, Object> entry : raw.entrySet()) { + result.put(entry.getKey().getName(), transform(entry.getKey(), entry.getValue())); + } + return result; + } + + /** + * Discouraged; returns the String so if it is external config, it will be the + * {@code $brooklyn:external(...)} format. + */ + @Override + @SuppressWarnings("rawtypes") + @Deprecated + public String get(Map flags, String key) { + return delegate.get(flags, key); + } + + /** + * Discouraged; returns the String so if it is external config, it will be the + * {@code $brooklyn:external(...)} format. + */ + @Override + public String getFirst(String ...keys) { + return delegate.getFirst(keys); + } + + /** + * Discouraged; returns the String so if it is external config, it will be the + * {@code $brooklyn:external(...)} format. + */ + @Override + @SuppressWarnings("rawtypes") + public String getFirst(Map flags, String ...keys) { + return delegate.getFirst(flags, keys); + } + + @Override + public BrooklynProperties submap(Predicate<ConfigKey<?>> filter) { + BrooklynProperties submap = delegate.submap(filter); + return new DeferredBrooklynProperties(submap, mgmt); + } + + @Override + public BrooklynProperties addEnvironmentVars() { + delegate.addEnvironmentVars(); + return this; + } + + @Override + public BrooklynProperties addSystemProperties() { + delegate.addSystemProperties(); + return this; + } + + @Override + public BrooklynProperties addFrom(ConfigBag cfg) { + delegate.addFrom(cfg); + return this; + } + + @Override + @SuppressWarnings("rawtypes") + public BrooklynProperties addFrom(Map map) { + delegate.addFrom(map); + return this; + } + + @Override + public BrooklynProperties addFrom(InputStream i) { + delegate.addFrom(i); + return this; + } + + @Override + public BrooklynProperties addFrom(File f) { + delegate.addFrom(f); + return this; + } + + @Override + public BrooklynProperties addFrom(URL u) { + delegate.addFrom(u); + return this; + } + + @Override + public BrooklynProperties addFromUrl(String url) { + delegate.addFromUrl(url); + return this; + } + + @Override + public BrooklynProperties addFromUrlProperty(String urlProperty) { + delegate.addFromUrlProperty(urlProperty); + return this; + } + + @Override + @SuppressWarnings("rawtypes") + public BrooklynProperties addFromMap(Map properties) { + delegate.addFromMap(properties); + return this; + } + + @Override + public boolean putIfAbsent(String key, Object value) { + return delegate.putIfAbsent(key, value); + } + + @Override + public String toString() { + return delegate.toString(); + } + + @Override + public Object put(Object key, Object value) { + return delegate.put(key, value); + } + + @Override + @SuppressWarnings("rawtypes") + public void putAll(Map vals) { + delegate.putAll(vals); + } + + @Override + public <T> Object put(HasConfigKey<T> key, T value) { + return delegate.put(key, value); + } + + @Override + public <T> Object put(ConfigKey<T> key, T value) { + return delegate.put(key, value); + } + + @Override + public <T> boolean putIfAbsent(ConfigKey<T> key, T value) { + return delegate.putIfAbsent(key, value); + } + + + ////////////////////////////////////////////////////////////////////////////////// + // Methods below from java.util.LinkedHashMap, which BrooklynProperties extends // + ////////////////////////////////////////////////////////////////////////////////// + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate.containsValue(value); + } + + @Override + public Object get(Object key) { + return delegate.get(key); + } + + @Override + public Object remove(Object key) { + return delegate.remove(key); + } + + @Override + public void clear() { + delegate.clear(); + } + + @Override + @SuppressWarnings("rawtypes") + public Set keySet() { + return delegate.keySet(); + } + + @Override + @SuppressWarnings("rawtypes") + public Collection values() { + return delegate.values(); + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Set<Map.Entry> entrySet() { + return delegate.entrySet(); + } + + @Override + public boolean equals(Object o) { + return delegate.equals(o); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + // put(Object, Object) already overridden + //@Override + //public Object put(Object key, Object value) { + + // putAll(Map) already overridden + //@Override + //public void putAll(Map m) { +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/VersionComparator.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/VersionComparator.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/VersionComparator.java new file mode 100644 index 0000000..94553b0 --- /dev/null +++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/VersionComparator.java @@ -0,0 +1,199 @@ +/* + * 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.text; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; + +import org.apache.brooklyn.util.text.NaturalOrderComparator; +import org.apache.brooklyn.util.text.Strings; + +import com.google.common.annotations.VisibleForTesting; + +/** + * {@link Comparator} for version strings. + * <p> + * SNAPSHOT items always lowest rated, + * then splitting on dots, + * using natural order comparator (so "9" < "10" and "4u8" < "4u20"), + * and preferring segments without qualifiers ("4" > "4beta"). + * <p> + * Impossible to follow semantics for all versioning schemes but + * does the obvious right thing for normal schemes + * and pretty well in fringe cases. + * <p> + * See test case for lots of examples. + */ +public class VersionComparator implements Comparator<String> { + + private static final String SNAPSHOT = "SNAPSHOT"; + + public static final VersionComparator INSTANCE = new VersionComparator(); + + public static VersionComparator getInstance() { + return INSTANCE; + } + + @Override + public int compare(String v1, String v2) { + if (v1==null && v2==null) return 0; + if (v1==null) return -1; + if (v2==null) return 1; + + boolean isV1Snapshot = v1.toUpperCase().contains(SNAPSHOT); + boolean isV2Snapshot = v2.toUpperCase().contains(SNAPSHOT); + if (isV1Snapshot == isV2Snapshot) { + // if snapshot status is the same, look at dot-split parts first + return compareDotSplitParts(splitOnDot(v1), splitOnDot(v2)); + } else { + // snapshot goes first + return isV1Snapshot ? -1 : 1; + } + } + + @VisibleForTesting + static String[] splitOnDot(String v) { + return v.split("(?<=\\.)|(?=\\.)"); + } + + private int compareDotSplitParts(String[] v1Parts, String[] v2Parts) { + for (int i = 0; ; i++) { + if (i >= v1Parts.length && i >= v2Parts.length) { + // end of both + return 0; + } + if (i == v1Parts.length) { + // sequence depends whether the extra part *starts with* a number + // ie + // 2.0 < 2.0.0 + // and + // 2.0.qualifier < 2.0 < 2.0.0qualifier < 2.0.0-qualifier < 2.0.0.qualifier < 2.0.0 < 2.0.9-qualifier + return isNumberInFirstCharPossiblyAfterADot(v2Parts, i) ? -1 : 1; + } + if (i == v2Parts.length) { + // as above but inverted + return isNumberInFirstCharPossiblyAfterADot(v1Parts, i) ? 1 : -1; + } + // not at end; compare this dot split part + + int result = compareDotSplitPart(v1Parts[i], v2Parts[i]); + if (result!=0) return result; + } + } + + private int compareDotSplitPart(String v1, String v2) { + String[] v1Parts = splitOnNonWordChar(v1); + String[] v2Parts = splitOnNonWordChar(v2); + + for (int i = 0; ; i++) { + if (i >= v1Parts.length && i >= v2Parts.length) { + // end of both + return 0; + } + if (i == v1Parts.length) { + // shorter set always wins here; i.e. + // 1-qualifier < 1 + return 1; + } + if (i == v2Parts.length) { + // as above but inverted + return -1; + } + // not at end; compare this dot split part + + String v1p = v1Parts[i]; + String v2p = v2Parts[i]; + + if (v1p.equals(v2p)) continue; + + if (isNumberInFirstChar(v1p) || isNumberInFirstChar(v2p)) { + // something starting with a number is higher than something not + if (!isNumberInFirstChar(v1p)) return -1; + if (!isNumberInFirstChar(v2p)) return 1; + + // both start with numbers; can use natural order comparison *unless* + // one is purely a number AND the other *begins* with that number, + // followed by non-digit chars, in which case prefer the pure number + // ie: + // 1beta < 1 + // but note + // 1 < 2beta < 11beta + if (isNumber(v1p) || isNumber(v2p)) { + if (!isNumber(v1p)) { + if (v1p.startsWith(v2p)) { + if (!isNumberInFirstChar(Strings.removeFromStart(v1p, v2p))) { + // v2 is a number, and v1 is the same followed by non-numbers + return -1; + } + } + } + if (!isNumber(v2p)) { + // as above but inverted + if (v2p.startsWith(v1p)) { + if (!isNumberInFirstChar(Strings.removeFromStart(v2p, v1p))) { + return 1; + } + } + } + // both numbers, skip to natural order comparison + } + } + + // otherwise it is in-order + int result = NaturalOrderComparator.INSTANCE.compare(v1p, v2p); + if (result!=0) return result; + } + } + + @VisibleForTesting + static String[] splitOnNonWordChar(String v) { + Collection<String> parts = new ArrayList<String>(); + String remaining = v; + + // use lookahead to split on all non-letter non-numbers, putting them into their own buckets + parts.addAll(Arrays.asList(remaining.split("(?<=[^0-9\\p{L}])|(?=[^0-9\\p{L}])"))); + return parts.toArray(new String[parts.size()]); + } + + @VisibleForTesting + static boolean isNumberInFirstCharPossiblyAfterADot(String[] parts, int i) { + if (parts==null || parts.length<=i) return false; + if (isNumberInFirstChar(parts[i])) return true; + if (".".equals(parts[i])) { + if (parts.length>i+1) + if (isNumberInFirstChar(parts[i+1])) + return true; + } + return false; + } + + @VisibleForTesting + static boolean isNumberInFirstChar(String v) { + if (v==null || v.length()==0) return false; + return Character.isDigit(v.charAt(0)); + } + + @VisibleForTesting + static boolean isNumber(String v) { + if (v==null || v.length()==0) return false; + return v.matches("[\\d]+"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8a3c17b9/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/VersionComparatorTest.java ---------------------------------------------------------------------- diff --git a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/VersionComparatorTest.java b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/VersionComparatorTest.java new file mode 100644 index 0000000..00fdb6e --- /dev/null +++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/VersionComparatorTest.java @@ -0,0 +1,102 @@ +/* + * 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.text; + +import static org.testng.Assert.assertEquals; + +import java.util.List; + +import org.apache.brooklyn.util.collections.MutableList; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class VersionComparatorTest { + + @Test + public void testStaticHelpers() { + Assert.assertEquals(VersionComparator.splitOnDot("a.b.cc"), new String[] { "a", ".", "b", ".", "cc" }); + Assert.assertEquals(VersionComparator.splitOnDot("a..b-c"), new String[] { "a", ".", ".", "b-c" }); + + Assert.assertEquals(VersionComparator.splitOnNonWordChar("a1-b__cc9c"), new String[] { + "a1", "-", "b", "_", "_", "cc9c" }); + + Assert.assertEquals(VersionComparator.isNumberInFirstChar("1a"), true); + Assert.assertEquals(VersionComparator.isNumberInFirstChar("a1"), false); + Assert.assertEquals(VersionComparator.isNumberInFirstChar(""), false); + Assert.assertEquals(VersionComparator.isNumberInFirstChar(null), false); + + Assert.assertEquals(VersionComparator.isNumber("1"), true); + Assert.assertEquals(VersionComparator.isNumber("1111"), true); + Assert.assertEquals(VersionComparator.isNumber("1a"), false); + Assert.assertEquals(VersionComparator.isNumber("a1"), false); + Assert.assertEquals(VersionComparator.isNumber(""), false); + Assert.assertEquals(VersionComparator.isNumber(null), false); + } + + @Test + public void testComparison() { + VersionComparator.INSTANCE.compare("B", "B-2"); + + assertVersionOrder("0", "1"); + assertVersionOrder("0", "0.0", "0.9", "0.10", "0.10.0", "1"); + + assertVersionOrder("a", "b"); + + assertVersionOrder("1beta", "1", "2beta", "11beta"); + assertVersionOrder("beta", "0", "1beta", "1-alpha", "1", "11beta", "11-alpha", "11"); + assertVersionOrder("1.0-a", "1.0-b", "1.0"); + + assertVersionOrder("qualifier", "0qualifier", "0-qualifier", "0", "1-qualifier", "1"); + + assertVersionOrder("2.0.qualifier", "2.0", "2.0.0qualifier", "2.0.0-qualifier", "2.0.0.qualifier", "2.0.0"); + assertVersionOrder("2.0.qualifier.0", "2.0", "2.0.0qualifier.0", "2.0.0-qualifier.0", "2.0.0.qualifier.0", "2.0.0", "2.0.0.0"); + + assertVersionOrder("0", "0.0", "0.1", "0.1.0", "0.1.1", "0.2", "0.2.1", "1", "1.0", "2"); + // case sensitive + assertVersionOrder("AA", "Aa", "aa"); + // letters in order, ignoring case, and using natural order on numbers, splitting at word boundaries + assertVersionOrder("A", "B-2", "B-10", "B", "B0", "C", "b", "b1", "b9", "b10", "c", "0"); + // and non-letter symbols are compared, in alpha order (e.g. - less than _) with dots even higher + assertVersionOrder("0-qual", "0", "0.1", "1-qualC", "1_qualB", "1.qualA", "1", "1.0"); + + // numeric comparison works with qualifiers, preferring unqualified + assertVersionOrder("0--qual", "0-qual", "0-qualB", "0-qualB2", "0-qualB10", "0-qualC", "0.qualA", "0", "0.1.qual", "0.1", "1"); + + // all snapshots rated lower + assertVersionOrder( + "0_SNAPSHOT", "0.1.SNAPSHOT", "1-SNAPSHOT-X-X", "1-SNAPSHOT-X", "1-SNAPSHOT-XX-X", "1-SNAPSHOT-XX", "1-SNAPSHOT", + "1.0-SNAPSHOT-B", "1.0.SNAPSHOT-A", + "1.2-SNAPSHOT", "1.10-SNAPSHOT", + "qualifer", + "0", "0.1", "1"); + } + + private static void assertVersionOrder(String v1, String v2, String ...otherVersions) { + List<String> versions = MutableList.<String>of().append(v1, v2, otherVersions); + + for (int i=0; i<versions.size(); i++) { + for (int j=0; j<versions.size(); j++) { + assertEquals(VersionComparator.getInstance().compare( + versions.get(i), versions.get(j)), + new Integer(i).compareTo(j), "comparing "+versions.get(i)+" and "+versions.get(j)); + } + } + } + +}
