View Javadoc
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 }