org.jboss.dashboard.factory.Component.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.dashboard.factory.Component.java

Source

/**
 * Copyright (C) 2012 JBoss Inc
 *
 * 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 org.jboss.dashboard.factory;

import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.lang.StringUtils;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * This class holds all the properties regarding a Factory bean definition.
 * Such definition is usually implemented by means of a .properties file inside the etc/factory directory..
 */
public class Component {
    private static transient org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
            .getLog(Component.class.getName());
    public static final String ALIAS_PROPERTY = "alias";
    public static final String CLASS_PROPERTY = "class";
    public static final String SCOPE_PROPERTY = "scope";
    public static final String DESC_PROPERTY = "description";
    public static final String ENABLED_PROPERTY = "enabled";
    public static final String SPECIAL_PROPERTY_PREFFIX = "$";

    public static final String SCOPE_GLOBAL = "global";
    public static final String SCOPE_SESSION = "session";
    public static final String SCOPE_PANEL_SESSION = "panelSession";
    public static final String SCOPE_REQUEST = "request";
    public static final String SCOPE_VOLATILE = "volatile";

    public final List VALID_SCOPES;

    public static final int STATUS_INCOMPLETE = 0;
    public static final int STATUS_VALID = 1;
    public static final int STATUS_INVALID = -1;

    private int status = STATUS_INCOMPLETE;
    private String name;
    private ComponentsTree tree;
    private String description;
    private String clazz;
    private String scope;
    private String alias;
    private boolean enabled = true;

    private Class componentClass;
    private Map componentConfiguredProperties = new Hashtable();
    private List propertiesFilesAdded = new ArrayList();
    private long creationOrderNumber;

    public long getCreationOrderNumber() {
        return creationOrderNumber;
    }

    public void setCreationOrderNumber(long creationOrderNumber) {
        this.creationOrderNumber = creationOrderNumber;
    }

    public Component(String name, ComponentsTree tree) {
        this.name = name;
        this.tree = tree;
        VALID_SCOPES = Collections.unmodifiableList(getInitScopes());
    }

    public ComponentsTree getTree() {
        return tree;
    }

    protected List getInitScopes() {
        List l = new ArrayList();
        l.add(SCOPE_GLOBAL);
        l.add(SCOPE_SESSION);
        l.add(SCOPE_PANEL_SESSION);
        l.add(SCOPE_REQUEST);
        l.add(SCOPE_VOLATILE);
        return l;
    }

    public Map getComponentConfiguredProperties() {
        return componentConfiguredProperties;
    }

    void addProperties(Properties properties, String fileName) {
        String declaredClass = properties.getProperty(SPECIAL_PROPERTY_PREFFIX + CLASS_PROPERTY);
        String declaredScope = properties.getProperty(SPECIAL_PROPERTY_PREFFIX + SCOPE_PROPERTY);
        String declaredDescr = properties.getProperty(SPECIAL_PROPERTY_PREFFIX + DESC_PROPERTY);
        String declaredEnabled = properties.getProperty(SPECIAL_PROPERTY_PREFFIX + ENABLED_PROPERTY);
        String declaredAlias = properties.getProperty(SPECIAL_PROPERTY_PREFFIX + ALIAS_PROPERTY);
        if (declaredClass != null && clazz != null) {
            try {

                Class currentClass = Class.forName(clazz);
                Class newClass = Class.forName(declaredClass);
                if (!currentClass.isAssignableFrom(newClass)) {
                    log.warn("Assigning class " + declaredClass + " to component " + name + " may cause errors. "
                            + "Default system class is " + clazz + ", new class is not descendant.");
                }
            } catch (ClassNotFoundException e) {
                log.error("Error:", e);
            }
        }
        clazz = declaredClass == null ? clazz : declaredClass;
        alias = declaredAlias == null ? alias : declaredAlias;
        scope = declaredScope == null ? scope : declaredScope;
        description = declaredDescr == null ? description : declaredDescr;
        enabled = declaredEnabled == null ? enabled : Boolean.valueOf(declaredEnabled).booleanValue();
        for (Enumeration en = properties.propertyNames(); en.hasMoreElements();) {
            String propertyName = (String) en.nextElement();
            if (propertyName.startsWith(SPECIAL_PROPERTY_PREFFIX)) {
                continue;
            }
            if (!isValidPropertyName(propertyName)) {
                log.error("Ignoring invalid property name " + propertyName + " in component " + name);
                continue;
            }
            String propertyValue = properties.getProperty(propertyName);
            storeProperty(propertyName, propertyValue);
        }
        propertiesFilesAdded.add(fileName);
    }

    /**
     * Determine if the property name is a valid property identifier
     *
     * @param propertyName Property name to evaluate
     * @return true  if the property name is a valid property identifier
     */
    protected boolean isValidPropertyName(String propertyName) {
        if (propertyName == null || propertyName.length() == 0)
            return false;
        if (propertyName.endsWith("-") || propertyName.endsWith("+"))
            propertyName = propertyName.substring(0, propertyName.length() - 1);
        if (propertyName.length() == 0)
            return false;
        for (int i = 0; i < propertyName.length(); i++) {
            char c = propertyName.charAt(i);
            if ('.' == c)
                break; //After this, it is a JXPath
            if (!Character.isJavaIdentifierPart(c))
                return false;
        }
        return true;
    }

    /**
     * Store in componentProperties the property given by name and value.
     *
     * @param propertyName
     * @param propertyValue
     */
    protected void storeProperty(String propertyName, String propertyValue) {
        List currentValue = null;
        PropertyChangeProcessingInstruction instruction = null;
        if (propertyName.endsWith("+")) {
            propertyName = propertyName.substring(0, propertyName.length() - 1).trim();
            instruction = new PropertyAddProcessingInstruction(this, propertyName, propertyValue);
        } else if (propertyName.endsWith("-")) {
            propertyName = propertyName.substring(0, propertyName.length() - 1).trim();
            instruction = new PropertySubstractProcessingInstruction(this, propertyName, propertyValue);
        } else {
            instruction = new PropertySetProcessingInstruction(this, propertyName, propertyValue);
        }

        currentValue = (List) componentConfiguredProperties.get(propertyName);
        if (currentValue == null)
            currentValue = new ArrayList();
        currentValue.add(instruction);
        componentConfiguredProperties.put(propertyName, currentValue);
    }

    /**
     * Writes a property value to the component
     *
     * @param propertyName
     * @param propertyValues
     */
    public void setProperty(String propertyName, String[] propertyValues) throws Exception {
        StringBuffer propertyValue = new StringBuffer();
        for (int i = 0; i < propertyValues.length; i++) {
            String value = propertyValues[i];
            if (i != 0)
                propertyValue.append(",");
            if (propertyValues.length > 1)
                propertyValue.append(StringUtils.replace(value, ",", ",,"));
            else
                propertyValue.append(value);
        }
        ArrayList list = new ArrayList();
        list.add(new PropertySetProcessingInstruction(this, propertyName, propertyValue.toString()));
        setObjectProperty(getObject(), propertyName, list, null);
    }

    public void setProperty(String propertyName, File propertyValue) throws Exception {
        Object obj = getObject();
        //Find a Field
        Field field = getField(obj, propertyName);
        if (field != null) {
            Class fieldClass = field.getType();
            if (File.class.equals(fieldClass)) {
                if (log.isDebugEnabled())
                    log.debug("Invoking field " + propertyName + " with file.");
                field.set(obj, propertyValue);
            } else if (byte[].class.equals(fieldClass)) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(propertyValue));
                int byteRead = -1;
                while ((byteRead = bis.read()) != -1) {
                    bos.write(byteRead);
                }
                bis.close();
                bos.close();

                if (log.isDebugEnabled())
                    log.debug("Invoking field " + propertyName + " with byte[].");
                field.set(obj, bos.toByteArray());
            } else {
                log.error("Cannot write a file to property " + propertyName + " in bean " + name);
            }
            return;
        }
        // Find a getter and setter.
        Method getter = getGetter(obj, propertyName);
        if (getter == null) {
            log.error("Cannot find a getter for property " + propertyName + " in class " + clazz
                    + ". Ignoring property.");
            return;
        }
        Method setter = getSetter(obj, getter, propertyName);
        if (setter == null) {
            log.error("Cannot find a setter for property " + propertyName + " in class " + clazz
                    + ". Ignoring property.");
            return;
        }
        Class returnType = getter.getReturnType();
        if (File.class.equals(returnType)) {
            if (log.isDebugEnabled())
                log.debug("Invoking " + setter + " with file.");
            setter.invoke(obj, new Object[] { propertyValue });
        } else if (byte[].class.equals(returnType)) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(propertyValue));
            int byteRead = -1;
            while ((byteRead = bis.read()) != -1) {
                bos.write(byteRead);
            }
            bis.close();
            bos.close();

            if (log.isDebugEnabled())
                log.debug("Invoking " + setter + " with byte[].");
            setter.invoke(obj, new Object[] { bos.toByteArray() });
        } else {
            log.error("Cannot write a file to property " + propertyName + " in bean " + name);
        }
    }

    public String getName() {
        return name;
    }

    public String getClazz() {
        return clazz;
    }

    public String getScope() {
        return scope;
    }

    public String getAlias() {
        return alias;
    }

    public String getDescription() {
        return description;
    }

    public List getPropertiesFilesAdded() {
        return Collections.unmodifiableList(propertiesFilesAdded);
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append(scope).append(" Component named ").append(name).append(" with class ").append(clazz)
                .append(" and params " + componentConfiguredProperties);
        return sb.toString();
    }

    /**
     * Update the status attribute to reflect the component status
     */
    public void validate() {
        if (!VALID_SCOPES.contains(scope)) {
            log.error("Invalid scope " + scope + " for component " + name + "");
            status = STATUS_INVALID;
            return;
        }
        try {
            componentClass = Class.forName(clazz);
        } catch (Throwable e) {
            log.error("Invalid class for component " + name + ": " + clazz, e);
            status = STATUS_INVALID;
            return;
        }
        status = STATUS_VALID;
    }

    public int getStatus() {
        return status;
    }

    /**
     * @return The object represented in this component.
     */
    public Object getObject() throws LookupException {
        if (!enabled)
            return null;
        if (log.isDebugEnabled())
            log.debug("Getting " + scope + " component " + name);
        if (status != STATUS_INVALID) {
            LookupHelper helper = ComponentsContextManager.getLookupHelper();
            synchronized (helper.getSynchronizationObject(getScope())) {
                if (getTheInstance() == null) {
                    return makeAndSetNewInstance();
                }
            }
            return getTheInstance();
        } else {
            log.warn("Component " + name + " is in invalid status.");
        }
        return null;
    }

    protected final Object getTheInstance() throws LookupException {
        LookupHelper helper = ComponentsContextManager.getLookupHelper();
        if (helper != null) {
            return helper.lookupObject(scope, name);
        } else {
            throw new LookupException(
                    "Cannot get " + getName() + ". Factory operation is outside a valid context.");
        }
    }

    protected final void setTheInstance(Object instance) {
        LookupHelper helper = ComponentsContextManager.getLookupHelper();
        if (helper != null) {
            helper.storeObject(scope, name, instance);
        } else {
            log.error("Cannot set " + getName() + ". Factory operation is outside a valid context.");
        }
    }

    protected Object makeAndSetNewInstance() {
        LookupHelper helper = ComponentsContextManager.getLookupHelper();
        synchronized (helper.getSynchronizationObject(getScope())) {
            Object object = null;
            if (status == STATUS_VALID) {
                if (log.isDebugEnabled())
                    log.debug("Making and initializing new " + clazz);

                if (scope.equals(SCOPE_GLOBAL)) {
                    try {
                        object = componentClass.getMethod("getInstance", new Class[0]).invoke(null, new Object[0]);
                    } catch (Exception e) {
                        if (log.isDebugEnabled())
                            log.debug("Error using getInstance() method on " + clazz);
                    }
                }

                if (object == null)
                    try {
                        object = componentClass.getConstructor(new Class[0]).newInstance(new Object[0]);
                    } catch (Exception e) {
                        log.error("Error creating instance for component " + name + " :", e);
                    }
                //End. Object should be created now
                if (object == null) {
                    status = STATUS_INVALID;
                    log.error("Make sure the component class has a default public constructor"
                            + (scope.equals(SCOPE_GLOBAL) ? ", or a static getInstance() method." : "."));
                } else {
                    if (object instanceof FactoryLifecycle) {
                        try {
                            if (object instanceof BasicFactoryElement) {
                                ((BasicFactoryElement) object).setComponentName(name);
                                ((BasicFactoryElement) object).setComponentScope(scope);
                                ((BasicFactoryElement) object).setComponentDescription(description);
                                ((BasicFactoryElement) object).setComponentAlias(alias);
                            }
                            ((FactoryLifecycle) object).init();
                            ((FactoryLifecycle) object).stop();
                        } catch (Exception e) {
                            log.error("Error in component lifecycle ", e);
                        }
                    }
                    setTheInstance(object);
                    JXPathContext ctx = JXPathContext.newContext(object);
                    for (Iterator it = componentConfiguredProperties.keySet().iterator(); it.hasNext();) {
                        String propertyName = (String) it.next();
                        List propertyValue = (List) componentConfiguredProperties.get(propertyName);
                        try {
                            setObjectProperty(object, propertyName, propertyValue, ctx);
                        } catch (Exception e) {
                            log.error("Error. Cannot set property " + getName() + "." + propertyName
                                    + " with configured values " + propertyValue, e);
                        }
                    }
                    status = STATUS_VALID;
                    if (object instanceof FactoryLifecycle) {
                        try {
                            ((FactoryLifecycle) object).start();
                        } catch (Exception e) {
                            log.error("Error in component lifecycle ", e);
                        }
                    }
                    setCreationOrderNumber(getTree().getNewOrderCounter());
                }
            } else {
                log.error("Infinite loop detected. Caused by component " + name
                        + " or some other component used by it.");
            }
            return object;
        }
    }

    protected Object setObjectProperty(Object obj, String propertyName, List propertyValue, JXPathContext ctx)
            throws Exception {
        if (log.isDebugEnabled())
            log.debug("Setting in " + clazz + " property " + propertyName + " with values " + propertyValue);
        if (obj instanceof Map) {
            ((Map) obj).put(propertyName, getValueForProperty(propertyValue, String.class)); //Map of Strings
        } else if (obj instanceof List) {
            try {
                int index = Integer.parseInt(propertyName);
                while (((List) obj).size() <= index)
                    ((List) obj).add(null);
                ((List) obj).set(index, getValueForProperty(propertyValue, String.class)); //List of Strings
            } catch (Exception e) {
                log.error("Error setting position " + propertyName + " in List " + name + ". ", e);
            }
        } else if (propertyName.indexOf('.') != -1) { //Set it by JXPath, valid only for strings
            ctx = ctx != null ? ctx : JXPathContext.newContext(obj);
            ctx.setValue(propertyName.replace('.', '/'), getValueForProperty(propertyValue, String.class));
        } else {
            //Find a Field
            Field field = getField(obj, propertyName);
            if (field != null) {
                Class fieldClass = field.getType();
                Object value = getValueForProperty(propertyValue, fieldClass);
                field.set(obj, value);
                return obj;
            }

            // Find a getter and setter.
            Method getter = getGetter(obj, propertyName);
            if (getter == null) {
                log.error("Cannot find a getter for property " + propertyName + " in class " + clazz
                        + ". Ignoring property.");
                return obj;
            }
            Method setter = getSetter(obj, getter, propertyName);
            if (setter == null) {
                log.error("Cannot find a setter for property " + propertyName + " in class " + clazz
                        + ". Ignoring property.");
                return obj;
            }
            Object value = getValueForProperty(propertyValue, getter.getReturnType());
            if (log.isDebugEnabled())
                log.debug("Invoking " + setter + " with value=" + value);
            setter.invoke(obj, new Object[] { value });
        }
        return obj;
    }

    protected Field getField(Object obj, String propertyName) {
        try {
            Field field = obj.getClass().getField(propertyName);
            int modifiers = field.getModifiers();
            if (Modifier.isPublic(modifiers) && !Modifier.isFinal(field.getModifiers())) {
                return field;
            }
        } catch (NoSuchFieldException e) {
        }
        if (log.isDebugEnabled()) {
            log.debug("Could not find a field for property " + propertyName + " in " + obj.getClass());
        }
        return null;
    }

    protected Method getGetter(Object obj, String propertyName) {
        Method getter = null;
        String propertyAccessorSuffix = StringUtils.capitalize(propertyName);
        String getterName = "get" + propertyAccessorSuffix;
        try {
            getter = obj.getClass().getMethod(getterName, new Class[0]);
        } catch (NoSuchMethodException e) {
            log.debug("No getter " + getterName + " found.");
            String booleanGetterName = "is" + propertyAccessorSuffix;
            try {
                getter = obj.getClass().getMethod(booleanGetterName, new Class[0]);
            } catch (NoSuchMethodException e1) {
                log.debug("No getter " + booleanGetterName + " found.");
            }
        }
        return getter;
    }

    protected Method getSetter(Object obj, Method getter, String propertyName) {
        Method setter = null;
        String propertyAccessorSuffix = StringUtils.capitalize(propertyName);
        String setterName = "set" + propertyAccessorSuffix;
        Class returnType = getter.getReturnType();
        try {
            setter = obj.getClass().getMethod(setterName, new Class[] { returnType });
        } catch (NoSuchMethodException e) {
            log.error("Cannot find a setter for property " + propertyName + " in class " + clazz
                    + ". Ignoring property.");
        }
        return setter;
    }

    protected Object getValueForProperty(List values, Class expectedClass) throws Exception {
        if (log.isDebugEnabled())
            log.debug("Converting to " + expectedClass + " values " + values);
        if (values == null || values.isEmpty())
            return null;
        Object valueToReturn = null;
        for (int i = 0; i < values.size(); i++) {
            PropertyChangeProcessingInstruction instruction = (PropertyChangeProcessingInstruction) values.get(i);
            valueToReturn = instruction.getValueAfterChange(valueToReturn, expectedClass);
        }
        if (expectedClass.isArray() && expectedClass.getComponentType().isPrimitive() && valueToReturn != null) {
            //Convert arrays of Integers, Shorts, ... to primitive type arrays.
            if (expectedClass.getComponentType().equals(int.class)) {
                int[] newValueToReturn = new int[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Integer) ((Object[]) valueToReturn)[i++])
                        .intValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(boolean.class)) {
                boolean[] newValueToReturn = new boolean[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Boolean) ((Object[]) valueToReturn)[i++])
                        .booleanValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(long.class)) {
                long[] newValueToReturn = new long[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Long) ((Object[]) valueToReturn)[i++])
                        .longValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(char.class)) {
                char[] newValueToReturn = new char[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Character) ((Object[]) valueToReturn)[i++])
                        .charValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(double.class)) {
                double[] newValueToReturn = new double[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Double) ((Object[]) valueToReturn)[i++])
                        .doubleValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(float.class)) {
                float[] newValueToReturn = new float[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Float) ((Object[]) valueToReturn)[i++])
                        .floatValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(byte.class)) {
                byte[] newValueToReturn = new byte[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Byte) ((Object[]) valueToReturn)[i++])
                        .byteValue())
                    ;
                return newValueToReturn;
            } else if (expectedClass.getComponentType().equals(short.class)) {
                short[] newValueToReturn = new short[((Object[]) valueToReturn).length];
                for (int i = 0; i < newValueToReturn.length; newValueToReturn[i] = ((Short) ((Object[]) valueToReturn)[i++])
                        .shortValue())
                    ;
                return newValueToReturn;
            }
        }
        return valueToReturn;
    }
}