org.xchain.framework.util.ComponentUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.xchain.framework.util.ComponentUtil.java

Source

/**
 *    Copyright 2011 meltmedia
 *
 *    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.xchain.framework.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.jxpath.JXPathContext;
import org.xchain.annotations.Begin;
import org.xchain.annotations.Component;
import org.xchain.annotations.End;
import org.xchain.annotations.In;
import org.xchain.annotations.PrefixMapping;
import org.xchain.framework.jxpath.ScopedJXPathContextImpl;
import org.xchain.framework.lifecycle.ComponentAnalysis;
import org.xchain.framework.lifecycle.ComponentAnalysis.InjectionAnalysis;

/**
 * Utility class for Components.
 *
 * @author Devon Tackett
 * @author John Trimble
 */
public class ComponentUtil {

    /**
     * Perform an analysis of a component class.
     * 
     * @param componentClass The component class to analyze.
     * 
     * @return An analysis of the component class.
     */
    public static ComponentAnalysis createAnalysis(Class componentClass) {
        ComponentAnalysis analysis = new ComponentAnalysis(componentClass);

        // Get the component annotation
        Component componentAnnotation = (Component) componentClass.getAnnotation(Component.class);

        // Get the localname and scope.
        analysis.setLocalName(componentAnnotation.localName());
        analysis.setScope(componentAnnotation.scope());

        // Get the method based dependency injections.  This will only work with public methods on the class.
        for (Method method : componentClass.getMethods()) {
            In inAnnotation = method.getAnnotation(In.class);
            if (inAnnotation != null) {
                analysis.addInjection(new InjectionAnalysis(inAnnotation.select(), inAnnotation.prefixMappings(),
                        inAnnotation.nullable(), method));
            }
        }

        // Build field based dependency injections.
        analyzeFields(componentClass, analysis);

        return analysis;
    }

    /**
     * Gather field based dependency injections.  This will find any private, protected or public fields with dependency injections.
     * 
     * @param componentClass The component class to analyze.
     * @param analysis The analysis to build upon.
     */
    private static void analyzeFields(Class componentClass, ComponentAnalysis analysis) {
        for (Field field : componentClass.getDeclaredFields()) {
            In inAnnotation = field.getAnnotation(In.class);
            if (inAnnotation != null) {
                field.setAccessible(true);
                analysis.addInjection(new InjectionAnalysis(inAnnotation.select(), inAnnotation.prefixMappings(),
                        inAnnotation.nullable(), field));
            }
        }

        // Check any parent interfaces.
        for (Class parentClass : componentClass.getInterfaces()) {
            analyzeFields(parentClass, analysis);
        }

        // Check the super class.
        if (componentClass.getSuperclass() != null) {
            analyzeFields(componentClass.getSuperclass(), analysis);
        }
    }

    /**
     * Create a new component from the given analysis.  This does not perform dependency injection nor begin the component.
     */
    public static Object createComponent(ComponentAnalysis analysis)
            throws InstantiationException, IllegalAccessException {
        return analysis.getComponentClass().newInstance();
    }

    /**
     * Perform dependency injection on the given component.
     */
    public static void doInjection(Object component, ComponentAnalysis analysis, JXPathContext context)
            throws IllegalAccessException, InvocationTargetException {
        for (InjectionAnalysis injection : analysis.getInjections()) {
            Map<String, String> originalPrefixMapping = pushPrefixMap(context, injection.getPrefixMappings());

            try {
                if (injection.getField() != null) {
                    injection.getField().set(component, context.getValue(injection.getSelect()));
                } else if (injection.getMethod() != null) {
                    injection.getMethod().invoke(component, context.getValue(injection.getSelect()));
                }
            } catch (Exception ex) {
                if (!injection.isNullable()) {
                    // Unable to inject value on a non-nullable field.
                    String failurePoint = null;
                    if (injection.getField() != null)
                        failurePoint = "field '" + injection.getField().getName() + "'";
                    else if (injection.getMethod() != null)
                        failurePoint = "method '" + injection.getMethod().getName() + "'";
                    String failurePath = " from path '" + injection.getSelect() + "'";
                    if (context instanceof ScopedJXPathContextImpl) {
                        ScopedJXPathContextImpl impl = (ScopedJXPathContextImpl) context;
                        failurePath += " at scope '" + impl.getScope() + "'.";
                    }
                    throw new DependencyInjectionException(
                            "Unable to inject value into " + failurePoint + failurePath, ex);
                }
            }

            popPrefixMap(context, originalPrefixMapping);
        }
    }

    /**
     * Add the given array of prefix mappings to the context.
     * 
     * @param context The working context.
     * @param prefixMappings The prefix mappings to add.
     * 
     * @return A prefix to namespace uri mapping of prefixes that were replaced. 
     */
    private static Map<String, String> pushPrefixMap(JXPathContext context, PrefixMapping prefixMappings[]) {
        Map<String, String> originalPrefixMapping = null;

        if (prefixMappings.length > 0) {
            originalPrefixMapping = new HashMap<String, String>();
            // Perform prefix mappings
            for (PrefixMapping prefixMap : prefixMappings) {
                // Store the original namespace for the prefix.
                originalPrefixMapping.put(prefixMap.prefix(), context.getNamespaceURI(prefixMap.prefix()));
                // Register the new namespace.
                context.registerNamespace(prefixMap.prefix(), prefixMap.uri());
            }
        }

        return originalPrefixMapping;
    }

    /**
     * Restore the given prefix to namespace uri mapping onto the context.
     * 
     * @param context The working context.
     * @param originalPrefixMapping The mapping to restore.
     */
    private static void popPrefixMap(JXPathContext context, Map<String, String> originalPrefixMapping) {
        if (originalPrefixMapping != null) {
            // Reverse prefix mappings
            for (String prefix : originalPrefixMapping.keySet()) {
                // Restore the original namespace for the prefix.
                context.registerNamespace(prefix, originalPrefixMapping.get(prefix));
            }
        }
    }

    /**
     * Invoke the begin method on the given component. 
     */
    public static void doBegin(Object component) throws InvocationTargetException, IllegalAccessException {
        for (Method method : component.getClass().getMethods()) {
            Begin beginAnnotation = method.getAnnotation(Begin.class);
            if (beginAnnotation != null) {
                method.invoke(component);
            }
        }
    }

    /**
     * Invoke the end method on the given component. 
     */
    public static void doEnd(Object component) throws InvocationTargetException, IllegalAccessException {
        for (Method method : component.getClass().getMethods()) {
            End beginAnnotation = method.getAnnotation(End.class);
            if (beginAnnotation != null) {
                method.invoke(component);
            }
        }
    }
}