001package 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 024import java.io.File; 025import java.io.IOException; 026 027import javax.servlet.RequestDispatcher; 028import javax.servlet.http.HttpServletRequest; 029 030import org.apache.commons.configuration2.Configuration; 031import org.apache.commons.lang3.StringUtils; 032import org.apache.logging.log4j.LogManager; 033import org.apache.logging.log4j.Logger; 034import org.apache.turbine.Turbine; 035import org.apache.turbine.pipeline.PipelineData; 036import org.apache.turbine.services.InitializationException; 037import org.apache.turbine.services.pull.ApplicationTool; 038import org.apache.turbine.services.pull.tools.TemplateLink; 039import org.apache.turbine.services.template.BaseTemplateEngineService; 040import org.apache.turbine.util.RunData; 041import 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 */ 052public 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 Logger log = LogManager.getLogger(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 * Adds some convenience objects to the request. For example an instance 095 * of TemplateLink which can be used to generate links to other templates. 096 * 097 * @param pipelineData the Turbine PipelineData object 098 */ 099 @Override 100 public void addDefaultObjects(PipelineData pipelineData) 101 { 102 HttpServletRequest req = pipelineData.get(Turbine.class, HttpServletRequest.class); 103 104 // 105 // This is a place where an Application Pull Tool is used 106 // in a regular Java Context. We have no Pull Service with the 107 // Jsp Paging stuff, but we can run our Application Tool by Hand: 108 // 109 ApplicationTool templateLink = new TemplateLink(); 110 templateLink.init(pipelineData); 111 112 req.setAttribute(LINK, templateLink); 113 req.setAttribute(PIPELINE_DATA, pipelineData); 114 } 115 116 /** 117 * Returns the default buffer size of the JspService 118 * 119 * @return The default buffer size. 120 */ 121 @Override 122 public int getDefaultBufferSize() 123 { 124 return bufferSize; 125 } 126 127 /** 128 * executes the JSP given by templateName. 129 * 130 * @param pipelineData A PipelineData Object 131 * @param templateName The template to execute 132 * @param isForward whether to perform a forward or include. 133 * 134 * @throws TurbineException If a problem occurred while executing the JSP 135 */ 136 @Override 137 public void handleRequest(PipelineData pipelineData, String templateName, boolean isForward) 138 throws TurbineException 139 { 140 if(!(pipelineData instanceof RunData)) 141 { 142 throw new RuntimeException("Can't cast to rundata from pipeline data."); 143 } 144 145 RunData data = (RunData)pipelineData; 146 147 /** template name with relative path */ 148 String relativeTemplateName = getRelativeTemplateName(templateName); 149 150 if (StringUtils.isEmpty(relativeTemplateName)) 151 { 152 throw new TurbineException( 153 "Template " + templateName + " not found in template paths"); 154 } 155 156 // get the RequestDispatcher for the JSP 157 RequestDispatcher dispatcher = data.getServletContext() 158 .getRequestDispatcher(relativeTemplateName); 159 160 try 161 { 162 if (isForward) 163 { 164 // forward the request to the JSP 165 dispatcher.forward(data.getRequest(), data.getResponse()); 166 } 167 else 168 { 169 data.getResponse().getWriter().flush(); 170 // include the JSP 171 dispatcher.include(data.getRequest(), data.getResponse()); 172 } 173 } 174 catch (Exception e) 175 { 176 // Let's try hard to send the error message to the browser, to speed up debugging 177 try 178 { 179 data.getResponse().getWriter().print("Error encountered processing a template: " 180 + templateName); 181 e.printStackTrace(data.getResponse().getWriter()); 182 } 183 catch (IOException ignored) 184 { 185 // ignore 186 } 187 188 // pass the exception to the caller according to the general 189 // contract for templating services in Turbine 190 throw new TurbineException( 191 "Error encountered processing a template: " + templateName, e); 192 } 193 } 194 195 /** 196 * executes the JSP given by templateName. 197 * 198 * @param pipelineData A PipelineData Object 199 * @param templateName The template to execute 200 * 201 * @throws TurbineException If a problem occurred while executing the JSP 202 */ 203 @Override 204 public void handleRequest(PipelineData pipelineData, String templateName) 205 throws TurbineException 206 { 207 handleRequest(pipelineData, templateName, false); 208 } 209 210 /** 211 * This method sets up the template cache. 212 */ 213 private void initJsp() 214 throws Exception 215 { 216 Configuration config = getConfiguration(); 217 218 // Set relative paths from config. 219 // Needed for javax.servlet.RequestDispatcher 220 relativeTemplatePaths = config.getStringArray(TEMPLATE_PATH_KEY); 221 222 // Use Turbine Servlet to translate the template paths. 223 templatePaths = new String [relativeTemplatePaths.length]; 224 for (int i=0; i < relativeTemplatePaths.length; i++) 225 { 226 relativeTemplatePaths[i] = warnAbsolute(relativeTemplatePaths[i]); 227 228 templatePaths[i] = Turbine.getRealPath(relativeTemplatePaths[i]); 229 } 230 231 bufferSize = config.getInt(JspService.BUFFER_SIZE_KEY, 232 JspService.BUFFER_SIZE_DEFAULT); 233 } 234 235 /** 236 * Determine whether a given template is available on the 237 * configured template pathes. 238 * 239 * @param template The name of the requested Template 240 * @return True if the template is available. 241 */ 242 @Override 243 public boolean templateExists(String template) 244 { 245 for (String templatePath : templatePaths) 246 { 247 if (templateExists(templatePath, template)) 248 { 249 return true; 250 } 251 } 252 return false; 253 } 254 255 /** 256 * Determine whether a given template exists on the supplied 257 * template path. This service ATM only supports file based 258 * templates so it simply checks for file existence. 259 * 260 * @param path The absolute (file system) template path 261 * @param template The name of the requested Template 262 * @return True if the template is available. 263 */ 264 private boolean templateExists(String path, String template) 265 { 266 return new File(path, template).exists(); 267 } 268 269 /** 270 * Searches for a template in the default.template path[s] and 271 * returns the template name with a relative path which is 272 * required by <a href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletContext.html#getRequestDispatcher(java.lang.String)"> 273 * javax.servlet.RequestDispatcher</a> 274 * 275 * @param template the name of the template 276 * @return String 277 */ 278 @Override 279 public String getRelativeTemplateName(String template) 280 { 281 String relativeTemplate = warnAbsolute(template); 282 283 // Find which template path the template is in 284 // We have a 1:1 match between relative and absolute 285 // pathes so we can use the index for translation. 286 for (int i = 0; i < templatePaths.length; i++) 287 { 288 if (templateExists(templatePaths[i], relativeTemplate)) 289 { 290 return relativeTemplatePaths[i] + "/" + relativeTemplate; 291 } 292 } 293 return null; 294 } 295 296 /** 297 * Warn if a template name or path starts with "/". 298 * 299 * @param template The template to test 300 * @return The template name with a leading / stripped off 301 */ 302 private String warnAbsolute(String template) 303 { 304 if (template.startsWith("/")) 305 { 306 log.warn("Template {} has a leading /, which is wrong!", template); 307 return template.substring(1); 308 } 309 return template; 310 } 311}