1 /*
2 * $Id$
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 package org.apache.struts.tiles;
23
24 import java.io.IOException;
25 import java.io.Serializable;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28
29 import jakarta.servlet.ServletContext;
30 import jakarta.servlet.ServletException;
31 import jakarta.servlet.ServletRequest;
32 import jakarta.servlet.http.HttpServletRequest;
33 import jakarta.servlet.http.HttpServletResponse;
34 import jakarta.servlet.jsp.PageContext;
35
36 import org.apache.struts.util.RequestUtils;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41 * Default implementation of TilesUtil.
42 * This class contains default implementation of utilities. This implementation
43 * is intended to be used without Struts.
44 */
45 public class TilesUtilImpl implements Serializable {
46 private static final long serialVersionUID = -3811960942326759160L;
47
48 /**
49 * The {@code Log} instances for this class.
50 */
51 private final static Logger LOG =
52 LoggerFactory.getLogger(TilesUtilImpl.class);
53 private transient final Logger log =
54 LoggerFactory.getLogger(TilesUtilImpl.class);
55
56 /** Constant name used to store factory in servlet context */
57 public static final String DEFINITIONS_FACTORY =
58 "org.apache.struts.tiles.DEFINITIONS_FACTORY";
59
60 /**
61 * JSP 2.0 include method to use which supports configurable flushing.
62 */
63 private static Method include = null;
64
65 /**
66 * Initialize the include variable with the
67 * JSP 2.0 method if available.
68 */
69 static {
70
71 try {
72 // get version of include method with flush argument
73 include = PageContext.class.getMethod("include", String.class, boolean.class);
74 } catch (NoSuchMethodException e) {
75 LOG.debug("Could not find JSP 2.0 include method. Using old one that doesn't support " +
76 "configurable flushing.", e);
77 }
78 }
79
80 /**
81 * Do a forward using request dispatcher.
82 *
83 * This method is used by the Tiles package anytime a forward is required.
84 * @param uri Uri or Definition name to forward.
85 * @param request Current page request.
86 * @param servletContext Current servlet context.
87 */
88 public void doForward(
89 String uri,
90 HttpServletRequest request,
91 HttpServletResponse response,
92 ServletContext servletContext)
93 throws IOException, ServletException {
94
95 request.getRequestDispatcher(uri).forward(request, response);
96 }
97
98 /**
99 * Do an include using request dispatcher.
100 *
101 * This method is used by the Tiles package when an include is required.
102 * The Tiles package can use indifferently any form of this method.
103 * @param uri Uri or Definition name to forward.
104 * @param request Current page request.
105 * @param response Current page response.
106 * @param servletContext Current servlet context.
107 */
108 public void doInclude(
109 String uri,
110 HttpServletRequest request,
111 HttpServletResponse response,
112 ServletContext servletContext)
113 throws IOException, ServletException {
114
115 request.getRequestDispatcher(uri).include(request, response);
116 }
117
118 /**
119 * Do an include using PageContext.include().
120 *
121 * This method is used by the Tiles package when an include is required.
122 * The Tiles package can use indifferently any form of this method.
123 * @param uri Uri or Definition name to forward.
124 * @param pageContext Current page context.
125 * @param flush If the writer should be flushed before the include
126 */
127 public void doInclude(String uri, PageContext pageContext, boolean flush)
128 throws IOException, ServletException {
129 try {
130 // perform include with new JSP 2.0 method that supports flushing
131 if (include != null) {
132 include.invoke(pageContext, new Object[]{uri, Boolean.valueOf(flush)});
133 return;
134 }
135 } catch (IllegalAccessException e) {
136 log.debug("Could not find JSP 2.0 include method. Using old one.", e);
137 } catch (InvocationTargetException e) {
138 if (e.getCause() instanceof ServletException){
139 throw ((ServletException)e.getCause());
140 } else if (e.getCause() instanceof IOException){
141 throw ((IOException)e.getCause());
142 } else {
143 throw new ServletException(e);
144 }
145 }
146
147 pageContext.include(uri);
148 }
149
150 /**
151 * Get definition factory from appropriate servlet context.
152 * @return Definitions factory or <code>null</code> if not found.
153 */
154 public DefinitionsFactory getDefinitionsFactory(
155 ServletRequest request,
156 ServletContext servletContext) {
157
158 return (DefinitionsFactory) servletContext.getAttribute(DEFINITIONS_FACTORY);
159 }
160
161 /**
162 * Create Definition factory from specified configuration object.
163 * Create an instance of the factory with the class specified in the config
164 * object. Then, initialize this factory and finally store the factory in
165 * appropriate context by calling
166 * {@link #makeDefinitionsFactoryAccessible(DefinitionsFactory, ServletContext)}.
167 * Factory creation is done by {@link #createDefinitionFactoryInstance(String)}.
168 * <p>
169 *
170 * @param servletContext Servlet Context passed to newly created factory.
171 * @param factoryConfig Configuration object passed to factory.
172 * @return newly created factory of type specified in the config object.
173 * @throws DefinitionsFactoryException If an error occur while initializing factory
174 */
175 public DefinitionsFactory createDefinitionsFactory(
176 ServletContext servletContext,
177 DefinitionsFactoryConfig factoryConfig)
178 throws DefinitionsFactoryException {
179
180 // Create configurable factory
181 DefinitionsFactory factory =
182 createDefinitionFactoryInstance(factoryConfig.getFactoryClassname());
183
184 factory.init(factoryConfig, servletContext);
185
186 // Make factory accessible from jsp tags (push it in appropriate context)
187 makeDefinitionsFactoryAccessible(factory, servletContext);
188 return factory;
189 }
190
191 /**
192 * Create Definition factory of specified classname.
193 * Factory class must extend the {@link DefinitionsFactory} class.
194 * The factory is wrapped appropriately with
195 * {@link org.apache.struts.tiles.definition.ComponentDefinitionsFactoryWrapper}
196 * if it is an instance of the deprecated ComponentDefinitionsFactory class.
197 * @param classname Class name of the factory to create.
198 * @return newly created factory.
199 * @throws DefinitionsFactoryException If an error occur while initializing factory
200 */
201 @SuppressWarnings("deprecation")
202 protected DefinitionsFactory createDefinitionFactoryInstance(String classname)
203 throws DefinitionsFactoryException {
204
205 try {
206 Class<?> factoryClass = RequestUtils.applicationClass(classname);
207 Object factory = factoryClass.getDeclaredConstructor().newInstance();
208
209 // Backward compatibility : if factory classes implements old interface,
210 // provide appropriate wrapper
211 if (factory instanceof ComponentDefinitionsFactory) {
212 factory =
213 new org.apache.struts.tiles.definition.ComponentDefinitionsFactoryWrapper(
214 (ComponentDefinitionsFactory) factory);
215 }
216 return (DefinitionsFactory) factory;
217
218 } catch (ClassCastException ex) { // Bad classname
219 throw new DefinitionsFactoryException(
220 "Error - createDefinitionsFactory : Factory class '"
221 + classname
222 + " must implement 'TilesDefinitionsFactory'.",
223 ex);
224
225 } catch (ClassNotFoundException ex) { // Bad classname
226 throw new DefinitionsFactoryException(
227 "Error - createDefinitionsFactory : Bad class name '"
228 + classname
229 + "'.",
230 ex);
231
232 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
233 | InvocationTargetException | NoSuchMethodException
234 | SecurityException ex) { // Bad constructor or error
235 throw new DefinitionsFactoryException(ex);
236 }
237 }
238
239 /**
240 * Make definition factory accessible to Tags.
241 * Factory is stored in servlet context.
242 * @param factory Factory to be made accessible.
243 * @param servletContext Current servlet context.
244 */
245 protected void makeDefinitionsFactoryAccessible(
246 DefinitionsFactory factory,
247 ServletContext servletContext) {
248
249 servletContext.setAttribute(DEFINITIONS_FACTORY, factory);
250 }
251 }