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

com.agapsys.web.toolkit.utils.SingletonManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Agapsys Tecnologia Ltda-ME.
 *
 * Licensed 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 com.agapsys.web.toolkit.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * Singleton manager.
 *
 * @param  Managed super-type
 */
public class SingletonManager {

    // 
    // =========================================================================
    /**
     * Returns an object instance of a given class using its default constructor.
     *
     * @param  instance type
     * @param clazz instance class.
     * @return an object instance of a given class using its default constructor.
     */
    private static  I getDefaultObjInstance(Class clazz) {
        try {
            I obj = clazz.getConstructor().newInstance();
            return obj;
        } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            throw new RuntimeException(String.format("Error instantiating class: '%s'", clazz), ex);
        }
    }
    // =========================================================================
    // 

    private final Class thisClass;
    private final Map, T> instanceMap = new LinkedHashMap<>();
    private final Set instanceSet = new LinkedHashSet<>();

    private final Set readOnlyInstanceSet = Collections.unmodifiableSet(instanceSet);
    private final Set> readOnlyClassSet = Collections.unmodifiableSet(instanceMap.keySet());

    /**
     * Constructor.
     * @param superClass managed super class
     */
    public SingletonManager(Class superClass) {
        thisClass = superClass;
    }

    /**
     * Clear all managed data.
     */
    public void clear() {
        synchronized(instanceMap) {
            instanceMap.clear();
            instanceSet.clear();
        }
    }

    /**
     * Returns a boolean indicating there are no managed instances.
     *
     * @return a boolean indicating there are no managed instances.
     */
    public boolean isEmpty() {
        synchronized(instanceMap) {
            return instanceMap.isEmpty();
        }
    }

    /**
     * Returns all managed instances.
     *
     * @return all managed instances.
     */
    public Set getInstances() {
        synchronized(instanceMap) {
            return readOnlyInstanceSet;
        }
    }

    /**
     * Returns all managed classes.
     *
     * @return all managed classes.
     */
    public Set> getClasses() {
        synchronized(instanceMap) {
            return readOnlyClassSet;
        }
    }

    private Class __getFirstConcreteSuperClass(Class clazz) {
        Class firstConcreteSuperClass = clazz;
        
        while(true) {
            Class superClass = firstConcreteSuperClass.getSuperclass();

            int modifiers = superClass.getModifiers();
            boolean isConcreteClass = !Modifier.isAbstract(modifiers) && !Modifier.isInterface(modifiers);

            if (isConcreteClass && thisClass.isAssignableFrom(superClass)) {
                firstConcreteSuperClass = (Class) superClass;
            } else {
                break;
            }
        }
        
        return firstConcreteSuperClass;
    }
    
    private void __clearChildren(Class parentClass) {
        Set> classDeletionSet = new LinkedHashSet<>();
        
        for (Map.Entry, T> entry : instanceMap.entrySet()) {
            Class tmpClass = entry.getKey();
            T instance = entry.getValue();
            
            if (__getFirstConcreteSuperClass(tmpClass) == parentClass) {
                classDeletionSet.add(tmpClass);
                instanceSet.remove(instance);
            }
        }
        
        for (Class tmpClass : classDeletionSet) {
            instanceMap.remove(tmpClass);
        }
        
    }
    
    /**
     * Registers an instance.
     *
     * @param instance instance to be managed.
     * @param overrideClassHierarchy defines class hierachy should be overriden.
     */
    public void registerInstance(T instance, boolean overrideClassHierarchy) {
        synchronized (instanceMap) {

            if (instance == null)
                throw new IllegalArgumentException("Instance cannot be null");
            
            Class firstConcreteSuperClass = __getFirstConcreteSuperClass(instance.getClass());
            __clearChildren(firstConcreteSuperClass);

            instanceMap.put((Class) instance.getClass(), instance);
            instanceSet.add(instance);
            
            if (!overrideClassHierarchy)
                return;

            Class tmpClass = instance.getClass();
            while(true) {
                // Register entire class hierachy up first subclass of 'thisClass'...
                Class superClass = tmpClass.getSuperclass();

                int modifiers = superClass.getModifiers();
                boolean isConcreteClass = !Modifier.isAbstract(modifiers) && !Modifier.isInterface(modifiers);

                if (isConcreteClass && thisClass.isAssignableFrom(superClass)) {
                    instanceMap.put((Class) superClass, instance);
                    tmpClass = superClass;
                } else {
                    break;
                }
            }
        }
    }

    /**
     * Registers an instance.
     * 
     * this is a convenience method for registerInstance(instance, true).
     * 
     * @param instance instance to be managed.
     */
    public final void registerInstance(T instance) {
        registerInstance(instance, true);
    }
    
    /**
     * Registers an instance using given class default constructor.
     *
     * @param  instance class.
     * @param instanceClass class used to instantiate an object using its default constructor.
     * @param overrideClassHierarchy defines class hierachy should be overriden.
     * @return created instance.
     */
    public  I registerClass(Class instanceClass, boolean overrideClassHierarchy) {
        synchronized(instanceMap) {
            if (instanceClass == null)
                throw new IllegalArgumentException("Class cannot be null");

            I instance = getDefaultObjInstance(instanceClass);
            registerInstance(instance, overrideClassHierarchy);
            return instance;
        }
    }

    /**
     * Registers an instance using given class default constructor.
     * 
     * This is a convenience method for registerClass(instanceClass, true).
     *
     * @param  instance class.
     * @param instanceClass class used to instantiate an object using its default constructor.
     * @return created instance.
     */    
    public final  I registerClass(Class instanceClass) {
        return registerClass(instanceClass, true);
    }
    
    /**
     * Returns an instance singleton.
     *
     * @param  instance type
     * @param instanceClass instance class
     * @param autoRegistration defines if an instance shall be created and
     * automatically registered if required. Passing false, implies returning
     * null if there is no associated instance.
     * @param overrideClassHierarchy defines class hierachy should be overriden. If autoRegistration is false and a instance is not registered, this argument will be ignored.
     * @return instance singleton.
     */
    public  I getInstance(Class instanceClass, boolean autoRegistration, boolean overrideClassHierarchy) {
        synchronized(instanceMap) {
            if (instanceClass == null)
                throw new IllegalArgumentException("Instance class cannot be null");

            I instance = (I) instanceMap.get(instanceClass);
            if (instance == null && autoRegistration) {
                instance = registerClass(instanceClass, overrideClassHierarchy);
            }

            return instance;
        }
    }
    
    /**
     * Returns an instance singleton.
     * 
     * This is a convenience method for getInstance(instanceClass, autoRegistration, true).
     *
     * @param  instance type
     * @param instanceClass instance class
     * @param autoRegistration defines if an instance shall be created and
     * automatically registered if required. Passing false, implies returning
     * null if there is no associated instance.
     * @return instance singleton.
     */
    public final  I getInstance(Class instanceClass, boolean autoRegistration) {
        return getInstance(instanceClass, autoRegistration, true);
    }

    /**
     * Returns an instance singleton.
     * 
     * This is a convenience method for getInstance(instanceClass, false).
     * @param  instance type
     * @param instanceClass instance class
     * @return instance singleton. If there is no associated instance, returns null.
     */
    public final  I getInstance(Class instanceClass) {
        return getInstance(instanceClass, false);
    }

}