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

org.ow2.cmi.lb.util.PolicyFactory Maven / Gradle / Ivy

/**
 * CMI : Cluster Method Invocation
 * Copyright (C) 2007,2008 Bull S.A.S.
 * Contact: [email protected]
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 * --------------------------------------------------------------------------
 * $Id:LBPolicyFactory.java 1124 2007-07-27 16:38:35Z loris $
 * --------------------------------------------------------------------------
 */

package org.ow2.cmi.lb.util;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import net.jcip.annotations.ThreadSafe;

import org.ow2.cmi.controller.common.ClusterViewManager;
import org.ow2.cmi.controller.common.ClusterViewManagerException;
import org.ow2.cmi.lb.LoadBalanceable;
import org.ow2.cmi.lb.PropertyConfigurationException;
import org.ow2.cmi.lb.data.PropertyData;
import org.ow2.cmi.lb.policy.IPolicy;
import org.ow2.cmi.lb.strategy.IStrategy;
import org.ow2.cmi.reference.ObjectNotFoundException;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * Define a factory to construct policies.
 * @param  the type parameter of the constructed policies
 * @author The new CMI team
 */
@ThreadSafe
public final class PolicyFactory {

    /**
     * Logger.
     */
    private static final Log LOGGER = LogFactory.getLog(PolicyFactory.class);

    /**
     * A manager to retrieve the cluster view.
     */
    private final ClusterViewManager clusterViewManager;

    /**
     * Map each classname of policy with its properties.
     */
    private static ConcurrentHashMap> policyClassName2PropertyData =
        new ConcurrentHashMap>();

    /**
     * Constructs a new factory for policies with a given manager of cluster view.
     * @param clusterViewManager a manager to retrieve the cluster view
     */
    public PolicyFactory(final ClusterViewManager clusterViewManager) {
        this.clusterViewManager = clusterViewManager;
    }


    /**
     * Returns a policy to access to the object with the given name.
     * @param objectName a name of object
     * @return a policy to access to the object with the given name
     * @throws PolicyFactoryException if the policy cannot be constructed
     * @throws ObjectNotFoundException if none object has the given name
     */
    @SuppressWarnings("unchecked")
    public IPolicy  getPolicy(final String objectName)
    throws PolicyFactoryException, ObjectNotFoundException {
        Class> policyClass;
        try {
            policyClass = (Class>) clusterViewManager.getPolicyClass(objectName);
        } catch (ClusterViewManagerException e) {
            LOGGER.error("Error while finding the policy class", e);
            throw new PolicyFactoryException("Error while finding the policy class", e);
        }
        Map  properties = clusterViewManager.getPropertiesForPolicy(objectName);
        Class> strategyClass;
        try {
            strategyClass = (Class>) clusterViewManager.getStrategyClass(objectName);
        } catch (ClusterViewManagerException e) {
            LOGGER.error("Error while finding the strategy class", e);
            throw new PolicyFactoryException("Error while finding the strategy class", e);
        }
        return getPolicy(policyClass, strategyClass, properties);
    }

    /**
     * Returns a policy for the given classes of policy, strategy and properties.
     * @param policyClass
     * @param strategyClass
     * @param properties
     * @return a policy to access to the object with the given name
     * @throws PolicyFactoryException if the policy cannot be constructed
     */
    @SuppressWarnings("unchecked")
    public IPolicy  getPolicy(
            final Class policyClass, final Class strategyClass,
            final Map  properties)
    throws PolicyFactoryException {
        IPolicy policy = createPolicy(policyClass);
        try {
            findProperties(policy.getClass());
        } catch (PropertyConfigurationException e) {
            LOGGER.error("Error while finding properties", e);
            throw new PolicyFactoryException("Error while finding properties", e);
        }
        if(properties != null) {
            try {
                setProperties(policy, properties);
            } catch (PropertyConfigurationException e) {
                LOGGER.error("Error while configuring the properties", e);
                throw new PolicyFactoryException("Error while configuring the properties", e);
            }
        }
        IStrategy strategy = createStrategy(strategyClass);
        policy.setStrategy(strategy);
        return policy;
    }

    /**
     * Find properties in a class defining policy.
     * @param policyClass a class defining policy
     * @throws PropertyConfigurationException
     */
    @SuppressWarnings("unchecked")
    private static void findProperties(final Class policyClass)
    throws PropertyConfigurationException {
        String policyClassname = policyClass.getName();

        if(policyClassName2PropertyData.containsKey(policyClassname)) {
            return;
        }

        ConcurrentHashMap properties =
            new ConcurrentHashMap();

        for(Method method : policyClass.getDeclaredMethods()) {
            String methodName = method.getName();

            if(methodName.startsWith("get") && !methodName.equals("getStrategy")) {
                PropertyData propertyData =
                    new PropertyData(method, (Class>) policyClass);
                properties.putIfAbsent(
                        propertyData.getPropertyName(), propertyData);
            }
        }
        policyClassName2PropertyData.putIfAbsent(policyClassname, properties);
    }

    /**
     * Returns a pure policy for a given class.
     * @param policyClass a class defining a policy
     * @return a pure policy to access to the object with the given name
     * @throws PolicyFactoryException if the policy cannot be constructed
     */
    @SuppressWarnings("unchecked")
    public IPolicy createPolicy(final Class policyClass) throws PolicyFactoryException {

        IPolicy policy = null;

        // Lookup if it exists a constructor with a parameter of type ClusterviewManager
        Constructor[] constructors = policyClass.getDeclaredConstructors();
        for(Constructor constructor : constructors) {
            Class[] paramTypes = constructor.getParameterTypes();
            if(paramTypes.length == 1 && paramTypes[0].equals(ClusterViewManager.class)) {
                try {
                    policy = (IPolicy) constructor.newInstance(clusterViewManager);
                } catch (Exception e) {
                    LOGGER.error("Cannot construct the policy with the instance of ClusterViewManager", e);
                    throw new PolicyFactoryException(
                            "Cannot construct the policy with the instance of ClusterViewManager", e);
                }
                break;
            }
        }
        // Use the default constructor
        if(policy == null) {
            try {
                policy = policyClass.newInstance();
            } catch (Exception e) {
                LOGGER.error("Cannot construct the LB policy", e);
                throw new PolicyFactoryException("Cannot construct the LB policy", e);
            }
        }
        // Find a setter for cluster view manager
        try {
            Method setter = policyClass.getMethod("setClusterViewManager", ClusterViewManager.class);
            setter.invoke(policy, clusterViewManager);
        } catch (Exception e) {
            LOGGER.debug("Cannot set the manager of cluster view", e);
        }
        return policy;
    }

    /**
     * Sets properties for a given policy.
     * @param policy a policy
     * @param properties properties of the policy
     * @throws PropertyConfigurationException if a property cannot be set
     */
    private void setProperties(final IPolicy policy, final Map properties)
    throws PropertyConfigurationException {
        for(Entry propertyEntry : properties.entrySet()) {
            setProperty(policy, propertyEntry.getKey(), propertyEntry.getValue());
        }
    }

    /**
     * Set a property for a given policy.
     * @param policy a policy
     * @param propertyName a name of property
     * @param propertyValue a value of property
     * @throws PropertyConfigurationException
     */
    @SuppressWarnings("unchecked")
    private void setProperty(
            final IPolicy policy, final String propertyName, final Object propertyValue)
    throws PropertyConfigurationException {
        Class policyClass = policy.getClass();
        PropertyData propertyData =
            policyClassName2PropertyData.get(policyClass.getName()).get(propertyName);
        try {
            propertyData.getSetter().invoke(policy, propertyValue);
        } catch (Exception e) {
            LOGGER.error("Cannot set the property for name {0} with {1}",
                    propertyName, propertyValue, e);
            throw new PropertyConfigurationException(
                    "Cannot set the property for name "
                    + propertyName + " with "
                    + propertyValue, e);
        }
    }

    /**
     * Returns a strategy for a given class.
     * @param strategyClass a class defining a strategy
     * @return a strategy to access to the object with the given name
     * @throws PolicyFactoryException if the strategy cannot be constructed
     */
    @SuppressWarnings("unchecked")
    private IStrategy createStrategy(final Class strategyClass)
    throws PolicyFactoryException {

        IStrategy strategy = null;

        // Lookup if it exists a constructor with a parameter of type ClusterviewManager
        Constructor[] constructors = strategyClass.getDeclaredConstructors();
        for(Constructor constructor : constructors) {
            Class[] paramTypes = constructor.getParameterTypes();
            if(paramTypes.length == 1 && paramTypes[0].equals(ClusterViewManager.class)) {
                try {
                    strategy = (IStrategy) constructor.newInstance(clusterViewManager);
                } catch (Exception e) {
                    LOGGER.error("Cannot construct the strategy with the instance of ClusterViewManager", e);
                    throw new PolicyFactoryException(
                            "Cannot construct the strategy with the instance of ClusterViewManager", e);
                }
                break;
            }
        }

        // Use the default constructor
        if(strategy == null) {
            try {
                strategy = strategyClass.newInstance();
            } catch (Exception e) {
                LOGGER.error("Cannot construct the strategy with the default constructor", e);
                throw new PolicyFactoryException("Cannot construct the strategy with the default constructor", e);
            }
        }
        return strategy;
    }

    //-------------------- Access to properties ---------------------------------------------------
    // TODO Should be externalized in a separate class

    @SuppressWarnings("unchecked")
    public static Map getProperties(final IPolicy policy)
    throws PropertyConfigurationException {
        Class policyClass = policy.getClass();
        // Check if properties have initialized
        findProperties(policyClass);
        Map propertyData =
            policyClassName2PropertyData.get(policyClass.getName());
        Map properties = new HashMap();
        Object propertyValue;
        for(String propertyName : propertyData.keySet()) {
            propertyValue = getProperty(policy, propertyName);
            properties.put(propertyName, propertyValue);
        }
        return properties;
    }

    @SuppressWarnings("unchecked")
    public static Object getProperty(final IPolicy policy, final String propertyName) throws PropertyConfigurationException {
        Class policyClass = policy.getClass();
        // Check if properties have initialized
        findProperties(policyClass);
        Map propertyData =
            policyClassName2PropertyData.get(policyClass.getName());
        PropertyData property = propertyData.get(propertyName);
        try {
            return property.getGetter().invoke(policy);
        } catch (Exception e) {
            LOGGER.error("Cannot invoke the getter for property {0}", propertyName, e);
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static Type getPropertyRawType(
            final Class policyClass, final String propertyName)
    throws PropertyConfigurationException {
        Type propertyType = getPropertyType(policyClass, propertyName);
        if(propertyType instanceof ParameterizedType) {
            return ((ParameterizedType) propertyType).getRawType();
        } else {
            return propertyType;
        }
    }

    @SuppressWarnings("unchecked")
    public static Type getPropertyType(
            final Class policyClass, final String propertyName) throws PropertyConfigurationException {
        // Check if properties have initialized
        findProperties(policyClass);
        return policyClassName2PropertyData.get(policyClass.getName()).get(propertyName).getPropertyType();
    }

    @SuppressWarnings("unchecked")
    public static Map getPropertyData(
            final Class policyClass)
    throws PropertyConfigurationException {
        // Check if properties have initialized
        findProperties(policyClass);
        return policyClassName2PropertyData.get(policyClass.getName());
    }

    /**
     * Converts the given value from String to the given type.
     * @param propertyName the property name
     * @param svalue a value
     * @return an object that has the same that the given field
     * @throws PropertyConfigurationException if the conversion is not possible
     */
    @SuppressWarnings("unchecked")
    public static Object convertString(
            final Class policyClass,
            final String propertyName,
            final String svalue) throws PropertyConfigurationException {
        Type propertyType = getPropertyType(policyClass, propertyName);
        return convertString(propertyType, svalue);
    }

    public static Object convertString(
            final Type propertyType,
            final String svalue) throws PropertyConfigurationException {
        Type type;
        if(propertyType instanceof ParameterizedType) {
            type = ((ParameterizedType) propertyType).getRawType();
        } else {
            type = propertyType;
        }
        Class klass = (Class) type;
        Object propertyValue;
        //  Attempt to cast String forward a primitive type
        if(klass.equals(String.class)) {
            propertyValue = svalue;
        } else if(klass.equals(boolean.class) || klass.equals(Boolean.class)) {
            propertyValue = Boolean.valueOf(svalue);
        } else if(klass.equals(byte.class) || klass.equals(Byte.class)) {
            propertyValue = Byte.valueOf(svalue);
        } else if(klass.equals(int.class) || klass.equals(Integer.class)) {
            propertyValue = Integer.valueOf(svalue);
        } else if(klass.equals(long.class) || klass.equals(Long.class)) {
            propertyValue = Long.valueOf(svalue);
        } else if(klass.equals(float.class) || klass.equals(Float.class)) {
            propertyValue = Float.valueOf(svalue);
        } else if(klass.equals(double.class) || klass.equals(Double.class)) {
            propertyValue = Double.valueOf(svalue);
        } else if(klass.equals(URL.class)){ // Try with some complex type
            try {
                propertyValue = new URL(svalue);
            } catch (MalformedURLException e) {
                throw new PropertyConfigurationException("Cannot convert the string " + svalue + " to an URL", e);
            }
        } else if(klass.equals(File.class)) {
            propertyValue = new File(svalue);
        } else if(klass.equals(Class.class)) {
            try {
                propertyValue = Class.forName(svalue);
            } catch (ClassNotFoundException e) {
                throw new PropertyConfigurationException("Cannot convert the string " + svalue + " to a class", e);
            }
        } else {
            LOGGER.error("Type of field not supported: {0}", klass.getName());
            throw new PropertyConfigurationException(
                    "Type of field not supported: " + klass.getName());
        }
        return propertyValue;
    }

    @SuppressWarnings("unchecked")
    public static List convertStrings(
            final Class policyClass,
            final String propertyName,
            final List slist)throws PropertyConfigurationException {
        Type parameterizedType = getPropertyType(policyClass, propertyName);
        if(!(parameterizedType instanceof ParameterizedType)) {
            LOGGER.error("{0} is not a parameterized type: cannot convert the elements of this collection", parameterizedType);
            throw new PropertyConfigurationException(
                    parameterizedType + "is not a parameterized type: cannot convert the elements of this collection");
        }
        Type propertyType = ((ParameterizedType) parameterizedType).getActualTypeArguments()[0];
        List values = new ArrayList();
        for(String svalue : slist) {
            values.add(convertString(propertyType, svalue));
        }
        return values;
    }

}