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 org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.tiles.access.TilesAccess;
24  import org.apache.tiles.factory.TilesContainerFactory;
25  import org.apache.tiles.TilesContainer;
26  import org.apache.tiles.TilesException;
27  import org.apache.myfaces.shared_tomahawk.webapp.webxml.ServletMapping;
28  import org.apache.myfaces.shared_tomahawk.webapp.webxml.WebXml;
29  
30  import javax.faces.application.ViewHandler;
31  import javax.faces.context.ExternalContext;
32  import javax.faces.context.FacesContext;
33  import javax.faces.FacesException;
34  import javax.faces.component.UIViewRoot;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  import javax.servlet.http.HttpSession;
38  import javax.servlet.ServletResponse;
39  import java.io.IOException;
40  import java.util.List;
41  import java.util.Locale;
42  
43  /**
44   * @since 1.1.7
45   * @author Martin Marinschek
46   * @version $Revision: 472792 $ $Date: 2006-11-09 07:34:47 +0100 (Do, 09 Nov 2006) $
47   */
48  public class JspTilesTwoViewHandlerImpl
49          extends ViewHandler {
50      private ViewHandler _viewHandler;
51  
52      private static final Log log = LogFactory.getLog(JspTilesTwoViewHandlerImpl.class);
53      private static final String TILES_DEF_EXT = ".tiles";
54  
55      private String tilesExtension = TILES_DEF_EXT;
56  
57      public JspTilesTwoViewHandlerImpl(ViewHandler viewHandler)
58      {
59          _viewHandler = viewHandler;
60      }
61  
62      private void initContainer(ExternalContext context) {
63  
64          if(TilesAccess.getContainer(context.getContext())==null) {
65              try
66              {
67                  TilesContainerFactory factory = TilesContainerFactory.getFactory(context.getContext());
68                  TilesContainer container = factory.createTilesContainer(context.getContext());
69                  TilesAccess.setContainer(context.getContext(),container);
70              }
71              catch (Exception e)
72              {
73                  throw new FacesException("Error reading tiles definitions : " + e.getMessage(), e);
74              }
75          }
76      }
77  
78      public void renderView(FacesContext facesContext, UIViewRoot viewToRender) throws IOException, FacesException
79      {
80          if (viewToRender == null)
81          {
82              log.fatal("viewToRender must not be null");
83              throw new NullPointerException("viewToRender must not be null");
84          }
85  
86          ExternalContext externalContext = facesContext.getExternalContext();
87  
88          String viewId = deriveViewId(externalContext, viewToRender.getViewId());
89  
90          if(viewId==null) {
91              //deriving view-id made clear we are not responsible for this view-id - call the delegate
92              _viewHandler.renderView(facesContext, viewToRender);
93              return;
94          }
95  
96  
97          initContainer(externalContext);
98  
99          String tilesId = deriveTileFromViewId(viewId);
100 
101         TilesContainer container = TilesAccess.getContainer(externalContext.getContext());
102 
103         Object[] requestObjects = {externalContext.getRequest(), externalContext.getResponse()};
104 
105         if(container.isValidDefinition(tilesId, requestObjects)) {
106 
107             //propagate our new view-id to wherever it makes sense
108             setViewId(viewToRender, viewId, facesContext);
109 
110             renderTilesView(externalContext, requestObjects, container, viewToRender, viewId, tilesId);
111         }
112         else {
113             //we're not using tiles as no valid definition has been found
114             //just call the delegate view-handler to let it do its thing
115             _viewHandler.renderView(facesContext, viewToRender);
116         }
117     }
118 
119     private void renderTilesView(ExternalContext externalContext, Object[] requestObjects, TilesContainer container, UIViewRoot viewToRender, String viewId, String tilesId) {
120         handleCharacterEncoding(viewId, externalContext, viewToRender);
121 
122         container.startContext(requestObjects);
123         try {
124             container.render(tilesId,requestObjects);
125         } catch (TilesException e) {
126             throw new FacesException(e);
127         }
128         finally {
129             container.endContext(requestObjects);
130         }
131 
132         handleCharacterEncodingPostDispatch(externalContext);
133     }
134 
135     private void setViewId(UIViewRoot viewToRender, String viewId, FacesContext facesContext) {
136         viewToRender.setViewId(viewId);
137         if(facesContext.getViewRoot()!=null) {
138             facesContext.getViewRoot().setViewId(viewId);
139         }
140     }
141 
142     private String deriveTileFromViewId(String viewId) {
143         String tilesId = viewId;
144         int idx = tilesId.lastIndexOf('.');
145         if (idx > 0)
146         {
147             tilesId = tilesId.substring(0, idx) + tilesExtension;
148         }
149         else
150         {
151             tilesId = tilesId  + tilesExtension;
152         }
153         return tilesId;
154     }
155 
156     private String deriveViewId(ExternalContext externalContext, String viewId) {
157         ServletMapping servletMapping = getServletMapping(externalContext);
158 
159         String defaultSuffix = externalContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
160         String suffix = defaultSuffix != null ? defaultSuffix : ViewHandler.DEFAULT_SUFFIX;
161         if (servletMapping.isExtensionMapping())
162         {
163             if (!viewId.endsWith(suffix))
164             {
165                 int dot = viewId.lastIndexOf('.');
166                 if (dot == -1)
167                 {
168                     if (log.isTraceEnabled()) log.trace("Current viewId has no extension, appending default suffix " + suffix);
169                     return viewId + suffix;
170                 }
171                 else
172                 {
173                     if (log.isTraceEnabled()) log.trace("Replacing extension of current viewId by suffix " + suffix);
174                     return viewId.substring(0, dot) + suffix;
175                 }
176             }
177 
178             //extension-mapped page ends with proper suffix - all ok
179             return viewId;
180         }
181         else if (!viewId.endsWith(suffix))
182         {
183             // path-mapping used, but suffix is no default-suffix
184             return null;
185         }
186         else {
187             //path-mapping used, suffix is default-suffix - all ok
188             return viewId;
189         }
190     }
191 
192     private void handleCharacterEncodingPostDispatch(ExternalContext externalContext) {
193         // handle character encoding as of section 2.5.2.2 of JSF 1.1
194         if (externalContext.getRequest() instanceof HttpServletRequest) {
195             HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
196             HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
197             HttpSession session = request.getSession(false);
198 
199             if (session != null) {
200                 session.setAttribute(ViewHandler.CHARACTER_ENCODING_KEY, response.getCharacterEncoding());
201             }
202         }
203     }
204 
205     private void handleCharacterEncoding(String viewId, ExternalContext externalContext, UIViewRoot viewToRender) {
206         if (log.isTraceEnabled()) log.trace("Dispatching to " + viewId);
207 
208         // handle character encoding as of section 2.5.2.2 of JSF 1.1
209         if (externalContext.getResponse() instanceof ServletResponse) {
210             ServletResponse response = (ServletResponse) externalContext.getResponse();
211             response.setLocale(viewToRender.getLocale());
212         }
213     }
214 
215     private static ServletMapping getServletMapping(ExternalContext externalContext)
216     {
217         String servletPath = externalContext.getRequestServletPath();
218         String requestPathInfo = externalContext.getRequestPathInfo();
219 
220         WebXml webxml = WebXml.getWebXml(externalContext);
221         List mappings = webxml.getFacesServletMappings();
222 
223         boolean isExtensionMapping = requestPathInfo == null;
224 
225         for (int i = 0, size = mappings.size(); i < size; i++)
226         {
227             ServletMapping servletMapping = (ServletMapping) mappings.get(i);
228             if (servletMapping.isExtensionMapping() == isExtensionMapping)
229             {
230                 String urlpattern = servletMapping.getUrlPattern();
231                 if (isExtensionMapping)
232                 {
233                     String extension = urlpattern.substring(1, urlpattern.length());
234                     if (servletPath.endsWith(extension))
235                     {
236                         return servletMapping;
237                     }
238                 }
239                 else
240                 {
241                     urlpattern = urlpattern.substring(0, urlpattern.length() - 2);
242                     // servletPath starts with "/" except in the case where the
243                     // request is matched with the "/*" pattern, in which case
244                     // it is the empty string (see Servlet Sepc 2.3 SRV4.4)
245                     if (servletPath.equals(urlpattern))
246                     {
247                         return servletMapping;
248                     }
249                 }
250             }
251         }
252         log.error("could not find pathMapping for servletPath = " + servletPath +
253                   " requestPathInfo = " + requestPathInfo);
254         throw new IllegalArgumentException("could not find pathMapping for servletPath = " + servletPath +
255                   " requestPathInfo = " + requestPathInfo);
256     }
257 
258 
259     public Locale calculateLocale(FacesContext context)
260     {
261         return _viewHandler.calculateLocale(context);
262     }
263 
264     public String calculateRenderKitId(FacesContext context)
265     {
266         return _viewHandler.calculateRenderKitId(context);
267     }
268 
269     public UIViewRoot createView(FacesContext context, String viewId)
270     {
271         return _viewHandler.createView(context, viewId);
272     }
273 
274     public String getActionURL(FacesContext context, String viewId)
275     {
276         return _viewHandler.getActionURL(context, viewId);
277     }
278 
279     public String getResourceURL(FacesContext context, String path)
280     {
281         return _viewHandler.getResourceURL(context, path);
282     }
283 
284     public UIViewRoot restoreView(FacesContext context, String viewId)
285     {
286         return _viewHandler.restoreView(context, viewId);
287     }
288 
289     public void writeState(FacesContext context) throws IOException
290     {
291         _viewHandler.writeState(context);
292     }
293 
294 }