View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.tomahawk.application.jsp;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Locale;
24  
25  import javax.faces.FacesException;
26  import javax.faces.application.ViewHandler;
27  import javax.faces.component.UIViewRoot;
28  import javax.faces.context.ExternalContext;
29  import javax.faces.context.FacesContext;
30  import javax.servlet.ServletContext;
31  import javax.servlet.ServletRequest;
32  import javax.servlet.ServletResponse;
33  import javax.servlet.http.HttpServletRequest;
34  import javax.servlet.http.HttpServletResponse;
35  import javax.servlet.http.HttpSession;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.myfaces.shared_tomahawk.webapp.webxml.ServletMapping;
40  import org.apache.myfaces.shared_tomahawk.webapp.webxml.WebXml;
41  import org.apache.struts.tiles.ComponentContext;
42  import org.apache.struts.tiles.ComponentDefinition;
43  import org.apache.struts.tiles.Controller;
44  import org.apache.struts.tiles.DefinitionsFactory;
45  import org.apache.struts.tiles.DefinitionsFactoryConfig;
46  import org.apache.struts.tiles.DefinitionsFactoryException;
47  import org.apache.struts.tiles.TilesUtil;
48  
49  /**
50   * @author Thomas Spiegl
51   * @version $Revision: 472792 $ $Date: 2006-11-09 01:34:47 -0500 (Thu, 09 Nov 2006) $
52   */
53  public class JspTilesViewHandlerImpl
54      extends ViewHandler
55  {
56      private ViewHandler _viewHandler;
57  
58      private static final Log log = LogFactory.getLog(JspTilesViewHandlerImpl.class);
59      private static final String TILES_DEF_EXT = ".tiles";
60      private static final String TILES_DEF_ATTR = "tiles-definitions";
61      private static final String TILES_EXT_ATTR = "tiles-extension";
62  
63      private String tilesExtension = TILES_DEF_EXT;
64      private DefinitionsFactory _definitionsFactory;
65  
66      public JspTilesViewHandlerImpl(ViewHandler viewHandler)
67      {
68          _viewHandler = viewHandler;
69      }
70  
71      private DefinitionsFactory getDefinitionsFactory()
72      {
73          if (_definitionsFactory == null)
74          {
75              if (log.isDebugEnabled()) log.debug("JspTilesViewHandlerImpl init");
76              ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
77              String tilesDefinitions = context.getInitParameter(TILES_DEF_ATTR);
78              if (tilesDefinitions == null)
79              {
80                  log.fatal("No Tiles definition found. Specify Definition files by adding "
81                            + TILES_DEF_ATTR + " param in your web.xml");
82                  return null;
83              }
84  
85              String tilezExtension = context.getInitParameter(TILES_EXT_ATTR);
86              if (tilezExtension != null)
87              {
88                  tilesExtension = tilezExtension;
89                  if (log.isDebugEnabled())
90                  {
91                  log.debug("Using non-default tiles extension of: " + tilesExtension);
92                  }
93              } 
94  
95              DefinitionsFactoryConfig factoryConfig = new DefinitionsFactoryConfig();
96              factoryConfig.setDefinitionConfigFiles(tilesDefinitions);
97              try
98              {
99                  if (log.isDebugEnabled()) log.debug("Reading tiles definitions");
100                 _definitionsFactory = TilesUtil.createDefinitionsFactory((ServletContext)context.getContext(),
101                                                                         factoryConfig);
102             }
103             catch (DefinitionsFactoryException e)
104             {
105                 log.fatal("Error reading tiles definitions", e);
106                 return null;
107             }
108         }
109         return _definitionsFactory;
110     }
111 
112     public void renderView(FacesContext facesContext, UIViewRoot viewToRender) throws IOException, FacesException
113     {
114         if (viewToRender == null)
115         {
116             log.fatal("viewToRender must not be null");
117             throw new NullPointerException("viewToRender must not be null");
118         }
119 
120         ExternalContext externalContext = facesContext.getExternalContext();
121 
122         String viewId = facesContext.getViewRoot().getViewId();
123         ServletMapping servletMapping = getServletMapping(externalContext);
124 
125         String defaultSuffix = externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
126         String suffix = defaultSuffix != null ? defaultSuffix : ViewHandler.DEFAULT_SUFFIX;
127         if (servletMapping.isExtensionMapping())
128         {
129             if (!viewId.endsWith(suffix))
130             {
131                 int dot = viewId.lastIndexOf('.');
132                 if (dot == -1)
133                 {
134                     if (log.isTraceEnabled()) log.trace("Current viewId has no extension, appending default suffix " + suffix);
135                     viewId = viewId + suffix;
136                 }
137                 else
138                 {
139                     if (log.isTraceEnabled()) log.trace("Replacing extension of current viewId by suffix " + suffix);
140                     viewId = viewId.substring(0, dot) + suffix;
141                 }
142                 facesContext.getViewRoot().setViewId(viewId);
143             }
144         }
145         else if (!viewId.endsWith(suffix))
146         {
147             // path-mapping used, but suffix is no default-suffix
148             dispatch(externalContext, viewToRender, viewId);
149             return;
150         }
151 
152         String tilesId = viewId;
153         int idx = tilesId.lastIndexOf('.');
154         if (idx > 0)
155         {
156             tilesId = tilesId.substring(0, idx) + tilesExtension;
157         }
158         else
159         {
160             tilesId = tilesId  + tilesExtension;
161         }
162         ServletRequest request = (ServletRequest)externalContext.getRequest();
163         ServletContext servletContext = (ServletContext)externalContext.getContext();
164 
165         ComponentDefinition definition = null;
166         try
167         {
168             definition = getDefinitionsFactory().getDefinition(tilesId, request, servletContext);
169             
170             if (definition == null)
171             {
172                 /**
173                  * Check for the definition without the leading '/' character.  Allows user to specify Tiles definitions without a 
174                  * leading '/' char.
175                  */
176                 int slashIndex = tilesId.indexOf("/");
177                 if (slashIndex == 0)
178                 {
179                     tilesId = tilesId.substring(1);
180                     definition = getDefinitionsFactory().getDefinition(tilesId, request, servletContext);
181                 }
182             }
183             
184             if (definition != null)
185             {
186                 // if tiles-definition could be found set ComponentContext & viewId
187                 ComponentContext tileContext = ComponentContext.getContext(request);
188                 if (tileContext == null)
189                 {
190                     tileContext = new ComponentContext(definition.getAttributes());
191                     ComponentContext.setContext(tileContext, request);
192                 }
193                 else
194                 {
195                     tileContext.addMissing(definition.getAttributes());
196                 }
197                 viewId = definition.getPage();
198                 // if a controller is defined for this tile, execute it
199                 Controller tilesController = definition.getOrCreateController();
200                 if (tilesController != null) {
201                     ServletResponse response = (ServletResponse) externalContext.getResponse();
202                     if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
203                         try {
204                             tilesController.execute(tileContext, (HttpServletRequest) request,
205                                     (HttpServletResponse) response, servletContext);
206                         } catch (Exception e) {
207                             throw new FacesException(e);
208                         }
209                     } // else not executing controller for non-HTTP request/response (is this right??)
210                 }
211             }
212         }
213         catch (DefinitionsFactoryException e)
214         {
215             throw new FacesException(e);
216         }
217         catch (InstantiationException e)
218         {
219             throw new FacesException(e);
220         }
221 
222         dispatch(externalContext, viewToRender, viewId);
223     }
224 
225     private void dispatch(ExternalContext externalContext, UIViewRoot viewToRender, String viewId)
226         throws IOException
227     {
228         if (log.isTraceEnabled()) log.trace("Dispatching to " + viewId);
229 
230         // handle character encoding as of section 2.5.2.2 of JSF 1.1
231         if (externalContext.getResponse() instanceof ServletResponse) {
232             ServletResponse response = (ServletResponse) externalContext.getResponse();
233             response.setLocale(viewToRender.getLocale());
234         }
235 
236         externalContext.dispatch(viewId);
237 
238         // handle character encoding as of section 2.5.2.2 of JSF 1.1
239         if (externalContext.getRequest() instanceof HttpServletRequest) {
240             HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
241             HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
242             HttpSession session = request.getSession(false);
243 
244             if (session != null) {
245                 session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY, response.getCharacterEncoding());
246             }
247         }
248 
249 
250     }
251 
252     private static ServletMapping getServletMapping(ExternalContext externalContext)
253     {
254         String servletPath = externalContext.getRequestServletPath();
255         String requestPathInfo = externalContext.getRequestPathInfo();
256 
257         WebXml webxml = WebXml.getWebXml(externalContext);
258         List mappings = webxml.getFacesServletMappings();
259 
260         boolean isExtensionMapping = requestPathInfo == null;
261 
262         for (int i = 0, size = mappings.size(); i < size; i++)
263         {
264             ServletMapping servletMapping = (ServletMapping) mappings.get(i);
265             if (servletMapping.isExtensionMapping() == isExtensionMapping)
266             {
267                 String urlpattern = servletMapping.getUrlPattern();
268                 if (isExtensionMapping)
269                 {
270                     String extension = urlpattern.substring(1, urlpattern.length());
271                     if (servletPath.endsWith(extension))
272                     {
273                         return servletMapping;
274                     }
275                 }
276                 else
277                 {
278                     urlpattern = urlpattern.substring(0, urlpattern.length() - 2);
279                     // servletPath starts with "/" except in the case where the
280                     // request is matched with the "/*" pattern, in which case
281                     // it is the empty string (see Servlet Sepc 2.3 SRV4.4)
282                     if (servletPath.equals(urlpattern))
283                     {
284                         return servletMapping;
285                     }
286                 }
287             }
288         }
289         log.error("could not find pathMapping for servletPath = " + servletPath +
290                   " requestPathInfo = " + requestPathInfo);
291         throw new IllegalArgumentException("could not find pathMapping for servletPath = " + servletPath +
292                   " requestPathInfo = " + requestPathInfo);
293     }
294 
295 
296     public Locale calculateLocale(FacesContext context)
297     {
298         return _viewHandler.calculateLocale(context);
299     }
300 
301     public String calculateRenderKitId(FacesContext context)
302     {
303         return _viewHandler.calculateRenderKitId(context);
304     }
305 
306     public UIViewRoot createView(FacesContext context, String viewId)
307     {
308         return _viewHandler.createView(context, viewId);
309     }
310 
311     public String getActionURL(FacesContext context, String viewId)
312     {
313         return _viewHandler.getActionURL(context, viewId);
314     }
315 
316     public String getResourceURL(FacesContext context, String path)
317     {
318         return _viewHandler.getResourceURL(context, path);
319     }
320 
321     public UIViewRoot restoreView(FacesContext context, String viewId)
322     {
323         return _viewHandler.restoreView(context, viewId);
324     }
325 
326     public void writeState(FacesContext context) throws IOException
327     {
328         _viewHandler.writeState(context);
329     }
330 
331 }