brooklyn.entity.rebind.RebindTestUtils.java Source code

Java tutorial

Introduction

Here is the source code for brooklyn.entity.rebind.RebindTestUtils.java

Source

/*
 * 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,
                        "classpath://brooklyn-catalog-empty.xml");
            }
            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);
    }

    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();
        }
    }
}