001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.chain.web.javax.servlet;
018
019import java.io.IOException;
020
021import javax.servlet.ServletException;
022import javax.servlet.http.HttpServletRequest;
023import javax.servlet.http.HttpServletResponse;
024
025import org.apache.commons.chain.Catalog;
026import org.apache.commons.chain.CatalogFactory;
027import org.apache.commons.chain.Command;
028import org.apache.commons.chain.web.javax.ChainServlet;
029
030/**
031 * Custom subclass of {@link ChainServlet} that also dispatches incoming
032 * requests to a configurable {@link Command} loaded from the specified
033 * {@link Catalog}.
034 *
035 * <p>In addition to the <em>servlet</em> init parameters supported by
036 * {@link ChainServlet}, this class supports the following additional
037 * parameters:</p>
038 * <ul>
039 * <li><strong>org.apache.commons.chain.CATALOG</strong> - Name of the
040 *     catalog from which to acquire commands to be executed. If not
041 *     specified, the default catalog for this application will be used.</li>
042 * <li><strong>org.apache.commons.chain.COMMAND</strong> - Name of the
043 *     {@link Command} (looked up in our configured {@link Catalog} used
044 *     to process all incoming servlet requests. If not specified,
045 *     defaults to {@code command}.</li>
046 * </ul>
047 *
048 * <p>Also, the {@code org.apache.commons.chain.CONFIG_ATTR}
049 * init parameter is also used to identify the
050 * {@link org.apache.commons.chain.Context} attribute under
051 * which our configured {@link Catalog} will be made available to
052 * {@link Command}s processing our requests, in addition to its definition
053 * of the {@code ServletContext} attribute key under which the
054 * {@link Catalog} is available.</p>
055 */
056
057public class ChainProcessor extends ChainServlet {
058    private static final long serialVersionUID = -6817532768031279260L;
059
060    // ------------------------------------------------------ Manifest Constants
061
062    /**
063     * The name of the servlet init parameter containing the name of the
064     * {@link Catalog} to use for processing incoming requests.
065     */
066    public static final String CATALOG =
067        "org.apache.commons.chain.CATALOG";
068
069    /**
070     * The default request attribute under which we expose the
071     * {@link Catalog} being used to subordinate {@link Command}s.
072     */
073    public static final String CATALOG_DEFAULT =
074        "org.apache.commons.chain.CATALOG";
075
076    /**
077     * The name of the servlet init parameter containing the name of the
078     * {@link Command} (loaded from our configured {@link Catalog} to use
079     * for processing each incoming request.
080     */
081    public static final String COMMAND =
082        "org.apache.commons.chain.COMMAND";
083
084    /**
085     * The default command name.
086     */
087    private static final String COMMAND_DEFAULT = "command";
088
089    // ------------------------------------------------------ Instance Variables
090
091    /**
092     * The name of the context attribute under which our {@link Catalog}
093     * is stored. This value is also used as the name of the context
094     * attribute under which the catalog is exposed to commands. If not
095     * specified, we will look up commands in the appropriate
096     * {@link Catalog} retrieved from our {@link CatalogFactory}
097     */
098    private String attribute = null;
099
100    /**
101     * The name of the {@link Catalog} to retrieve from the
102     * {@link CatalogFactory} for this application, or {@code null}
103     * to select the default {@link Catalog}.
104     */
105    private String catalog = null;
106
107    /**
108     * The name of the {@link Command} to be executed for each incoming
109     * request.
110     */
111    private String command = null;
112
113    // ------------------------------------------------------------ Constructors
114
115    /**
116     * The Default-Constructor for this class.
117     */
118    public ChainProcessor() {
119    }
120
121    // --------------------------------------------------------- Servlet Methods
122
123    /**
124     * Clean up as this application is shut down.
125     */
126    @Override
127    public void destroy() {
128        super.destroy();
129        attribute = null;
130        catalog = null;
131        command = null;
132    }
133
134    /**
135     * Cache the name of the command we should execute for each request.
136     *
137     * @throws ServletException if an initialization error occurs
138     */
139    @Override
140    public void init() throws ServletException {
141        super.init();
142        attribute = getServletConfig().getInitParameter(CONFIG_ATTR);
143        catalog = getServletConfig().getInitParameter(CATALOG);
144        command = getServletConfig().getInitParameter(COMMAND);
145        if (command == null) {
146            command = COMMAND_DEFAULT;
147        }
148    }
149
150    /**
151     * Configure a {@link ServletWebContext} for the current request, and
152     * pass it to the {@code execute()} method of the specified
153     * {@link Command}, loaded from our configured {@link Catalog}.
154     *
155     * @param request The request we are processing
156     * @param response The response we are creating
157     *
158     * @throws IOException if an input/output error occurs
159     * @throws ServletException if a servlet exception occurs
160     */
161    @Override
162    public void service(HttpServletRequest request,
163                        HttpServletResponse response)
164        throws IOException, ServletException {
165
166        ServletWebContext context =
167            new ServletWebContext(getServletContext(), request, response);
168        Catalog<ServletWebContext> theCatalog = null;
169        if (attribute != null) {
170            @SuppressWarnings("unchecked")
171            Catalog<ServletWebContext> catalog = (Catalog<ServletWebContext>)
172                    getServletContext().getAttribute(this.attribute);
173            theCatalog = catalog;
174        } else if (catalog != null) {
175            theCatalog = CatalogFactory.<ServletWebContext>getInstance().getCatalog(catalog);
176        } else {
177            theCatalog = CatalogFactory.<ServletWebContext>getInstance().getCatalog();
178        }
179        if (attribute == null) {
180            request.setAttribute(CATALOG_DEFAULT, theCatalog);
181        }
182        Command<ServletWebContext> command = theCatalog.getCommand(this.command);
183        try {
184            command.execute(context);
185        } catch (Exception e) {
186            throw new ServletException(e);
187        }
188    }
189}