001    package org.apache.turbine.services.jsp;
002    
003    
004    /*
005     * Licensed to the Apache Software Foundation (ASF) under one
006     * or more contributor license agreements.  See the NOTICE file
007     * distributed with this work for additional information
008     * regarding copyright ownership.  The ASF licenses this file
009     * to you under the Apache License, Version 2.0 (the
010     * "License"); you may not use this file except in compliance
011     * with the License.  You may obtain a copy of the License at
012     *
013     *   http://www.apache.org/licenses/LICENSE-2.0
014     *
015     * Unless required by applicable law or agreed to in writing,
016     * software distributed under the License is distributed on an
017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018     * KIND, either express or implied.  See the License for the
019     * specific language governing permissions and limitations
020     * under the License.
021     */
022    
023    
024    import java.io.File;
025    import java.io.IOException;
026    
027    import javax.servlet.RequestDispatcher;
028    import javax.servlet.ServletConfig;
029    import javax.servlet.http.HttpServletRequest;
030    
031    import org.apache.commons.configuration.Configuration;
032    import org.apache.commons.lang.StringUtils;
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import org.apache.turbine.Turbine;
036    import org.apache.turbine.services.InitializationException;
037    import org.apache.turbine.services.pull.ApplicationTool;
038    import org.apache.turbine.services.pull.tools.TemplateLink;
039    import org.apache.turbine.services.template.BaseTemplateEngineService;
040    import org.apache.turbine.util.RunData;
041    import org.apache.turbine.util.TurbineException;
042    
043    /**
044     * This is a Service that can process JSP templates from within a Turbine
045     * screen.
046     *
047     * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
048     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
049     * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
050     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
051     */
052    public class TurbineJspService
053            extends BaseTemplateEngineService
054            implements JspService
055    {
056        /** The base path[s] prepended to filenames given in arguments */
057        private String[] templatePaths;
058    
059        /** The relative path[s] prepended to filenames */
060        private String[] relativeTemplatePaths;
061    
062        /** The buffer size for the output stream. */
063        private int bufferSize;
064    
065        /** Logging */
066        private static Log log = LogFactory.getLog(TurbineJspService.class);
067    
068        /**
069         * Load all configured components and initialize them. This is
070         * a zero parameter variant which queries the Turbine Servlet
071         * for its config.
072         *
073         * @throws InitializationException Something went wrong in the init
074         *         stage
075         */
076        @Override
077        public void init()
078            throws InitializationException
079        {
080            try
081            {
082                initJsp();
083                registerConfiguration(JspService.JSP_EXTENSION);
084                setInit(true);
085            }
086            catch (Exception e)
087            {
088                throw new InitializationException(
089                    "TurbineJspService failed to initialize", e);
090            }
091        }
092    
093        /**
094         * Performs early initialization of this Turbine service.
095         *
096         * @param config The ServletConfiguration from Turbine
097         *
098         * @throws InitializationException Something went wrong when starting up.
099         * @deprecated use init() instead.
100         */
101        @Deprecated
102        public void init(ServletConfig config)
103            throws InitializationException
104        {
105            init();
106        }
107    
108        /**
109         * Adds some convenience objects to the request.  For example an instance
110         * of TemplateLink which can be used to generate links to other templates.
111         *
112         * @param data the turbine rundata object
113         */
114        public void addDefaultObjects(RunData data)
115        {
116            HttpServletRequest req = data.getRequest();
117    
118            //
119            // This is a place where an Application Pull Tool is used
120            // in a regular Java Context. We have no Pull Service with the
121            // Jsp Paging stuff, but we can run our Application Tool by Hand:
122            //
123            ApplicationTool templateLink = new TemplateLink();
124            templateLink.init(data);
125    
126            req.setAttribute(LINK, templateLink);
127            req.setAttribute(RUNDATA, data);
128        }
129    
130        /**
131         * Returns the default buffer size of the JspService
132         *
133         * @return The default buffer size.
134         */
135        public int getDefaultBufferSize()
136        {
137            return bufferSize;
138        }
139    
140        /**
141         * executes the JSP given by templateName.
142         *
143         * @param data A RunData Object
144         * @param templateName the filename of the template.
145         * @throws TurbineException Any exception thrown while processing will be
146         *         wrapped into a TurbineException and rethrown.
147         */
148        public void handleRequest(RunData data, String templateName)
149            throws TurbineException
150        {
151            handleRequest(data, templateName, false);
152        }
153    
154        /**
155         * executes the JSP given by templateName.
156         *
157         * @param data A RunData Object
158         * @param templateName the filename of the template.
159         * @param isForward whether to perform a forward or include.
160         * @throws TurbineException Any exception trown while processing will be
161         *         wrapped into a TurbineException and rethrown.
162         */
163        public void handleRequest(RunData data, String templateName, boolean isForward)
164            throws TurbineException
165        {
166            /** template name with relative path */
167            String relativeTemplateName = getRelativeTemplateName(templateName);
168    
169            if (StringUtils.isEmpty(relativeTemplateName))
170            {
171                throw new TurbineException(
172                    "Template " + templateName + " not found in template paths");
173            }
174    
175            // get the RequestDispatcher for the JSP
176            RequestDispatcher dispatcher = data.getServletContext()
177                .getRequestDispatcher(relativeTemplateName);
178    
179            try
180            {
181                if (isForward)
182                {
183                    // forward the request to the JSP
184                    dispatcher.forward(data.getRequest(), data.getResponse());
185                }
186                else
187                {
188                    data.getResponse().getWriter().flush();
189                    // include the JSP
190                    dispatcher.include(data.getRequest(), data.getResponse());
191                }
192            }
193            catch (Exception e)
194            {
195                // as JSP service is in Alpha stage, let's try hard to send the
196                // error message to the browser, to speed up debugging
197                try
198                {
199                    data.getResponse().getWriter().print("Error encountered processing a template: "
200                        + templateName);
201                    e.printStackTrace(data.getResponse().getWriter());
202                }
203                catch (IOException ignored)
204                {
205                    // ignore
206                }
207    
208                // pass the exception to the caller according to the general
209                // contract for tamplating services in Turbine
210                throw new TurbineException(
211                    "Error encountered processing a template: " + templateName, e);
212            }
213        }
214    
215        /**
216         * This method sets up the template cache.
217         */
218        private void initJsp()
219            throws Exception
220        {
221            Configuration config = getConfiguration();
222    
223            // Set relative paths from config.
224            // Needed for javax.servlet.RequestDispatcher
225            relativeTemplatePaths = config.getStringArray(TEMPLATE_PATH_KEY);
226    
227            // Use Turbine Servlet to translate the template paths.
228            templatePaths = new String [relativeTemplatePaths.length];
229            for (int i=0; i < relativeTemplatePaths.length; i++)
230            {
231                relativeTemplatePaths[i] = warnAbsolute(relativeTemplatePaths[i]);
232    
233                templatePaths[i] = Turbine.getRealPath(relativeTemplatePaths[i]);
234            }
235    
236            bufferSize = config.getInt(JspService.BUFFER_SIZE_KEY,
237                JspService.BUFFER_SIZE_DEFAULT);
238        }
239    
240        /**
241         * Determine whether a given template is available on the
242         * configured template pathes.
243         *
244         * @param template The name of the requested Template
245         * @return True if the template is available.
246         */
247        @Override
248        public boolean templateExists(String template)
249        {
250            for (int i = 0; i < templatePaths.length; i++)
251            {
252                if (templateExists(templatePaths[i], template))
253                {
254                    return true;
255                }
256            }
257            return false;
258        }
259    
260        /**
261         * Determine whether a given template exists on the supplied
262         * template path. This service ATM only supports file based
263         * templates so it simply checks for file existence.
264         *
265         * @param path The absolute (file system) template path
266         * @param template The name of the requested Template
267         * @return True if the template is available.
268         */
269        private boolean templateExists(String path, String template)
270        {
271            return new File(path, template).exists();
272        }
273    
274        /**
275         * Searchs for a template in the default.template path[s] and
276         * returns the template name with a relative path which is
277         * required by <a href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletContext.html#getRequestDispatcher(java.lang.String)">
278         * javax.servlet.RequestDispatcher</a>
279         *
280         * @param template
281         * @return String
282         */
283        public String getRelativeTemplateName(String template)
284        {
285            template = warnAbsolute(template);
286    
287            // Find which template path the template is in
288            // We have a 1:1 match between relative and absolute
289            // pathes so we can use the index for translation.
290            for (int i = 0; i < templatePaths.length; i++)
291            {
292                if (templateExists(templatePaths[i], template))
293                {
294                    return relativeTemplatePaths[i] + "/" + template;
295                }
296            }
297            return null;
298        }
299    
300        /**
301         * Warn if a template name or path starts with "/".
302         *
303         * @param template The template to test
304         * @return The template name with a leading / stripped off
305         */
306        private String warnAbsolute(String template)
307        {
308            if (template.startsWith("/"))
309            {
310                log.warn("Template " + template
311                    + " has a leading /, which is wrong!");
312                return template.substring(1);
313            }
314            return template;
315        }
316    }