All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.change.ChangeFactory Maven / Gradle / Ivy

There is a newer version: 4.28.0
Show newest version
package liquibase.change;

import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.servicelocator.ServiceLocator;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Factory class for constructing the correct liquibase.change.Change implementation based on a command name.
 * For XML-based changelogs, the tag name is the command name.
 * Change implementations are looked up via the {@link ServiceLocator}.
 *
 * @see liquibase.change.Change
 */
public class ChangeFactory {

    private static ChangeFactory instance;

    private Map>> registry = new ConcurrentHashMap>>();
    private Map, ChangeMetaData> metaDataByClass = new ConcurrentHashMap, ChangeMetaData>();

    private ChangeFactory() {
    }

    private void init() {
        Class[] classes;
        classes = ServiceLocator.getInstance().findClasses(Change.class);

        for (Class clazz : classes) {
            //noinspection unchecked
            register(clazz);
        }
    }

    /**
     * Return the singleton ChangeFactory instance.
     */
    public static synchronized ChangeFactory getInstance() {
        if (instance == null) {
            instance = new ChangeFactory();
            instance.init();
        }
        return instance;
    }

    /**
     * Reset the ChangeFactory so it reloads the registry on the next call to @{link #getInstance()}. Mainly used in testing
     */
    public static void reset() {
        instance = null;
    }


    /**
     * Register a new Change class.
     * Normally called automatically by ChangeFactory on all Change implementations found by the ServiceLocator, but it can be called manually if needed.
     */
    public void register(Class changeClass) {
        try {
            Change instance = changeClass.newInstance();
            ChangeMetaData metaData = getChangeMetaData(instance);
            String name = metaData.getName();
            if (registry.get(name) == null) {
                registry.put(name, new TreeSet>(new Comparator>() {
                    @Override
                    public int compare(Class o1, Class o2) {
                        try {
                            return -1 * new Integer(getChangeMetaData(o1.newInstance()).getPriority()).compareTo(getChangeMetaData(o2.newInstance()).getPriority());
                        } catch (Exception e) {
                            throw new UnexpectedLiquibaseException(e);
                        }
                    }
                }));
            }
            registry.get(name).add(changeClass);
        } catch (Exception e) {
            throw new UnexpectedLiquibaseException(e);
        }
    }

    public ChangeMetaData getChangeMetaData(Change change) {
        if (!metaDataByClass.containsKey(change.getClass())) {
            metaDataByClass.put(change.getClass(), change.createChangeMetaData());
        }
        return metaDataByClass.get(change.getClass());
    }

    /**
     * Unregister all instances of a given Change name. Normally used for testing, but can be called manually if needed.
     */
    public void unregister(String name) {
        registry.remove(name);
    }

    /**
     * Return the registry of all Changes found. Key is the change name and the values are a sorted set of implementations, ordered by Priority descending.
     * Normally used only for information/debugging purposes. The returned map is read only.
     */
    public Map>> getRegistry() {
        return Collections.unmodifiableMap(registry);
    }


    /**
     * Returns all defined changes in the registry. Returned set is not modifiable.
     */
    public Set getDefinedChanges() {
        return Collections.unmodifiableSet(registry.keySet());
    }

    /**
     * Clear the registry of all Changes found. Normally used for testing.
     */
    public void clear() {
        registry.clear();
        metaDataByClass.clear();
    }

    /**
     * Create a new Change implementation for the given change name. The class of the constructed object will be the Change implementation with the highest priority.
     * Each call to create will return a new instance of the Change.
     */
    public Change create(String name) {
        SortedSet> classes = registry.get(name);

        if (classes == null) {
            return null;
        }

        try {
            return classes.iterator().next().newInstance();
        } catch (Exception e) {
            throw new UnexpectedLiquibaseException(e);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy