com.agapsys.web.toolkit.utils.SingletonManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of web-app-toolkit-core Show documentation
Show all versions of web-app-toolkit-core Show documentation
Essential tools for back-end applications
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 extends T> tmpClass = entry.getKey();
T instance = entry.getValue();
if (__getFirstConcreteSuperClass(tmpClass) == parentClass) {
classDeletionSet.add(tmpClass);
instanceSet.remove(instance);
}
}
for (Class extends T> 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 extends T>) 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 extends T>) 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy