| /* |
| * 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 brooklyn.entity.rebind; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import java.io.File; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.TimeoutException; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import brooklyn.config.BrooklynProperties; |
| import brooklyn.config.BrooklynServerConfig; |
| import brooklyn.entity.Application; |
| import brooklyn.entity.Entity; |
| import brooklyn.entity.rebind.Dumpers.Pointer; |
| import brooklyn.entity.rebind.dto.MementosGenerators; |
| import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore; |
| import brooklyn.entity.rebind.persister.FileBasedObjectStore; |
| import brooklyn.entity.rebind.persister.PersistMode; |
| import brooklyn.entity.rebind.persister.PersistenceObjectStore; |
| import brooklyn.entity.trait.Identifiable; |
| import brooklyn.location.Location; |
| import brooklyn.management.ManagementContext; |
| import brooklyn.management.ha.HighAvailabilityMode; |
| import brooklyn.management.ha.ManagementNodeState; |
| import brooklyn.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore; |
| import brooklyn.management.internal.LocalManagementContext; |
| import brooklyn.management.internal.ManagementContextInternal; |
| import brooklyn.mementos.BrooklynMemento; |
| import brooklyn.mementos.BrooklynMementoRawData; |
| import brooklyn.test.entity.LocalManagementContextForTests; |
| import brooklyn.util.io.FileUtil; |
| import brooklyn.util.javalang.Serializers; |
| import brooklyn.util.javalang.Serializers.ObjectReplacer; |
| import brooklyn.util.time.Duration; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Maps; |
| |
| public class RebindTestUtils { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(RebindTestUtils.class); |
| |
| private static final Duration TIMEOUT = Duration.seconds(20); |
| |
| public static <T> T serializeAndDeserialize(T memento) throws Exception { |
| ObjectReplacer replacer = new ObjectReplacer() { |
| private final Map<Pointer, Object> replaced = Maps.newLinkedHashMap(); |
| |
| @Override public Object replace(Object toserialize) { |
| if (toserialize instanceof Location || toserialize instanceof Entity) { |
| Pointer pointer = new Pointer(((Identifiable)toserialize).getId()); |
| replaced.put(pointer, toserialize); |
| return pointer; |
| } |
| return toserialize; |
| } |
| @Override public Object resolve(Object todeserialize) { |
| if (todeserialize instanceof Pointer) { |
| return checkNotNull(replaced.get(todeserialize), todeserialize); |
| } |
| return todeserialize; |
| } |
| }; |
| |
| try { |
| return Serializers.reconstitute(memento, replacer); |
| } catch (Exception e) { |
| try { |
| Dumpers.logUnserializableChains(memento, replacer); |
| //Dumpers.deepDumpSerializableness(memento); |
| } catch (Throwable t) { |
| LOG.warn("Error logging unserializable chains for memento "+memento+" (propagating original exception)", t); |
| } |
| throw e; |
| } |
| } |
| |
| public static void deleteMementoDir(String path) { |
| deleteMementoDir(new File(path)); |
| } |
| |
| public static void deleteMementoDir(File f) { |
| FileBasedObjectStore.deleteCompletely(f); |
| } |
| |
| public static void checkMementoSerializable(Application app) throws Exception { |
| BrooklynMemento memento = MementosGenerators.newBrooklynMemento(app.getManagementContext()); |
| checkMementoSerializable(memento); |
| } |
| |
| public static void checkMementoSerializable(BrooklynMemento memento) throws Exception { |
| serializeAndDeserialize(memento); |
| } |
| |
| public static LocalManagementContext newPersistingManagementContext(File mementoDir, ClassLoader classLoader) { |
| return managementContextBuilder(mementoDir, classLoader).buildStarted(); |
| } |
| public static LocalManagementContext newPersistingManagementContext(File mementoDir, ClassLoader classLoader, long persistPeriodMillis) { |
| return managementContextBuilder(mementoDir, classLoader) |
| .persistPeriodMillis(persistPeriodMillis) |
| .buildStarted(); |
| } |
| |
| public static LocalManagementContext newPersistingManagementContextUnstarted(File mementoDir, ClassLoader classLoader) { |
| return managementContextBuilder(mementoDir, classLoader).buildUnstarted(); |
| } |
| |
| public static ManagementContextBuilder managementContextBuilder(File mementoDir, ClassLoader classLoader) { |
| return new ManagementContextBuilder(classLoader, mementoDir); |
| } |
| public static ManagementContextBuilder managementContextBuilder(ClassLoader classLoader, File mementoDir) { |
| return new ManagementContextBuilder(classLoader, mementoDir); |
| } |
| public static ManagementContextBuilder managementContextBuilder(ClassLoader classLoader, PersistenceObjectStore objectStore) { |
| return new ManagementContextBuilder(classLoader, objectStore); |
| } |
| |
| public static class ManagementContextBuilder { |
| final ClassLoader classLoader; |
| BrooklynProperties properties; |
| PersistenceObjectStore objectStore; |
| Duration persistPeriod = Duration.millis(100); |
| boolean forLive; |
| boolean enableOsgi = false; |
| boolean emptyCatalog; |
| |
| ManagementContextBuilder(File mementoDir, ClassLoader classLoader) { |
| this(classLoader, new FileBasedObjectStore(mementoDir)); |
| } |
| ManagementContextBuilder(ClassLoader classLoader, File mementoDir) { |
| this(classLoader, new FileBasedObjectStore(mementoDir)); |
| } |
| ManagementContextBuilder(ClassLoader classLoader, PersistenceObjectStore objStore) { |
| this.classLoader = checkNotNull(classLoader, "classLoader"); |
| this.objectStore = checkNotNull(objStore, "objStore"); |
| } |
| |
| public ManagementContextBuilder persistPeriodMillis(long persistPeriodMillis) { |
| checkArgument(persistPeriodMillis > 0, "persistPeriodMillis must be greater than 0; was "+persistPeriodMillis); |
| return persistPeriod(Duration.millis(persistPeriodMillis)); |
| } |
| public ManagementContextBuilder persistPeriod(Duration persistPeriod) { |
| checkNotNull(persistPeriod); |
| this.persistPeriod = persistPeriod; |
| return this; |
| } |
| |
| public ManagementContextBuilder properties(BrooklynProperties properties) { |
| this.properties = checkNotNull(properties, "properties"); |
| return this; |
| } |
| |
| public ManagementContextBuilder forLive(boolean val) { |
| this.forLive = val; |
| return this; |
| } |
| |
| public ManagementContextBuilder enableOsgi(boolean val) { |
| this.enableOsgi = val; |
| return this; |
| } |
| |
| public ManagementContextBuilder emptyCatalog() { |
| this.emptyCatalog = true; |
| return this; |
| } |
| |
| public ManagementContextBuilder emptyCatalog(boolean val) { |
| this.emptyCatalog = val; |
| return this; |
| } |
| |
| public LocalManagementContext buildUnstarted() { |
| LocalManagementContext unstarted; |
| BrooklynProperties properties = this.properties != null |
| ? this.properties |
| : BrooklynProperties.Factory.newDefault(); |
| if (this.emptyCatalog) { |
| properties.putIfAbsent(BrooklynServerConfig.BROOKLYN_CATALOG_URL, ManagementContextInternal.EMPTY_CATALOG_URL); |
| } |
| if (forLive) { |
| unstarted = new LocalManagementContext(properties); |
| } else { |
| unstarted = LocalManagementContextForTests.builder(true).useProperties(properties).disableOsgi(!enableOsgi).build(); |
| } |
| |
| objectStore.injectManagementContext(unstarted); |
| objectStore.prepareForSharedUse(PersistMode.AUTO, HighAvailabilityMode.DISABLED); |
| BrooklynMementoPersisterToObjectStore newPersister = new BrooklynMementoPersisterToObjectStore( |
| objectStore, |
| unstarted.getBrooklynProperties(), |
| classLoader); |
| ((RebindManagerImpl) unstarted.getRebindManager()).setPeriodicPersistPeriod(persistPeriod); |
| unstarted.getRebindManager().setPersister(newPersister, PersistenceExceptionHandlerImpl.builder().build()); |
| // set the HA persister, in case any children want to use HA |
| unstarted.getHighAvailabilityManager().setPersister(new ManagementPlaneSyncRecordPersisterToObjectStore(unstarted, objectStore, classLoader)); |
| return unstarted; |
| } |
| |
| public LocalManagementContext buildStarted() { |
| LocalManagementContext unstarted = buildUnstarted(); |
| unstarted.getHighAvailabilityManager().disabled(); |
| unstarted.getRebindManager().startPersistence(); |
| return unstarted; |
| } |
| |
| } |
| |
| /** |
| * Convenience for common call; delegates to {@link #rebind(RebindOptions)} |
| */ |
| public static Application rebind(File mementoDir, ClassLoader classLoader) throws Exception { |
| return rebind(RebindOptions.create() |
| .mementoDir(mementoDir) |
| .classLoader(classLoader)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Application rebind(File mementoDir, ClassLoader classLoader, RebindExceptionHandler exceptionHandler) throws Exception { |
| return rebind(RebindOptions.create() |
| .mementoDir(mementoDir) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Application rebind(ManagementContext newManagementContext, ClassLoader classLoader) throws Exception { |
| return rebind(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .classLoader(classLoader)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Application rebind(ManagementContext newManagementContext, ClassLoader classLoader, RebindExceptionHandler exceptionHandler) throws Exception { |
| return rebind(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Application rebind(ManagementContext newManagementContext, File mementoDir, ClassLoader classLoader) throws Exception { |
| return rebind(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .mementoDir(mementoDir) |
| .classLoader(classLoader)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Application rebind(ManagementContext newManagementContext, File mementoDir, ClassLoader classLoader, RebindExceptionHandler exceptionHandler) throws Exception { |
| return rebind(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .mementoDir(mementoDir) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Application rebind(ManagementContext newManagementContext, File mementoDir, |
| ClassLoader classLoader, RebindExceptionHandler exceptionHandler, PersistenceObjectStore objectStore) throws Exception { |
| return rebind(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .mementoDir(mementoDir) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler) |
| .objectStore(objectStore)); |
| } |
| |
| public static Application rebind(RebindOptions options) throws Exception { |
| Collection<Application> newApps = rebindAll(options); |
| if (newApps.isEmpty()) throw new IllegalStateException("Application could not be rebinded; serialization probably failed"); |
| return Iterables.getFirst(newApps, null); |
| } |
| |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebindAll(RebindOptions)} |
| */ |
| @Deprecated |
| public static Collection<Application> rebindAll(File mementoDir, ClassLoader classLoader) throws Exception { |
| return rebindAll(RebindOptions.create() |
| .mementoDir(mementoDir) |
| .classLoader(classLoader)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Collection<Application> rebindAll(File mementoDir, ClassLoader classLoader, RebindExceptionHandler exceptionHandler) throws Exception { |
| return rebindAll(RebindOptions.create() |
| .mementoDir(mementoDir) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Collection<Application> rebindAll(LocalManagementContext newManagementContext, ClassLoader classLoader, RebindExceptionHandler exceptionHandler) throws Exception { |
| return rebindAll(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Collection<Application> rebindAll(ManagementContext newManagementContext, File mementoDir, ClassLoader classLoader) throws Exception { |
| return rebindAll(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .mementoDir(mementoDir) |
| .classLoader(classLoader)); |
| } |
| |
| /** |
| * @deprecated since 0.7.0; use {@link #rebind(RebindOptions)} |
| */ |
| @Deprecated |
| public static Collection<Application> rebindAll(ManagementContext newManagementContext, File mementoDir, ClassLoader classLoader, RebindExceptionHandler exceptionHandler, PersistenceObjectStore objectStore) throws Exception { |
| return rebindAll(RebindOptions.create() |
| .newManagementContext(newManagementContext) |
| .mementoDir(mementoDir) |
| .classLoader(classLoader) |
| .exceptionHandler(exceptionHandler) |
| .objectStore(objectStore)); |
| } |
| |
| public static Collection<Application> rebindAll(RebindOptions options) throws Exception { |
| File mementoDir = options.mementoDir; |
| File mementoDirBackup = options.mementoDirBackup; |
| ClassLoader classLoader = checkNotNull(options.classLoader, "classLoader"); |
| ManagementContextInternal origManagementContext = (ManagementContextInternal) options.origManagementContext; |
| ManagementContextInternal newManagementContext = (ManagementContextInternal) options.newManagementContext; |
| PersistenceObjectStore objectStore = options.objectStore; |
| RebindExceptionHandler exceptionHandler = options.exceptionHandler; |
| boolean hasPersister = newManagementContext != null && newManagementContext.getRebindManager().getPersister() != null; |
| boolean checkSerializable = options.checkSerializable; |
| boolean terminateOrigManagementContext = options.terminateOrigManagementContext; |
| |
| LOG.info("Rebinding app, using mementoDir " + mementoDir + "; object store " + objectStore); |
| |
| if (newManagementContext == null) { |
| // TODO Could use empty properties, to save reading brooklyn.properties file. |
| // Would that affect any tests? |
| newManagementContext = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault()); |
| } |
| if (!hasPersister) { |
| if (objectStore == null) { |
| objectStore = new FileBasedObjectStore(checkNotNull(mementoDir, "mementoDir and objectStore must not both be null")); |
| } |
| objectStore.injectManagementContext(newManagementContext); |
| objectStore.prepareForSharedUse(PersistMode.AUTO, HighAvailabilityMode.DISABLED); |
| |
| BrooklynMementoPersisterToObjectStore newPersister = new BrooklynMementoPersisterToObjectStore( |
| objectStore, |
| newManagementContext.getBrooklynProperties(), |
| classLoader); |
| newManagementContext.getRebindManager().setPersister(newPersister, PersistenceExceptionHandlerImpl.builder().build()); |
| } else { |
| if (objectStore != null) throw new IllegalStateException("Must not supply ManagementContext with persister and an object store"); |
| } |
| |
| if (checkSerializable) { |
| checkNotNull(origManagementContext, "must supply origManagementContext with checkSerializable"); |
| RebindTestUtils.checkCurrentMementoSerializable(origManagementContext); |
| } |
| |
| if (terminateOrigManagementContext) { |
| checkNotNull(origManagementContext, "must supply origManagementContext with terminateOrigManagementContext"); |
| origManagementContext.terminate(); |
| } |
| |
| if (mementoDirBackup != null) { |
| FileUtil.copyDir(mementoDir, mementoDirBackup); |
| FileUtil.setFilePermissionsTo700(mementoDirBackup); |
| } |
| |
| List<Application> newApps = newManagementContext.getRebindManager().rebind(classLoader, exceptionHandler, ManagementNodeState.MASTER); |
| newManagementContext.getRebindManager().startPersistence(); |
| return newApps; |
| } |
| |
| public static void waitForPersisted(Application origApp) throws InterruptedException, TimeoutException { |
| waitForPersisted(origApp.getManagementContext()); |
| } |
| |
| public static void waitForPersisted(ManagementContext managementContext) throws InterruptedException, TimeoutException { |
| managementContext.getRebindManager().waitForPendingComplete(TIMEOUT, true); |
| } |
| |
| public static void checkCurrentMementoSerializable(Application app) throws Exception { |
| checkCurrentMementoSerializable(app.getManagementContext()); |
| } |
| |
| public static void checkCurrentMementoSerializable(ManagementContext mgmt) throws Exception { |
| BrooklynMemento memento = MementosGenerators.newBrooklynMemento(mgmt); |
| serializeAndDeserialize(memento); |
| } |
| |
| /** |
| * Dumps out the persisted mementos that are at the given directory. |
| * |
| * Binds to the persisted state (as a "hot standby") to load the raw data (as strings), and to write out the |
| * entity, location, policy, enricher, feed and catalog-item data. |
| * |
| * @param dir The directory containing the persisted state |
| */ |
| public static void dumpMementoDir(File dir) { |
| LocalManagementContextForTests mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty()); |
| FileBasedObjectStore store = null; |
| BrooklynMementoPersisterToObjectStore persister = null; |
| try { |
| store = new FileBasedObjectStore(dir); |
| store.injectManagementContext(mgmt); |
| store.prepareForSharedUse(PersistMode.AUTO, HighAvailabilityMode.HOT_STANDBY); |
| persister = new BrooklynMementoPersisterToObjectStore(store, BrooklynProperties.Factory.newEmpty(), RebindTestUtils.class.getClassLoader()); |
| BrooklynMementoRawData data = persister.loadMementoRawData(RebindExceptionHandlerImpl.builder().build()); |
| List<BrooklynObjectType> types = ImmutableList.of(BrooklynObjectType.ENTITY, BrooklynObjectType.LOCATION, |
| BrooklynObjectType.POLICY, BrooklynObjectType.ENRICHER, BrooklynObjectType.FEED, |
| BrooklynObjectType.CATALOG_ITEM); |
| for (BrooklynObjectType type : types) { |
| LOG.info(type+" ("+data.getObjectsOfType(type).keySet()+"):"); |
| for (Map.Entry<String, String> entry : data.getObjectsOfType(type).entrySet()) { |
| LOG.info("\t"+type+" "+entry.getKey()+": "+entry.getValue()); |
| } |
| } |
| } finally { |
| if (persister != null) persister.stop(false); |
| if (store != null) store.close(); |
| mgmt.terminate(); |
| } |
| } |
| } |