org.apache.struts.service.factory.FactoryService.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.struts.service.factory.FactoryService.java

Source

/*
 * $Header: /home/cvs/jakarta-struts/contrib/service-manager/services/factory/src/org/apache/struts/service/factory/FactoryService.java,v 1.2 2002/12/08 07:53:31 rleland Exp $
 * $Revision: 1.2 $
 * $Date: 2002/12/08 07:53:31 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Struts", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.struts.service.factory;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.io.IOException;
import javax.servlet.ServletException;
import java.util.MissingResourceException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;
import org.apache.struts.service.Service;
import org.apache.struts.service.ServiceManager;
import org.apache.struts.service.ServletServiceManager;
import org.apache.struts.action.ServicesServlet;
import org.xml.sax.Attributes;

public class FactoryService extends Service {

    /** Default class name to register bean factory mapping  
    */
    protected String factoryMappingClass = "org.apache.struts.service.factory.FactoryMapping";

    /** Default class name to register bean template
    */
    protected String templateClass = "org.apache.struts.service.factory.Template";

    /** Default class name to register parameter in bean template
    */
    protected String parameterMappingClass = "org.apache.struts.service.factory.ParameterMapping";

    /** Default class name to register link between bean template and action
    */
    protected String registrationClass = "org.apache.struts.service.factory.Registration";

    /** Default class name to register parameter setter for bean registration
    */
    protected String parameterValueClass = "org.apache.struts.service.factory.ParameterValue";

    /** Default scope to store created beans
    */
    protected String defaultBeanSaveScope = "request";

    /** Default scope to search parameter value
    */
    protected String defaultParameterSource = "request.parameter";

    /** Bean registrations map to store links between bean templates and actions.
    *  Every map entry is pair of key  and value.
    *  Key is a action path - equal to path with which appropriate action 
    *  is stored. Servlet uses it to find list of bean registrations
    *  for corresponed action.
    *  Value is a ArrayList with bean registrations. Each registration
    *  holds name of the bean template, name of the factory, etc.
    */
    protected HashMap registrations = new HashMap();

    /** Factories map - all created at processing phase
    *  factories stay here.
    */
    protected HashMap factories = new HashMap();

    /** FactoryMapping map - container to store factories descriptions.
    */
    protected HashMap factoryMappings = new HashMap();

    /** Templates map - to store info about registered in application
    *  bean templates. Each bean template description contains info
    *  about bean type, template name, to refer it from bean registrations and
    *  list of parameters descriptions to use it at point of bean creation
    *  in factory.
    */
    protected HashMap templates = new HashMap();

    /**
    * The resources object for our internal resources.
    */
    protected MessageResources internal = null;

    /**
    * The Java base name of our internal resources.
    */
    protected String internalName = "org.apache.struts.service.factory.FactoryServiceResources";

    /**
    * Initialize our internal MessageResources bundle.
    *
    * @exception ServletException if we cannot initialize these resources
    */
    protected void initInternal() throws Exception {

        try {
            internal = MessageResources.getMessageResources(internalName);
        } catch (MissingResourceException e) {
            log("Cannot load internal resources from '" + internalName + "'", e);
            throw new UnavailableException("Cannot load internal resources from '" + internalName + "'");
        }
    }

    public void init() throws Exception {
        initInternal();
    }

    /** Adds bean registrationto be used in action processing.
    * @param beanRegistration The bean registration.
    */
    public void addRegistration(Registration registration) {

        String key = registration.getPath();
        ArrayList beanList = (ArrayList) registrations.get(key);
        if (beanList == null) {
            beanList = new ArrayList();
            registrations.put(key, beanList);
        }
        beanList.add(registration);
    }

    /**
    * Register a bean factory definition to the set configured for this servlet.
    *
    * @param beanFactoryMapping The bean factory definition.
    */
    public void addFactoryMapping(FactoryMapping factoryMapping) {
        factoryMappings.put(factoryMapping.getName(), factoryMapping);
    }

    /**
    * Register a bean template definition to the set configured for this servlet.
    *
    * @param beanTemplate The bean template definition to be added
    */
    public void addTemplate(Template template) {
        templates.put(template.getName(), template);
    }

    /** Return the bean factory definition associated with the specified
    * logical name, if any; otherwise return <code>null</code>.
    *
    * @param name Logical name of the requested bean factory definition
    * @return BeanFactoryMapping
    */
    public FactoryMapping findFactoryMapping(String name) {
        return ((FactoryMapping) factoryMappings.get(name));
    }

    /** Return the bean template definition associated with the specified
    * logical name, if any; otherwise return <code>null</code>.
    *
    * @param name Logical name of the requested bean template definition
    * @return BeanTemplate
    */
    public Template findTemplate(String name) {
        return ((Template) templates.get(name));
    }

    protected ServicesServlet getServlet() {
        return (ServicesServlet) ((ServletServiceManager) manager).getServlet();
    }

    /**
    * General purpose preprocessing hook used to generate beans according
    * to list of bean registrations.
    *
    * @param request The servlet request we are processing
    * @param response The servlet response we are generating
    *
    * @return <code>true</code> if the remainder of the standard processing
    *  should be performed, or <code>false</code> if the response has already
    *  been created so the calling method should immediately exit
    *
    * @exception IOException if an input/output error occurs
    * @exception ServletException if a servlet exception occurs
    */
    //        public boolean processPreprocess( HttpServletRequest request,
    //                                             HttpServletResponse response)
    public boolean processPreprocess(Object parameters) throws IOException, ServletException {

        Object[] parList = (Object[]) parameters;
        HttpServletRequest request = (HttpServletRequest) parList[0];
        HttpServletResponse response = (HttpServletResponse) parList[1];

        String path = getServlet().processPath(request);
        if (path == null) {
            if (getDebug() >= 1)
                log(" No path available for request URI " + request.getRequestURI());
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, internal.getMessage("processPath"));
            return false;
        }
        if (getDebug() >= 1)
            log("Processing a " + request.getMethod() + " for " + path);

        ActionMapping mapping = getServlet().processMapping(path, request);
        if (mapping == null) {
            if (getDebug() >= 1)
                log(" No mapping available for path " + path);
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, internal.getMessage("processInvalid", path));
            return false;
        }

        ArrayList beanList = (ArrayList) registrations.get(mapping.getPath());

        // Bean list is not null - there are some beans registered to be created
        // before this action processing
        if (beanList != null) {

            if (getDebug() >= 1)
                log("BeanList not null! Start to create beans");

            Iterator iterator = beanList.iterator();
            while (iterator.hasNext()) {
                if (!processRegistration((Registration) iterator.next(), request, response))
                    return false;
            }
        }

        return (true);

    }

    /** Process bean registration. It returns false if some errors
     *  occured and true as flag of successful operation. Successful
     *  issue is after such steps as - <br>
     *  <p>
     *  <li> search bean template
     *  <li> search factory
     *  <li> create bean with this factory from bean template
     *  <li> store bean in session or request scope</p>
     *
     *  @param beanRegistration Bean registration to be processed
     *  @param request The servlet request we are processing
     *  @param response The servlet response we are generating
     *
     *  @return boolean
     *
     *  @throws IOExceprion
     *  @throws ServletExceprion
     */
    public boolean processRegistration(Registration registration, HttpServletRequest request,
            HttpServletResponse response) throws IOException, ServletException {
        String name = null;
        FactoryMapping factoryMapping = null;
        Template template = null;
        Object bean = null;

        if (getDebug() >= 1)
            log("Process Registration for name = '" + registration.getName() + "' and factory '"
                    + registration.getFactory() + "'");

        factoryMapping = findFactoryMapping(registration.getFactory());
        // find bean factory or exit with error
        if (factoryMapping == null) {
            if (getDebug() >= 1)
                log(" No factory available for name " + registration.getFactory());
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid factory name");
            return false;
        }
        // find bean template or exit with error
        template = findTemplate(registration.getName());
        if (template == null) {
            if (getDebug() >= 1)
                log(" No template available for name " + registration.getName());
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid template name");
            return false;
        }
        try {
            bean = processCreateBean(template, factoryMapping, registration, request);
        } catch (Throwable e) {
            log("Exception at create instance process!", e);
            e.printStackTrace(System.out);
        }
        if (bean == null) {
            if (getDebug() >= 1)
                log(" Bean not created! Bean template name - " + name);
            if (registration.getNecessary()) {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                        "Can not create internal resource");
                return false;
            }
        } else {
            String beanName = null;
            String beanScope = registration.getScope();
            if (registration.getAlias() != null)
                beanName = registration.getAlias();
            else
                beanName = registration.getName();
            if (beanScope == null)
                beanScope = defaultBeanSaveScope;
            if ("session".equals(beanScope)) {
                request.getSession().setAttribute(beanName, bean);
                if (getDebug() >= 1)
                    log("Save bean in session context under name - " + beanName);
            } else if ("request".equals(beanScope)) {
                request.setAttribute(beanName, bean);
                if (getDebug() >= 1)
                    log("Save bean in request context under name - " + beanName + " object saved in request - "
                            + request.getAttribute(beanName));
            } else {
                if (getDebug() >= 1)
                    log("Bean is not saved in scope - " + registration.getScope());
            }
        }
        return true;
    }

    /** Create bean from bean template with help of bean factory. Bind all
     *  parameters, find or create factory and call <code>Factory.create</code>
     *  method to create target bean.
     *
     *  @param beanTemplate Template of bean to be created (type, parameters, etc.)
     *  @param beanFactoryMapping Factory mapping to find and use target factory
     *  @param beanRegistration Bean registration
     *  @param request The servlet request we are processing
     *
     *  @return Object
     * 
     *  @throws Throwable
     */
    public Object processCreateBean(Template template, FactoryMapping factoryMapping, Registration registration,
            HttpServletRequest request) throws Throwable {
        Object bean = null;
        Parameter[] parameters = null;
        Factory factory = null;
        int parametersCount = template.getParametersCount();
        if (parametersCount > 0) {
            parameters = new Parameter[parametersCount];
            Iterator parametersIterator = template.getParameters();
            for (int i = 0; parametersIterator.hasNext(); i++) {
                parameters[i] = processParameterInit((ParameterMapping) parametersIterator.next(), registration,
                        request);
            }
        }
        factory = processFactoryCreate(factoryMapping, request);
        if (factory != null) {
            if (getDebug() >= 1)
                log("Factory created or found - " + factory);
            bean = factory.create(template.getType(), parameters);
            if (getDebug() >= 1) {
                if (bean != null)
                    log("Bean created - " + bean);
                else
                    log("Bean not created for template - " + template.getName());
            }
        } else {
            if (getDebug() >= 1)
                log("Factory not created! - " + factory);
        }
        return bean;
    }

    /** Initialize parameter to be used at bean creation process in bean factory.
     *  Strategy to choose value to set tho the bean parameter - <br><p>
     *  <li>if bean registration contains direct parameter setter  - 
     *      parameter-value tag with value to be used for this parameter
     *      then new parameter will initialized with it
     *  <li>if parameter mapping has <code>force</code> attribute with 
     *      <code>true</code> value then value of the property 
     *      <code>value</code> from the <cede>ParameterMapping</code>
     *      will be used as value for the parameter
     *  <li>if <code>source</code> parameter supplied in parameter mapping
     *      then retrieve value from specified source (default source - 
     *      request.parameter). If nothing found in specified source
     *      (null value) then <code>value</code> attribute from 
     *      paramater mapping will be used as default value for parameter.</p>
     *  
     *  <p>Available sources - <br>
     *  <li><code>request.parameter</code> - parameter of the request by name from 
     *      <code>name</code> attribute from parameterMapping
     *  <li><code>request</code> or <code>request.attribute</code> - attribute
     *      from request named by name from <code>name</code> attribute from 
     *      parameterMapping
     *  <li><code>session</code> or <code>session.attribute</code> - attribute
     *      from session named by name from <code>name</code> attribute from 
     *      parameterMapping
     *  <li><code>application</code> or <code>application.attribute</code> - 
     *      attribute from application scope named by name from <code>name</code> 
     *      attribute from parameterMapping 
     *  <li><code>application.property</code> - value of the servlet property
     *      named by name from <code>name</code> attribute from parameterMapping
     *  <li><code>form</code> or <code>form.property</code> - value from form
     *      mapped to this action if exists one. <code>name</code> attribute 
     *      from parameterMapping used as name of the property
     *  </p>
     *  
     *  @param parameterMapping Mapping for parameter
     *  @param registration Bean registration
     *  @param request The servlet request we are processing
     *
     *  @return Parameter
     */
    public Parameter processParameterInit(ParameterMapping parameterMapping, Registration registration,
            HttpServletRequest request) throws Throwable {
        Parameter parameter = new Parameter(parameterMapping);
        Object parValue = registration.getParameterValue(parameterMapping.getName());
        if (parValue != null) {
            parameter.setValue(ConvertUtils.convert((String) parValue, parameterMapping.getStub()));
        } else if (parameterMapping.getForce()) {
            parameter.setValue(ConvertUtils.convert(parameterMapping.getValue(), parameterMapping.getStub()));
        } else {
            String parameterSource = parameterMapping.getSource();
            if (parameterSource == null)
                parameterSource = defaultParameterSource;
            if ("request".equals(parameterSource) || "request.attribute".equals(parameterSource)) {
                parValue = request.getAttribute(parameterMapping.getName());
                if (parValue != null && parValue.getClass().getName().equals(parameterMapping.getType())) {
                    parameter.setValue(parValue);
                } else {
                    if (getDebug() >= 1)
                        log("Attribute for parameter '" + parameterMapping.getName()
                                + "' not found in request scope or type is different");
                }
            } else if ("session".equals(parameterSource) || "session.attribute".equals(parameterSource)) {
                HttpSession session = ((HttpServletRequest) request).getSession(false);
                if (session != null) {
                    parValue = session.getAttribute(parameterMapping.getName());
                    if (parValue != null && parValue.getClass().getName().equals(parameterMapping.getType())) {
                        parameter.setValue(parValue);
                    } else {
                        if (getDebug() >= 1)
                            log("Attribute for parameter '" + parameterMapping.getName()
                                    + "' not found in session scope or type is different");
                    }
                }
            } else if ("application".equals(parameterSource) || "application.attribute".equals(parameterSource)) {
                parValue = getServlet().getServletContext().getAttribute(parameterMapping.getName());
                if (parValue != null && parValue.getClass().getName().equals(parameterMapping.getType())) {
                    parameter.setValue(parValue);
                } else {
                    if (getDebug() >= 1)
                        log("Attribute for parameter '" + parameterMapping.getName()
                                + "' not found in application scope or type is different");
                }
            } else if ("application.property".equals(parameterSource)) {
                parameter.setValue(PropertyUtils.getProperty(this, parameterMapping.getName()));
            } else if ("form".equals(parameterSource) || "form.property".equals(parameterSource)) {
                parameter.setValue(PropertyUtils.getProperty(this, parameterMapping.getName()));
            } else {
                parameter.setValue(ConvertUtils.convert(request.getParameter(parameterMapping.getName()),
                        parameterMapping.getStub()));
            }
            if (parameter.getValue() == null) {
                parameter.setValue(ConvertUtils.convert(parameterMapping.getValue(), parameterMapping.getStub()));
            }
        }
        return parameter;
    }

    /** Returns ready to use bean factory. First time it try to
      * find it in map of early created factories. It returns one if finds
      * any. Otherwise new factory will be instantiated, stored in
      * in cash and method returns it.
      * @param factoryMapping - factory mapping with factory definitions
      * @param request - http request link
     */
    protected Factory processFactoryCreate(FactoryMapping factoryMapping, HttpServletRequest request) {

        // Acquire the Action instance we will be using
        String factoryClass = factoryMapping.getType();

        if (getDebug() >= 1)
            log(" Looking for Factory instance for class " + factoryClass);

        Factory factoryInstance = (Factory) factories.get(factoryClass);
        if (factoryInstance == null) {
            synchronized (factories) {

                if (getDebug() >= 1)
                    log("  Double checking for Factory instance already there");

                // Double check to avoid a race condition
                factoryInstance = (Factory) factories.get(factoryClass);
                if (factoryInstance != null)
                    return (factoryInstance);

                try {

                    if (getDebug() >= 1)
                        log("  Creating new Factory instance for class - " + factoryClass);

                    factoryInstance = factoryMapping.instantiateFactory();
                    factoryInstance.setFactoryService(this);
                    factories.put(factoryClass, factoryInstance);
                } catch (Throwable t) {
                    log("Error creating bean Factory instance for name '" + factoryMapping.getName()
                            + "', class name '" + factoryClass + "'", t);
                    return (null);
                }
            }
        }
        return (factoryInstance);

    }

    public void initDigester(Digester digester, String path) {

        String factoryPath = path + "/factories/factory";
        String templatePath = path + "/templates/template";
        String templateParameterPath = templatePath + "/parameter";
        String beanPath = "struts-config/action-mappings/action/bean";
        String parameterValuePath = beanPath + "/parameter-value";

        digester.addObjectCreate(factoryPath, factoryMappingClass, "className");
        digester.addSetProperties(factoryPath);
        digester.addSetNext(factoryPath, "addFactoryMapping", factoryMappingClass);
        //                digester.addSetProperty( factoryPath + , nameProperty, nameValue);

        digester.addObjectCreate(templatePath, templateClass, "className");
        digester.addSetProperties(templatePath);
        digester.addSetNext(templatePath, "addTemplate", templateClass);
        //                digester.addSetProperty( templatePath + setPropertyPath, nameProperty, nameValue);

        digester.addObjectCreate(templateParameterPath, parameterMappingClass, "className");
        digester.addSetProperties(templateParameterPath);
        digester.addSetNext(templateParameterPath, "addParameter", parameterMappingClass);

        // Add bean-creation rules to support auto bean generation

        digester.addObjectCreate(beanPath, registrationClass, "className");
        digester.addSetProperties(beanPath);
        digester.addRule(beanPath, new AddBeanRegistrationRule(digester, this));
        //                digester.addSetProperty( beanRegisterPath + setPropertyPath, nameProperty, nameValue);

        digester.addObjectCreate(parameterValuePath, parameterValueClass, "className");
        digester.addSetProperties(parameterValuePath);
        digester.addSetNext(parameterValuePath, "addParameterValue", parameterValueClass);

    }

}

// ------------------------------------------------------------ Private Classes

/** Rule to process bean registration in action mapping.
 */
final class AddBeanRegistrationRule extends Rule {

    protected FactoryService service = null;

    /** Public constructor to create rule.
     * @param digester Parent digester to accept this rule.
     */
    public AddBeanRegistrationRule(Digester digester, FactoryService service) {

        super(digester);

        this.service = service;

    }

    /** Initial point to start processing.
     * @param attributes Tag attributes.
     * @throws Exception at some kind of trouble in process
     */
    public void begin(Attributes attributes) throws Exception {

        Registration registration = (Registration) digester.peek(0);
        ActionMapping parent = (ActionMapping) digester.peek(1);

        registration.setPath(parent.getPath());
        service.addRegistration(registration);

    }

}