| /* |
| * 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.catalog; |
| |
| import static org.testng.Assert.assertEquals; |
| |
| import java.util.Map; |
| |
| import org.apache.brooklyn.api.effector.Effector; |
| 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.LocationSpec; |
| import org.apache.brooklyn.api.mgmt.classloading.BrooklynClassLoadingContext; |
| import org.apache.brooklyn.api.policy.Policy; |
| import org.apache.brooklyn.api.typereg.ManagedBundle; |
| import org.apache.brooklyn.api.typereg.RegisteredType; |
| import org.apache.brooklyn.camp.brooklyn.AbstractYamlRebindTest; |
| import org.apache.brooklyn.core.catalog.internal.CatalogUtils; |
| import org.apache.brooklyn.core.effector.Effectors; |
| import org.apache.brooklyn.core.entity.EntityInternal; |
| import org.apache.brooklyn.core.entity.StartableApplication; |
| import org.apache.brooklyn.core.entity.trait.Startable; |
| import org.apache.brooklyn.core.mgmt.ha.OsgiBundleInstallationResult; |
| import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; |
| import org.apache.brooklyn.core.mgmt.osgi.OsgiVersionMoreEntityTest; |
| import org.apache.brooklyn.core.test.entity.TestEntity; |
| 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.collections.MutableMap; |
| import org.apache.brooklyn.util.core.ClassLoaderUtils; |
| import org.apache.brooklyn.util.core.ResourceUtils; |
| import org.apache.brooklyn.util.exceptions.ReferenceWithError; |
| import org.apache.brooklyn.util.javalang.Reflections; |
| import org.apache.brooklyn.util.osgi.OsgiTestResources; |
| import org.apache.brooklyn.util.osgi.VersionedName; |
| import org.apache.brooklyn.util.text.Strings; |
| import org.osgi.framework.Bundle; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.testng.Assert; |
| import org.testng.annotations.Test; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Iterables; |
| |
| /** Many of the same tests as per {@link OsgiVersionMoreEntityTest} but using YAML for catalog and entities, so catalog item ID is set automatically */ |
| public class CatalogOsgiVersionMoreEntityRebindTest extends AbstractYamlRebindTest implements OsgiTestResources { |
| |
| @SuppressWarnings("unused") |
| private static final Logger log = LoggerFactory.getLogger(CatalogOsgiVersionMoreEntityRebindTest.class); |
| |
| @Override |
| protected boolean useOsgi() { |
| return true; |
| } |
| |
| @Test |
| public void testRebindAppIncludingBundleAllWorksAndPreservesChecksum() throws Exception { |
| TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_V1_PATH); |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V1_URL) ); |
| |
| RegisteredType item = mgmt().getTypeRegistry().get(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| Assert.assertNotNull(item); |
| Assert.assertEquals(item.getContainingBundle(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.1.0"); |
| |
| ManagedBundle mb = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(VersionedName.fromString(item.getContainingBundle())); |
| Assert.assertNotNull(mb); |
| String c1 = mb.getChecksum(); |
| Assert.assertTrue(Strings.isNonBlank(c1), "Missing checksum for bundle"); |
| |
| Map<String, ManagedBundle> bundles1 = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles(); |
| |
| createAndStartApplication("services: [ { type: "+BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY+" } ]"); |
| |
| StartableApplication newApp = rebind(); |
| |
| // bundles installed |
| Map<String, ManagedBundle> bundles = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundles(); |
| Asserts.assertEquals(bundles, bundles1); |
| |
| //item installed |
| item = mgmt().getTypeRegistry().get(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| Assert.assertNotNull(item); |
| Assert.assertEquals(item.getContainingBundle(), OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.1.0"); |
| |
| // containing bundle set, matches, and checksum matches |
| mb = ((ManagementContextInternal)mgmt()).getOsgiManager().get().getManagedBundle(VersionedName.fromString(item.getContainingBundle())); |
| Assert.assertEquals(mb, bundles.get(mb.getId())); |
| Assert.assertEquals(mb.getChecksum(), c1, "checksums should be the same after rebinding"); |
| |
| Assert.assertNotNull(newApp); |
| } |
| |
| @Test |
| public void testPolicyInBundleReferencedByStockCatalogItem() throws Exception { |
| TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH); |
| |
| String policyType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_POLICY; |
| |
| addCatalogItems( |
| "brooklyn.catalog:", |
| " id: wrapped-entity", |
| " version: 1.0", |
| " item:", |
| " services:", |
| " - type: " + TestEntity.class.getName()); |
| |
| addCatalogItems( |
| "brooklyn.catalog:", |
| " id: with-policy-from-library", |
| " version: 1.0", |
| " brooklyn.libraries:", |
| " - classpath:" + OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH, |
| " item:", |
| " services:", |
| " - type: " + BasicApplication.class.getName(), |
| " brooklyn.children:", |
| " - type: wrapped-entity:1.0", |
| " brooklyn.policies:", |
| " - type: " + policyType); |
| |
| Entity app = createAndStartApplication("services: [ { type: 'with-policy-from-library:1.0' } ]"); |
| Entity entity = Iterables.getOnlyElement(app.getChildren()); |
| Policy policy = Iterables.getOnlyElement(entity.policies()); |
| assertEquals(policy.getPolicyType().getName(), policyType); |
| |
| StartableApplication newApp = rebind(); |
| Entity newEntity = Iterables.getOnlyElement(newApp.getChildren()); |
| Policy newPolicy = Iterables.getOnlyElement(newEntity.policies()); |
| assertEquals(newPolicy.getPolicyType().getName(), policyType); |
| } |
| |
| // See https://issues.apache.org/jira/browse/BROOKLYN-410 |
| @Test |
| @SuppressWarnings("unchecked") |
| public void testRebindsLocationFromBundle() throws Exception { |
| TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH); |
| |
| String locationType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_LOCATION; |
| String locationTypeWithBundlePrefix = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_SYMBOLIC_NAME_FULL + ":" + locationType; |
| |
| addCatalogItems( |
| "brooklyn.catalog:", |
| " id: with-library", |
| " version: 1.0", |
| " brooklyn.libraries:", |
| " - classpath:" + OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH, |
| " item:", |
| " services:", |
| " - type: " + BasicApplication.class.getName(), |
| " brooklyn.children:", |
| " - type: " + TestEntity.class.getName()); |
| |
| Entity app = createAndStartApplication("services: [ { type: 'with-library:1.0' } ]"); |
| Entity entity = Iterables.getOnlyElement(app.getChildren()); |
| |
| // Add a location that can only be classloaded from the `brooklyn.libraries` bundle |
| Reflections reflections = new Reflections(CatalogOsgiVersionMoreEntityRebindTest.class.getClassLoader()); |
| Class<? extends Location> locationClazz = (Class<? extends Location>) new ClassLoaderUtils(reflections.getClassLoader(), mgmt()).loadClass(locationTypeWithBundlePrefix); |
| Location loc = mgmt().getLocationManager().createLocation(LocationSpec.create(locationClazz)); |
| ((EntityInternal)entity).addLocations(ImmutableList.of(loc)); |
| |
| // Confirm that we can rebind, and thus instantiate that location |
| StartableApplication newApp = rebind(); |
| Entity newEntity = Iterables.getOnlyElement(newApp.getChildren()); |
| Location newLoc = Iterables.getOnlyElement(newEntity.getLocations()); |
| assertEquals(newLoc.getClass().getName(), locationType); |
| } |
| |
| @Test |
| public void testEffectorInBundleReferencedByStockCatalogItem() throws Exception { |
| TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH); |
| |
| String effectorType = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_EFFECTOR; |
| |
| addCatalogItems( |
| "brooklyn.catalog:", |
| " id: wrapped-entity", |
| " version: 1.0", |
| " item:", |
| " services:", |
| " - type: " + TestEntity.class.getName()); |
| |
| addCatalogItems( |
| "brooklyn.catalog:", |
| " id: with-effector-from-library", |
| " version: 1.0", |
| " brooklyn.libraries:", |
| " - classpath:" + OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH, |
| " item:", |
| " services:", |
| " - type: " + BasicApplication.class.getName(), |
| " brooklyn.children:", |
| " - type: wrapped-entity:1.0", |
| " brooklyn.initializers:", |
| " - type: " + effectorType); |
| |
| Entity app = createAndStartApplication("services: [ { type: 'with-effector-from-library:1.0' } ]"); |
| Entity entity = Iterables.getOnlyElement(app.getChildren()); |
| Effector<?> effector = entity.getEntityType().getEffectorByName("myEffector").get(); |
| entity.invoke(effector, ImmutableMap.<String, Object>of()).get(); |
| |
| StartableApplication newApp = rebind(); |
| Entity newEntity = Iterables.getOnlyElement(newApp.getChildren()); |
| Effector<?> newEffector = newEntity.getEntityType().getEffectorByName("myEffector").get(); |
| newEntity.invoke(newEffector, ImmutableMap.<String, Object>of()).get(); |
| } |
| |
| @Test |
| public void testClassAccessAfterUninstall() throws Exception { |
| TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH); |
| |
| // install dependency |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL) ); |
| |
| // now the v2 bundle |
| OsgiBundleInstallationResult b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL) ).get(); |
| |
| Assert.assertEquals(b.getVersionedName().toString(), BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.2.0"); |
| |
| String yaml = Strings.lines("name: simple-app-yaml", |
| "services:", |
| "- type: " + BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| Entity app = createAndStartApplication(yaml); |
| Entity more = Iterables.getOnlyElement( app.getChildren() ); |
| |
| Assert.assertEquals( |
| more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Bob")).get(), |
| "HI BOB FROM V2"); |
| |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().uninstallUploadedBundle(b.getMetadata()); |
| Assert.assertEquals(b.getBundle().getState(), Bundle.UNINSTALLED); |
| |
| // can still call things |
| Assert.assertEquals( |
| more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Claudia")).get(), |
| "HI CLAUDIA FROM V2"); |
| |
| // but still uninstalled, and attempt to create makes error |
| Assert.assertEquals(b.getBundle().getState(), Bundle.UNINSTALLED); |
| try { |
| Entity app2 = createAndStartApplication(yaml); |
| Asserts.shouldHaveFailedPreviously("Expected deployment to fail after uninstall; instead got "+app2); |
| } catch (Exception e) { |
| Asserts.expectedFailureContainsIgnoreCase(e, "unable to match", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| } |
| |
| try { |
| StartableApplication app2 = rebind(); |
| Asserts.shouldHaveFailedPreviously("Expected deployment to fail rebind; instead got "+app2); |
| } catch (Exception e) { |
| // should fail to rebind this entity |
| Asserts.expectedFailureContainsIgnoreCase(e, more.getId(), "unable to load", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| } |
| } |
| |
| // @Test |
| public void testClassAccessAfterUpgrade() throws Exception { |
| TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_MORE_ENTITIES_0_1_0_PATH); |
| |
| // install dependency |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL) ).checkNoError(); |
| |
| // now the v2 bundle |
| OsgiBundleInstallationResult b2a = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL) ).get(); |
| |
| Assert.assertEquals(b2a.getVersionedName().toString(), BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL+":"+"0.2.0"); |
| Assert.assertEquals(b2a.getCode(), OsgiBundleInstallationResult.ResultCode.INSTALLED_NEW_BUNDLE); |
| |
| String yaml = Strings.lines("name: simple-app-yaml", |
| "services:", |
| "- type: " + BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| Entity app = createAndStartApplication(yaml); |
| Entity more = Iterables.getOnlyElement( app.getChildren() ); |
| |
| Assert.assertEquals( |
| more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Bob")).get(), |
| "HI BOB FROM V2"); |
| |
| // unforced upgrade should report already installed |
| ReferenceWithError<OsgiBundleInstallationResult> installEvilTwin = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL) ); |
| Assert.assertTrue(installEvilTwin.hasError()); |
| Assert.assertEquals(installEvilTwin.getWithoutError().getCode(), |
| OsgiBundleInstallationResult.ResultCode.ERROR_PREPARING_BUNDLE); |
| |
| // force upgrade |
| OsgiBundleInstallationResult b2b = ((ManagementContextInternal)mgmt()).getOsgiManager().get().install(b2a.getMetadata(), |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_URL), true, true, true).get(); |
| Assert.assertEquals(b2a.getBundle(), b2b.getBundle()); |
| Assert.assertEquals(b2b.getCode(), OsgiBundleInstallationResult.ResultCode.UPDATED_EXISTING_BUNDLE); |
| |
| // calls to things previously instantiated get the old behaviour |
| Assert.assertEquals( |
| more.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Claudia")).get(), |
| "HI CLAUDIA FROM V2"); |
| |
| // but new deployment gets the new behaviour |
| StartableApplication app2 = (StartableApplication) createAndStartApplication(yaml); |
| Entity more2 = Iterables.getOnlyElement( app2.getChildren() ); |
| Assert.assertEquals( |
| more2.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Daphne")).get(), |
| "HO DAPHNE FROM V2 EVIL TWIN"); |
| app2.stop(); |
| |
| // and after rebind on the old we get new behaviour |
| StartableApplication app1 = rebind(); |
| Entity more1 = Iterables.getOnlyElement( app1.getChildren() ); |
| Assert.assertEquals( |
| more1.invoke(Effectors.effector(String.class, "sayHI").buildAbstract(), MutableMap.of("name", "Eric")).get(), |
| "HO ERIC FROM V2 EVIL TWIN"); |
| } |
| |
| @Test |
| public void testClusterWithEntitySpecFromOsgi() throws Exception { |
| // install dependencies |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL) ).checkNoError(); |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL) ).get(); |
| |
| RegisteredType ci = Preconditions.checkNotNull( mgmt().getTypeRegistry().get(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY) ); |
| EntitySpec<DynamicCluster> clusterSpec = EntitySpec.create(DynamicCluster.class) |
| .configure(DynamicCluster.INITIAL_SIZE, 1) |
| .configure(DynamicCluster.MEMBER_SPEC, origManagementContext.getTypeRegistry().createSpec(ci, null, EntitySpec.class)); |
| |
| final Entity app = mgmt().getEntityManager().createEntity(EntitySpec.create(BasicApplication.class).child(clusterSpec)); |
| app.invoke(Startable.START, MutableMap.of()).get(); |
| |
| rebind(); |
| } |
| |
| protected RegisteredType installWrappedMoreEntity() { |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_OSGI_ENTITIES_URL) ).checkNoError(); |
| ((ManagementContextInternal)mgmt()).getOsgiManager().get().install( |
| new ResourceUtils(getClass()).getResourceFromUrl(BROOKLYN_TEST_MORE_ENTITIES_V2_URL) ).get(); |
| addCatalogItems( |
| "brooklyn.catalog:", |
| " id: wrapped-more-entity", |
| " version: 1.0", |
| " item:", |
| " services:", |
| " - type: " + BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| |
| RegisteredType ci = Preconditions.checkNotNull( mgmt().getTypeRegistry().get("wrapped-more-entity") ); |
| return ci; |
| } |
| |
| @Test |
| public void testRebindsClusterWithEntitySpecWrappingOsgi() throws Exception { |
| RegisteredType ci = installWrappedMoreEntity(); |
| EntitySpec<DynamicCluster> clusterSpec = EntitySpec.create(DynamicCluster.class) |
| .configure(DynamicCluster.INITIAL_SIZE, 1) |
| .configure(DynamicCluster.MEMBER_SPEC, origManagementContext.getTypeRegistry().createSpec(ci, null, EntitySpec.class)); |
| |
| final Entity app = mgmt().getEntityManager().createEntity(EntitySpec.create(BasicApplication.class).child(clusterSpec)); |
| app.invoke(Startable.START, MutableMap.of()).get(); |
| |
| rebind(); |
| } |
| |
| /** Does the class loader for the wrapped-more-entity type inherit more-entity's class loader? |
| * Was tempting to say yes but the implementation is hard as we need to add API methods to find the |
| * supertype (or supertypes). We might do that at which point we could change these semantics if we wished. |
| * However there is also an argument that the instantiation engine determines where inherited loaders |
| * behave transitively and where they don't, so we shouldn't have a blanket rule that you can always |
| * see someone else's loaders just by extending them. In any case the "compelling use case" for |
| * considering this (and noticing it, and adding comments to {@link RegisteredType#getLibraries()}) |
| * is xml deserialization of entity specs in persisted state, and: |
| * (a) there are other ways to do that, and |
| * (b) we'd like to move away from that and use the same yaml-based instantiation engine used for initial construction. */ |
| @Test |
| public void testWrappedEntityClassLoaderDoesntHaveAncestorClassLoader() throws Exception { |
| RegisteredType ci = installWrappedMoreEntity(); |
| BrooklynClassLoadingContext clc = CatalogUtils.newClassLoadingContext(mgmt(), ci); |
| try { |
| clc.loadClass(BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| Asserts.shouldHaveFailedPreviously(); |
| } catch (Exception e) { |
| Asserts.expectedFailureContainsIgnoreCase(e, "unable to load", BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| } |
| } |
| |
| // could support this now that we have a local cache; but probably not needed; see BasicBrooklynCatalog.scanAnnotationsInBundle |
| // @Test |
| // public void testRebindJavaScanningBundleInCatalog() throws Exception { |
| // CatalogScanOsgiTest.installJavaScanningMoreEntitiesV2(mgmt(), this); |
| // rebind(); |
| // RegisteredType item = mgmt().getTypeRegistry().get(OsgiTestResources.BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY); |
| // Assert.assertNotNull(item, "Scanned item should have been available after rebind"); |
| // } |
| |
| } |