package org.mqnaas.clientprovider.impl; /* * #%L * MQNaaS :: Client Provider * %% * Copyright (C) 2007 - 2015 Fundació Privada i2CAT, Internet i Innovació a Catalunya * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * <http://www.gnu.org/licenses/lgpl-3.0.html>. * #L% */ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.mqnaas.bundletree.IBundleGuard; import org.mqnaas.bundletree.IClassListener; import org.mqnaas.bundletree.utils.ClassFilterFactory; import org.mqnaas.core.api.ICapability; import org.mqnaas.core.api.ICoreModelCapability; import org.mqnaas.core.api.IRootResource; import org.mqnaas.core.api.Specification; import org.mqnaas.core.api.annotations.DependingOn; import org.mqnaas.core.api.exceptions.ApplicationActivationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Offers common methods and attributes for implementations of provider factories. * * @author Georg Mansky-Kummert (i2CAT) * @author Julio Carlos Barrera * * @param <CP> * Provider of the implementation */ public abstract class AbstractProviderFactory<CP> implements ICapability { private final Logger log = LoggerFactory.getLogger(getClass()); @DependingOn(core = true) protected ICoreModelCapability coreModelCapability; @DependingOn(core = true) protected IBundleGuard bundleGuard; public static boolean isSupporting(IRootResource resource) { return resource.getDescriptor().getSpecification().getType() == Specification.Type.CORE; } protected Map<Class<CP>, CP> internalClientProviders = new ConcurrentHashMap<Class<CP>, CP>(); // internal {@link IClassListener} instance protected InternalClassListener internalClassListener; protected abstract Class<?> getInternalProviderClass(); @Override public void activate() throws ApplicationActivationException { // register class listener log.info("Registering as ClassListener."); internalClassListener = new InternalClassListener(getInternalProviderClass()); bundleGuard.registerClassListener(ClassFilterFactory.createBasicClassFilter(getInternalProviderClass()), internalClassListener); } @Override public void deactivate() { // unregister class listeners log.info("Unregistering as ClassListener."); bundleGuard.unregisterClassListener(internalClassListener); } private void internalClientProviderAdded(Class<CP> clazz) { try { internalClientProviders.put(clazz, clazz.newInstance()); } catch (InstantiationException e) { // this are guaranteed to be an instantiable class log.error("Error instantiating IClientProvider of class: " + clazz, e); } catch (IllegalAccessException e) { // at this moment, access to class should be granted log.error("Error instantiating IClientProvider of class: " + clazz, e); } } private void internalClientProviderRemoved(Class<? extends CP> clazz) { // remove them internalClientProviders.remove(clazz); } protected class InternalClassListener implements IClassListener { private Class<?> internalProviderClass; public InternalClassListener(Class<?> internalProviderClass) { this.internalProviderClass = internalProviderClass; } @Override // safe checking castings @SuppressWarnings("unchecked") public void classEntered(Class<?> clazz) { log.debug("Received classEntered event for class: " + clazz.getCanonicalName()); if (internalProviderClass.isAssignableFrom(clazz)) { internalClientProviderAdded((Class<CP>) clazz); } else { log.error("Unknown ClassListener classEntered event received from class " + clazz.getCanonicalName()); } } @Override // safe checking castings @SuppressWarnings("unchecked") public void classLeft(Class<?> clazz) { log.debug("Received classLeft event for class: " + clazz.getCanonicalName()); if (internalProviderClass.isAssignableFrom(clazz)) { internalClientProviderRemoved((Class<? extends CP>) clazz); } else { log.error("Unknown ClassListener classLeft event received from class " + clazz.getCanonicalName()); } } } /** * Returns true if given two classes have common types on first generic interface (or successive first super-interfaces) present in given valid * types. * * @param validTypes * valid types to be checked * @param class1 * first target {@link Class} * @param class2 * second target {@link Class} * @return true if given two classes have common types on first generic interface present in given valid types; false otherwise */ protected static boolean doTypeArgumentsMatch(Set<Type> validTypes, Class<?> class1, Class<?> class2) { int numTypes; if ((numTypes = getNumberOfTypes(class1)) != getNumberOfTypes(class2)) { return false; } for (int i = 0; i < numTypes; i++) { Type type1 = getTypeArgument(validTypes, i, class1); Type type2 = getTypeArgument(validTypes, i, class2); if (!getTypeArgument(validTypes, i, class1).equals(getTypeArgument(validTypes, i, class2)) && !compareWithBoundedType(type1, type2)) { return false; } } return true; } /** * Retrieves index {@link Type} of the first generic interface implemented in given {@link Class} if it is present in given valid types. * * @param validTypes * valid Types to be checked * @param index * index of Types to be checked * @param clazz * target Class * @return Type found or null if not found */ private static Type getTypeArgument(Set<Type> validTypes, int index, Class<?> clazz) { // Look for the specific generic interfaces... for (Type type : clazz.getGenericInterfaces()) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; if (validTypes.contains(parameterizedType.getRawType())) { return parameterizedType.getActualTypeArguments()[index]; } } else { // only analyzing first interface in each iteration Class<?> superClass = clazz.getInterfaces()[0]; if (superClass != null) { return getTypeArgument(validTypes, index, superClass); } } } return null; } /** * Retrieves the number of generic types of first interface of given {@link Class} or its implemented super-interfaces. * * @param clazz * target Class * @return number of types */ private static int getNumberOfTypes(Class<?> clazz) { for (Type interfaze : clazz.getGenericInterfaces()) { if (interfaze instanceof ParameterizedType) { return ((ParameterizedType) interfaze).getActualTypeArguments().length; } else { // only analyzing first interface in each iteration Class<?> superClass = clazz.getInterfaces()[0]; if (superClass != null) { return getNumberOfTypes(superClass); } } } return 0; } private static boolean compareWithBoundedType(Type type1, Type type2) { if (type1 instanceof TypeVariable<?> && type2 instanceof TypeVariable<?> && ((TypeVariable<?>) type2).equals(type1)) { if (compareBoundedTypes((TypeVariable<?>) type1, (TypeVariable<?>) type2)) { return true; } } else if (type1 instanceof TypeVariable<?>) { if (compareTypeWithBoundedType(type2, (TypeVariable<?>) type1)) { return true; } } else if (type2 instanceof TypeVariable<?>) { if (compareTypeWithBoundedType(type1, (TypeVariable<?>) type2)) { return true; } } return false; } private static boolean compareTypeWithBoundedType(Type type, TypeVariable<?> boundedType) { for (Type bound : boundedType.getBounds()) { if (bound.equals(type)) { return true; } } return false; } private static boolean compareBoundedTypes(TypeVariable<?> type1, TypeVariable<?> type2) { for (Type bound1 : type1.getBounds()) { for (Type bound2 : type2.getBounds()) { if (bound1.equals(bound2)) { return true; } } } return false; } }