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.view.jsp;
20  
21  import java.io.IOException;
22  import java.util.Locale;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  
26  import javax.faces.FacesException;
27  import javax.faces.component.UIViewRoot;
28  import javax.faces.context.ExternalContext;
29  import javax.faces.context.FacesContext;
30  import javax.faces.event.PostAddToViewEvent;
31  import javax.faces.render.ResponseStateManager;
32  import javax.servlet.ServletRequest;
33  import javax.servlet.ServletResponse;
34  import javax.servlet.http.HttpServletResponse;
35  import javax.servlet.jsp.jstl.core.Config;
36  
37  import org.apache.myfaces.application.jsp.ServletViewResponseWrapper;
38  import org.apache.myfaces.application.viewstate.StateCacheUtils;
39  import org.apache.myfaces.shared.view.JspViewDeclarationLanguageBase;
40  import org.apache.myfaces.view.facelets.tag.composite.CompositeLibrary;
41  import org.apache.myfaces.view.facelets.tag.jsf.core.CoreLibrary;
42  import org.apache.myfaces.view.facelets.tag.jsf.html.HtmlLibrary;
43  import org.apache.myfaces.view.facelets.tag.ui.UILibrary;
44  
45  /**
46   * @author Simon Lessard (latest modification by $Author$)
47   * @version $Revision$ $Date$
48   * 
49   * @since 2.0
50   */
51  public class JspViewDeclarationLanguage extends JspViewDeclarationLanguageBase
52  {
53      //private static final Log log = LogFactory.getLog(JspViewDeclarationLanguage.class);
54      public static final Logger log = Logger.getLogger(JspViewDeclarationLanguage.class.getName());
55      
56      /**
57       * Tags that are only available on facelets and not on JSP.
58       * If a user uses one of these tags on a JSP, we will provide
59       * a more informative error message than the standard one.
60       */
61      public static final String[] FACELETS_ONLY_F_TAGS = {"ajax", "event", "metadata"};
62      public static final String[] FACELETS_ONLY_H_TAGS = {"outputScript", "outputStylesheet",
63                                                           "head", "body", "button", "link"};
64      
65      /**
66       * 
67       */
68      public JspViewDeclarationLanguage()
69      {
70          if (log.isLoggable(Level.FINEST))
71          {
72              log.finest("New JspViewDeclarationLanguage instance created");
73          }
74      }
75  
76      /**
77       * {@inheritDoc}
78       */
79      @Override
80      public void buildView(FacesContext context, UIViewRoot view) throws IOException
81      {
82          // call buildView() from JspViewDeclarationLanguageBase to do some startup work
83          super.buildView(context, view);
84          
85          ExternalContext externalContext = context.getExternalContext();
86          ServletResponse response = (ServletResponse) externalContext.getResponse();
87          ServletRequest request = (ServletRequest) externalContext.getRequest();
88          
89          Locale locale = view.getLocale();
90          response.setLocale(locale);
91          Config.set(request, Config.FMT_LOCALE, context.getViewRoot().getLocale());
92  
93          String viewId = view.getViewId();
94          ServletViewResponseWrapper wrappedResponse = new ServletViewResponseWrapper((HttpServletResponse) response);
95  
96          externalContext.setResponse(wrappedResponse);
97          try
98          {
99              externalContext.dispatch(viewId);
100         }
101         catch (FacesException e)
102         {
103             // try to extract the most likely exceptions here
104             // and provide a better error message for them
105             
106             String message = e.getMessage(); 
107             
108             // errors related to using facelets-only tags on a JSP page
109             if (message != null)
110             {
111                 // does the message contain "f" (prefix f of tags)
112                 // or the related uri http://java.sun.com/jsf/core
113                 if (message.contains("\"f\"") 
114                         || message.contains("\"" + CoreLibrary.NAMESPACE + "\""))
115                 {
116                     // check facelets-only f tags
117                     for (String tag : FACELETS_ONLY_F_TAGS)
118                     {
119                         if (message.contains("\"" + tag + "\""))
120                         {
121                             String exceptionMessage = "The tag f:" + tag + 
122                                     " is only available on facelets.";
123                             throw new FacesException(exceptionMessage, 
124                                     new FaceletsOnlyException(exceptionMessage, e.getCause()));
125                         }
126                     }
127                 }  
128                 else if (message.contains("\"h\"") 
129                         || message.contains("\"" + HtmlLibrary.NAMESPACE + "\""))
130                 {
131                     // check facelets-only h tags
132                     for (String tag : FACELETS_ONLY_H_TAGS)
133                     {
134                         if (message.contains("\"" + tag + "\""))
135                         {
136                             String exceptionMessage = "The tag h:" + tag + 
137                                     " is only available on facelets.";
138                             throw new FacesException(exceptionMessage, 
139                                     new FaceletsOnlyException(exceptionMessage, e.getCause()));
140                         }
141                     }
142                 }
143                 else 
144                 {
145                     // check facelets-only namespaces
146                     String namespace = null;
147                     if (message.contains(UILibrary.NAMESPACE))
148                     {
149                         namespace = UILibrary.NAMESPACE;
150                     }
151                     else if (message.contains(CompositeLibrary.NAMESPACE))
152                     {
153                         namespace = CompositeLibrary.NAMESPACE;
154                     }
155                     
156                     if (namespace != null)
157                     {
158                         // the message contains a facelets-only namespace
159                         String exceptionMessage = "All tags with namespace " +
160                                 namespace + " are only available on facelets.";
161                         throw new FacesException(exceptionMessage, 
162                                 new FaceletsOnlyException(exceptionMessage, e.getCause()));
163                     }
164                 }
165             }
166             
167             // no rule applied to this Exception - rethrow it
168             throw e;
169         }
170         finally
171         {
172             externalContext.setResponse(response);
173         }
174 
175         boolean errorResponse = wrappedResponse.getStatus() < 200 || wrappedResponse.getStatus() > 299;
176         if (errorResponse)
177         {
178             wrappedResponse.flushToWrappedResponse();
179             return;
180         }
181 
182         // Skip this step if we are rendering an ajax request, because no content outside
183         // f:view tag should be output.
184         // Note that the ResponseSwitch would prevent this output from beeing written
185         // in renderView(), but not providing the information at all makes it faster!
186         if (!context.getPartialViewContext().isPartialRequest())
187         {
188             // store the wrapped response in the request, so it is thread-safe
189             setAfterViewTagResponseWrapper(externalContext, wrappedResponse);
190         }
191         
192         // Publish PostAddToView over UIViewRoot, because this is not done automatically.
193         context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIViewRoot.class, view);
194     }
195 
196     /**
197      * 
198      */
199     @Override
200     protected boolean isViewStateAlreadyEncoded(FacesContext context)
201     {
202         ResponseStateManager responseStateManager = context.getRenderKit().getResponseStateManager();
203         if (StateCacheUtils.isMyFacesResponseStateManager(responseStateManager))
204         {
205             if (StateCacheUtils.getMyFacesResponseStateManager(responseStateManager).
206                     isWriteStateAfterRenderViewRequired(context))
207             {
208                 return false;
209             }
210             else
211             {
212                 return true;
213             }
214         }
215         else
216         {
217             return false;
218         }
219     }
220 
221     @Override
222     protected void sendSourceNotFound(FacesContext context, String message)
223     {
224         HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
225         try
226         {
227             context.responseComplete();
228             response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
229         }
230         catch (IOException ioe)
231         {
232             throw new FacesException(ioe);
233         }
234     }
235 
236 }