com.netspective.sparx.form.action.ActionWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.netspective.sparx.form.action.ActionWrapper.java

Source

/*
 * Copyright (c) 2000-2004 Netspective Communications LLC. All rights reserved.
 *
 * Netspective Communications LLC ("Netspective") permits redistribution, modification and use of this file in source
 * and binary form ("The Software") under the Netspective Source License ("NSL" or "The License"). The following
 * conditions are provided as a summary of the NSL but the NSL remains the canonical license and must be accepted
 * before using The Software. Any use of The Software indicates agreement with the NSL.
 *
 * 1. Each copy or derived work of The Software must preserve the copyright notice and this notice unmodified.
 *
 * 2. Redistribution of The Software is allowed in object code form only (as Java .class files or a .jar file
 *    containing the .class files) and only as part of an application that uses The Software as part of its primary
 *    functionality. No distribution of the package is allowed as part of a software development kit, other library,
 *    or development tool without written consent of Netspective. Any modified form of The Software is bound by these
 *    same restrictions.
 *
 * 3. Redistributions of The Software in any form must include an unmodified copy of The License, normally in a plain
 *    ASCII text file unless otherwise agreed to, in writing, by Netspective.
 *
 * 4. The names "Netspective", "Axiom", "Commons", "Junxion", and "Sparx" are trademarks of Netspective and may not be
 *    used to endorse or appear in products derived from The Software without written consent of Netspective.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT,
 * ARE HEREBY DISCLAIMED.
 *
 * NETSPECTIVE AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A
 * RESULT OF USING OR DISTRIBUTING THE SOFTWARE. IN NO EVENT WILL NETSPECTIVE OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN
 * IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 */
package com.netspective.sparx.form.action;

import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.commons.lang.exception.NestableRuntimeException;

import com.netspective.axiom.ConnectionContext;
import com.netspective.axiom.connection.DriverManagerConnectionProvider;
import com.netspective.commons.value.Value;
import com.netspective.commons.value.ValueSource;
import com.netspective.commons.value.ValueSources;
import com.netspective.commons.xdm.XdmParseContext;
import com.netspective.commons.xdm.XmlDataModelSchema;
import com.netspective.commons.xdm.XmlDataModelSchema.ConstructionFinalizeListener;
import com.netspective.commons.xdm.XmlDataModelSchema.CustomElementAttributeSetter;
import com.netspective.commons.xdm.exception.DataModelException;
import com.netspective.sparx.form.field.DialogFieldStates;
import com.netspective.sparx.form.field.DialogFields;
import com.netspective.sparx.util.HttpUtils;

public class ActionWrapper implements CustomElementAttributeSetter, ConstructionFinalizeListener {
    private final XmlDataModelSchema actionSchema;
    private final ActionDialog actionDialog;
    private final Method setActionDialogContextMethod;
    private final Method setServletEnvironmentMethod;
    private final Method getDataSourceNameMethod;
    private final Method getDataSourceInfoMethod;
    private final Method setConnectionMethod;
    private final Method setConnectionContextMethod;
    private ActionConstructor actionConstructor;
    private ActionExecutor actionExecutor;
    private ActionValidator actionValidator;
    private Map fieldMutators = new HashMap();
    private String assignRequestParams;
    private String executeMethodName = "execute";
    private String getNextActionMethodName = "getNextAction";
    private String isValidMethodName = "isValid";

    public ActionWrapper(final ActionDialog actionDialog, final Class actionClass) {
        this.actionDialog = actionDialog;
        this.actionSchema = actionClass == null ? null : XmlDataModelSchema.getSchema(actionClass);
        this.setServletEnvironmentMethod = getMethod("setServletEnvironment",
                new Class[] { Servlet.class, ServletRequest.class, ServletResponse.class });
        this.getDataSourceNameMethod = getMethod("getDataSourceName", (Class[]) null);
        this.getDataSourceInfoMethod = getMethod("getDataSourceInfo", (Class[]) null);
        this.setConnectionMethod = getMethod("setConnection", Connection.class);
        this.setConnectionContextMethod = getMethod("setConnectionContext", ConnectionContext.class);
        this.setActionDialogContextMethod = getMethod("setActionDialogContext", ActionDialogContext.class);
    }

    public void initializeBean() throws NoSuchMethodException {
        locateConstructor();
        locateMutators();
        locateValidator();
        locateExecutor();
    }

    public void finalizeConstruction(XdmParseContext pc, Object element, String elementName)
            throws DataModelException {
        try {
            initializeBean();
        } catch (NoSuchMethodException e) {
            actionDialog.getLog().error(e);
            throw new DataModelException(pc, e);
        }
    }

    /**
     * If the <action> tag has any attributes other than 'class' then they are field-name to setter/mutator name
     * maps like <action class='x.y.z' X="XXX" Y="ZZZ"/>. This would map the field name "X" to a setXXX() method
     * instead of setX(). Similarly, the field name "Y" would call setZZZ() instead of setZ(). Any fields not mapped
     * like this would be mapped normally (same as field name).
     */
    public void setCustomDataModelElementAttribute(XdmParseContext pc, XmlDataModelSchema schema, Object parent,
            String attrName, String attrValue)
            throws DataModelException, InvocationTargetException, IllegalAccessException, DataModelException {
        DialogFields dialogFields = actionDialog.getFields();

        if (dialogFields.getByQualifiedName(attrName) != null) {
            FieldMutator fieldMutator = locateMutator(attrValue);
            if (fieldMutator != null)
                fieldMutators.put(attrName, fieldMutator);
        } else
            schema.setAttribute(pc, parent, attrName, attrValue, true);

    }

    public String getAssignRequestParams() {
        return assignRequestParams;
    }

    public void setAssignRequestParams(String assignRequestParams) {
        this.assignRequestParams = assignRequestParams;
    }

    public String getExecuteMethodName() {
        return executeMethodName;
    }

    public void setExecuteMethodName(String executeMethodName) {
        this.executeMethodName = executeMethodName;
    }

    public String getGetNextActionMethodName() {
        return getNextActionMethodName;
    }

    public void setGetNextActionMethodName(String getNextActionMethodName) {
        this.getNextActionMethodName = getNextActionMethodName;
    }

    public ActionValidator getActionValidator() {
        return actionValidator;
    }

    private Method getMethod(String name, Class paramType) {
        try {
            return actionSchema.getBean().getMethod(name, new Class[] { paramType });
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    private Method getMethod(String name, Class[] paramTypes) {
        try {
            return actionSchema.getBean().getMethod(name, paramTypes);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    private void locateConstructor() {
        final Class actionClass = actionSchema.getBean();

        actionConstructor = new ActionConstructor() {
            public Object constructAction() throws Exception {
                return actionClass.newInstance();
            }
        };
    }

    public FieldMutator locateMutator(String attributeName) {
        try {
            final Method method = (Method) actionSchema.getAttributeSetterMethods().get(attributeName);
            if (method != null) {
                Class[] args = method.getParameterTypes();
                if (args.length == 1) {
                    Class arg = args[0];
                    if (java.lang.String.class.equals(arg) && arg.isArray()) {
                        return new FieldMutator() {
                            public void set(Object instance, Value value) throws Exception {
                                method.invoke(instance, new Object[] { value.getTextValues() });
                            }
                        };
                    } else if (Value.class.equals(arg)) {
                        return new FieldMutator() {
                            public void set(Object instance, Value value) throws Exception {
                                method.invoke(instance, new Object[] { value });
                            }
                        };
                    } else if (Date.class.equals(arg)) {
                        return new FieldMutator() {
                            public void set(Object instance, Value value) throws Exception {
                                // assuming that if a date is expected then the Object of the value.getValue() will be a date
                                method.invoke(instance, new Object[] { (Date) value.getValue() });
                            }
                        };
                    } else {
                        final XmlDataModelSchema.AttributeSetter as = (XmlDataModelSchema.AttributeSetter) actionSchema
                                .getAttributeSetters().get(attributeName);
                        if (as != null) {
                            return new FieldMutator() {
                                public void set(Object instance, Value value) throws Exception {
                                    if (value.getTextValue() != null)
                                        as.set(null, instance, value.getTextValue());
                                }
                            };
                        }
                    }
                }
            } else
                actionDialog.getLog().warn(
                        "No setter method found for field " + attributeName + " in " + actionSchema.getBean());
        } catch (Exception e) {
            actionDialog.getLog().error("Unable to create mutator '" + attributeName + "' for "
                    + actionSchema.getBean() + ": " + e.getMessage(), e);
            throw new NestableRuntimeException(e);
        }

        // if we get to here, unable to set the attribute
        actionDialog.getLog()
                .warn("Unable to locate mutator '" + attributeName + "' for " + actionSchema.getBean());

        return null;
    }

    public void locateMutators() {
        DialogFields dialogFields = actionDialog.getFields();

        for (Iterator i = dialogFields.getFieldsMapByQualifiedName().keySet().iterator(); i.hasNext();) {
            String fieldName = (String) i.next();
            if (!fieldMutators.containsKey(fieldName)) {
                FieldMutator fieldMutator = locateMutator(fieldName);
                if (fieldMutator != null)
                    fieldMutators.put(fieldName, fieldMutator);
            }
        }
    }

    public void locateValidator() throws NoSuchMethodException {
        final Method isValidMethod = getMethod(isValidMethodName, List.class) == null
                ? getMethod(isValidMethodName, (Class[]) null)
                : getMethod(isValidMethodName, List.class);
        if (isValidMethod != null) {
            if (isValidMethod.getReturnType() == boolean.class) {
                actionValidator = new ActionValidator() {
                    public boolean isValid(Object instance, List messages) throws Exception {
                        Boolean result = (Boolean) isValidMethod.invoke(instance, new Object[] { messages });
                        return result.booleanValue();
                    }
                };
            }
        } else
            actionValidator = null;
    }

    public void locateExecutor() throws NoSuchMethodException {
        final Method execMethod = getMethod(executeMethodName, Writer.class) == null
                ? getMethod(executeMethodName, (Class[]) null)
                : getMethod(executeMethodName, Writer.class);
        if (execMethod == null)
            throw new NoSuchMethodException("Unable to find " + executeMethodName + "() or " + executeMethodName
                    + "(Writer) method in " + actionSchema.getBean());

        if (execMethod.getParameterTypes().length > 0) {
            if (execMethod.getReturnType() == String.class) {
                actionExecutor = new ActionExecutor() {
                    public String execute(Writer writer, Object instance) throws Exception {
                        return (String) execMethod.invoke(instance, new Object[] { writer });
                    }
                };
            } else {
                actionExecutor = new ActionExecutor() {
                    public String execute(Writer writer, Object instance) throws Exception {
                        execMethod.invoke(instance, new Object[] { writer });
                        return null;
                    }
                };
            }
        } else {
            if (execMethod.getReturnType() == String.class) {
                actionExecutor = new ActionExecutor() {
                    public String execute(Writer writer, Object instance) throws Exception {
                        return (String) execMethod.invoke(instance, null);
                    }
                };
            } else {
                actionExecutor = new ActionExecutor() {
                    public String execute(Writer writer, Object instance) throws Exception {
                        execMethod.invoke(instance, null);
                        return null;
                    }
                };
            }
        }
    }

    protected void initInstance(Object instance, ActionDialogContext dc) throws Exception {
        if (setActionDialogContextMethod != null)
            setActionDialogContextMethod.invoke(instance, new Object[] { dc });
        if (setServletEnvironmentMethod != null)
            setServletEnvironmentMethod.invoke(instance,
                    new Object[] { dc.getServlet(), dc.getRequest(), dc.getResponse() });
    }

    protected void assignInstanceValues(Object instance, ActionDialogContext dc) throws Exception {
        DialogFieldStates dfs = dc.getFieldStates();

        for (Iterator i = fieldMutators.entrySet().iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            String fieldName = (String) entry.getKey();
            FieldMutator actionMutator = (FieldMutator) entry.getValue();

            actionMutator.set(instance, dfs.getState(fieldName).getValue());
        }

        if (assignRequestParams != null)
            HttpUtils.assignParamsToInstance(dc.getHttpRequest(), instance, assignRequestParams);
    }

    protected void assignDatabaseConnection(Object instance, ActionDialogContext dc) throws Exception {
        if (setConnectionContextMethod != null || setConnectionMethod != null) {
            String dataSourceId = dc.getDefaultDataSource();
            if (getDataSourceNameMethod != null)
                dataSourceId = (String) getDataSourceNameMethod.invoke(instance, null);

            if (dataSourceId != null) {
                ValueSource dataSourceIdValueSource = ValueSources.getInstance()
                        .getValueSourceOrStatic(dataSourceId);
                dataSourceId = dataSourceIdValueSource.getTextValue(dc);

                if (dataSourceId.equals("DEFAULT"))
                    dataSourceId = null;

                ConnectionContext cc = dc.openActionConnection(dataSourceId);
                if (setConnectionContextMethod != null)
                    setConnectionContextMethod.invoke(instance, new Object[] { cc });
                if (setConnectionMethod != null)
                    setConnectionMethod.invoke(instance, new Object[] { cc.getConnection() });
            } else {
                if (getDataSourceInfoMethod != null) {
                    Object dataSourceInfo = getDataSourceInfoMethod.invoke(instance, null);
                    if (dataSourceInfo != null) {
                        DriverManagerConnectionProvider.DataSourceInfo dsInfo = DriverManagerConnectionProvider.PROVIDER
                                .createDataSource();
                        if (dataSourceInfo instanceof List) {
                            if (!dsInfo.setInfo((List) dataSourceInfo))
                                dc.getDialog().getLog().error(
                                        "Found getDataSourceInfo() method in action class and it returns type List but has fewer than two entries. Need at least driver class and URL.");
                        } else if (dataSourceInfo instanceof String[]) {
                            if (!dsInfo.setInfo((String[]) dataSourceInfo))
                                dc.getDialog().getLog().error(
                                        "Found getDataSourceInfo() method in action class and it returns type String[] but has fewer than two entries. Need at least driver class and URL.");
                        } else if (dataSourceInfo instanceof Map) {
                            if (!dsInfo.setInfo((Map) dataSourceInfo, null))
                                dc.getDialog().getLog().error(
                                        "Found getDataSourceInfo() method in action class and it returns type Map but does not have 'jdbc-driver-class' or 'jdbc-connection-url'.");
                        } else
                            dc.getDialog().getLog().error(
                                    "Found getDataSourceInfo() method in action class but it returns an object ("
                                            + dataSourceInfo + ") that NEFS doesn't understand.");

                        ConnectionContext cc = dc.openActionConnection(dsInfo);
                        if (setConnectionContextMethod != null)
                            setConnectionContextMethod.invoke(instance, new Object[] { cc });
                        if (setConnectionMethod != null)
                            setConnectionMethod.invoke(instance, new Object[] { cc.getConnection() });
                    }
                }
            }
        }
    }

    public Object constructInstance(ActionDialogContext dc) throws Exception {
        Object result;
        try {
            result = actionConstructor.constructAction();
        } catch (Exception e) {
            actionDialog.getLog().error(e);
            throw new NestableRuntimeException(e);
        }

        initInstance(result, dc);
        assignInstanceValues(result, dc);
        assignDatabaseConnection(result, dc);

        return result;
    }

    public ActionExecutor getActionExecutor() {
        return actionExecutor;
    }

    protected interface ActionConstructor {
        public Object constructAction() throws Exception;
    }

    protected interface ActionValidator {
        public boolean isValid(Object instance, List messages) throws Exception;
    }

    protected interface ActionExecutor {
        public String execute(Writer writer, Object instance) throws Exception;
    }

    protected interface FieldMutator {
        public void set(Object instance, Value value) throws Exception;
    }

}