/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.brooklyn.core.mgmt.rebind; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNotSame; import java.io.Closeable; import java.net.URL; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; import org.apache.brooklyn.core.catalog.internal.CatalogInitialization; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.AbstractApplication; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.entity.StartableApplication; import org.apache.brooklyn.core.entity.factory.ApplicationBuilder; import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; import org.apache.brooklyn.core.sensor.Sensors; import org.apache.brooklyn.test.support.TestResourceUnavailableException; import org.apache.brooklyn.util.core.javalang.UrlClassLoader; import com.google.common.base.Function; public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplication> { @SuppressWarnings("unused") private static final Logger LOG = LoggerFactory.getLogger(RebindCatalogEntityTest.class); /* * Code contained in brooklyn-AppInCatalog.jar is: * * package brooklyn.entity.rebind; * public class AppInCatalog extends AbstractApplication { * public static final ConfigKey<String> MY_CONF = ConfigKeys.newStringConfigKey("myconf"); * public static final AttributeSensor<String> MY_SENSOR = Sensors.newStringSensor("mysensor"); * } */ private static final String JAR_PATH = "/org/apache/brooklyn/core/test/rebind/sample-app-in-catalog/brooklyn-AppInCatalog.jar"; private static final String APP_CLASSNAME = "org.apache.brooklyn.core.test.rebind.sample_app_in_catalog.AppInCatalog"; private URL url; @Override protected boolean useEmptyCatalog() { return true; } @Override protected StartableApplication createApp() { // do nothing here return null; } @BeforeMethod(alwaysRun=true) @Override public void setUp() throws Exception { TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), JAR_PATH); url = getClass().getResource(JAR_PATH); assertNotNull(url, "Could not find on classpath: "+JAR_PATH); super.setUp(); } // TODO Failed in jenkins (once on 20141104, with invocationCount=100): mysensor was null post-rebind. // // Note: to test before/after behaviour (i.e. that we're really fixing what we think we are) then comment out the body of: // AbstractMemento.injectTypeClass(Class) // // NB: this behaviour is generally deprecated in favour of OSGi now. @Test public void testRestoresAppFromCatalogClassloader() throws Exception { @SuppressWarnings("unchecked") Class<? extends AbstractApplication> appClazz = (Class<? extends AbstractApplication>) new UrlClassLoader(url).loadClass(APP_CLASSNAME); origManagementContext.getCatalog().addItem(appClazz); EntitySpec<StartableApplication> appSpec = EntitySpec.create(StartableApplication.class, appClazz) .configure("myconf", "myconfval"); origApp = ApplicationBuilder.newManagedApp(appSpec, origManagementContext); ((EntityInternal)origApp).sensors().set(Sensors.newStringSensor("mysensor"), "mysensorval"); newApp = rebindWithAppClass(); Entities.dumpInfo(newApp); assertNotSame(newApp, origApp); assertEquals(newApp.getId(), origApp.getId()); assertEquals(newApp.getClass().getName(), APP_CLASSNAME); assertEquals(newApp.getEntityType().getName(), APP_CLASSNAME); assertEquals(newApp.getAttribute(Sensors.newStringSensor("mysensor")), "mysensorval"); assertEquals(newApp.getConfig(ConfigKeys.newStringConfigKey("myconf")), "myconfval"); } @Test(invocationCount=100, groups="Integration") public void testRestoresAppFromCatalogClassloaderManyTimes() throws Exception { // Need to fix package name and rebuild brooklyn-AppInCatalog.jar // or better add it as a new test-bundles subproject testRestoresAppFromCatalogClassloader(); } // TODO Not using RebindTestUtils.rebind(mementoDir, getClass().getClassLoader()); // because that won't have right catalog classpath. // How to reuse that code cleanly? protected StartableApplication rebindWithAppClass() throws Exception { RebindTestUtils.waitForPersisted(origApp); LocalManagementContext newManagementContext = RebindTestUtils.newPersistingManagementContextUnstarted(mementoDir, classLoader); UrlClassLoader ucl = new UrlClassLoader(url); @SuppressWarnings("unchecked") final Class<? extends AbstractApplication> appClazz = (Class<? extends AbstractApplication>) ucl.loadClass(APP_CLASSNAME); // ucl.close is only introduced in java 1.7 if (ucl instanceof Closeable) ((Closeable)ucl).close(); newManagementContext.getCatalogInitialization().addPopulationCallback(new Function<CatalogInitialization, Void>() { @Override public Void apply(CatalogInitialization input) { input.getManagementContext().getCatalog().addItem(appClazz); return null; } }); ClassLoader classLoader = newManagementContext.getCatalog().getRootClassLoader(); classLoader.loadClass(appClazz.getName()); List<Application> newApps = newManagementContext.getRebindManager().rebind(classLoader, null, ManagementNodeState.MASTER); newManagementContext.getRebindManager().startPersistence(); return (StartableApplication) newApps.get(0); } }