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 javax.faces.webapp;
20  
21  import java.io.IOException;
22  import java.util.logging.Level;
23  import java.util.logging.Logger;
24  
25  import javax.faces.FacesException;
26  import javax.faces.FactoryFinder;
27  import javax.faces.application.ResourceHandler;
28  import javax.faces.context.FacesContext;
29  import javax.faces.context.FacesContextFactory;
30  import javax.faces.lifecycle.Lifecycle;
31  import javax.faces.lifecycle.LifecycleFactory;
32  import javax.servlet.Servlet;
33  import javax.servlet.ServletConfig;
34  import javax.servlet.ServletException;
35  import javax.servlet.ServletRequest;
36  import javax.servlet.ServletResponse;
37  import javax.servlet.annotation.MultipartConfig;
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
42  /**
43   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
44   */
45  @MultipartConfig
46  public final class FacesServlet implements Servlet
47  {
48      //private static final Log log = LogFactory.getLog(FacesServlet.class);
49      private static final Logger log = Logger.getLogger(FacesServlet.class.getName());
50      
51      /**
52       * Comma separated list of URIs of (additional) faces config files.
53       * (e.g. /WEB-INF/my-config.xml)See JSF 1.0 PRD2, 10.3.2
54       * Attention: You do not need to put /WEB-INF/faces-config.xml in here.
55       */
56      @JSFWebConfigParam(since="1.1")
57      public static final String CONFIG_FILES_ATTR = "javax.faces.CONFIG_FILES";
58  
59      /**
60       * Identify the Lifecycle instance to be used.
61       */
62      @JSFWebConfigParam(since="1.1")
63      public static final String LIFECYCLE_ID_ATTR = "javax.faces.LIFECYCLE_ID";
64      
65      /**
66       * Disable automatic FacesServlet xhtml mapping.
67       */
68      @JSFWebConfigParam(since="2.3")
69      public static final String DISABLE_FACESSERVLET_TO_XHTML_PARAM_NAME = "javax.faces.DISABLE_FACESSERVLET_TO_XHTML";
70  
71      private static final String SERVLET_INFO = "FacesServlet of the MyFaces API implementation";
72      
73      private ServletConfig _servletConfig;
74      private FacesContextFactory _facesContextFactory;
75      private Lifecycle _lifecycle;
76  
77      public FacesServlet()
78      {
79          super();
80      }
81  
82      public void destroy()
83      {
84          _servletConfig = null;
85          _facesContextFactory = null;
86          _lifecycle = null;
87          if (log.isLoggable(Level.FINEST))
88          {
89              log.finest("destroy");
90          }
91      }
92  
93      public ServletConfig getServletConfig()
94      {
95          return _servletConfig;
96      }
97  
98      public String getServletInfo()
99      {
100         return SERVLET_INFO;
101     }
102 
103     private String getLifecycleId()
104     {
105         // 1. check for Servlet's init-param
106         // 2. check for global context parameter
107         // 3. use default Lifecycle Id, if none of them was provided
108         String serLifecycleId = _servletConfig.getInitParameter(LIFECYCLE_ID_ATTR);
109         String appLifecycleId = _servletConfig.getServletContext().getInitParameter(LIFECYCLE_ID_ATTR);
110         appLifecycleId = serLifecycleId == null ? appLifecycleId : serLifecycleId;
111         return appLifecycleId != null ? appLifecycleId : LifecycleFactory.DEFAULT_LIFECYCLE;
112     }
113 
114     public void init(ServletConfig servletConfig) throws ServletException
115     {
116         if (log.isLoggable(Level.FINEST))
117         {
118             log.finest("init begin");
119         }
120         _servletConfig = servletConfig;
121         _facesContextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
122         // TODO: null-check for Weblogic, that tries to initialize Servlet before ContextListener
123 
124         // Javadoc says: Lifecycle instance is shared across multiple simultaneous requests, it must be implemented in a
125         // thread-safe manner.
126         // So we can acquire it here once:
127         LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
128         _lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
129         if (log.isLoggable(Level.FINEST))
130         {
131             log.finest("init end");
132         }
133     }
134 
135     public void service(ServletRequest request, ServletResponse response) throws IOException, ServletException
136     {
137         // If the request and response arguments to this method are not instances of HttpServletRequest and 
138         // HttpServletResponse, respectively, the results of invoking this method are undefined.
139         // In this case ClassCastException
140         HttpServletRequest httpRequest = (HttpServletRequest)request;
141         String pathInfo = httpRequest.getPathInfo();
142 
143         // if it is a prefix mapping ...
144         
145         /*
146          * This method must respond to requests that start with the following strings by invoking the sendError 
147          * method on the response argument (cast to HttpServletResponse), passing the code 
148          * HttpServletResponse.SC_NOT_FOUND as the argument.
149          * 
150          *       /WEB-INF/
151          *       /WEB-INF
152          *       /META-INF/
153          *       /META-INF
154          */
155         if (pathInfo != null && (pathInfo.startsWith("/WEB-INF") || pathInfo.startsWith("/META-INF")))
156         {
157             StringBuffer buffer = new StringBuffer();
158 
159             buffer.append(" Someone is trying to access a secure resource : ").append(pathInfo);
160             buffer.append("\n remote address is ").append(httpRequest.getRemoteAddr());
161             buffer.append("\n remote host is ").append(httpRequest.getRemoteHost());
162             buffer.append("\n remote user is ").append(httpRequest.getRemoteUser());
163             buffer.append("\n request URI is ").append(httpRequest.getRequestURI());
164 
165             log.warning(buffer.toString());
166 
167             // Why does RI return a 404 and not a 403, SC_FORBIDDEN ?
168 
169             ((HttpServletResponse)response).sendError(HttpServletResponse.SC_NOT_FOUND);
170             return;
171         }
172         
173         // If none of the cases described above in the specification for this method apply to the servicing of this 
174         // request, the following action must be taken to service the request:
175         if (log.isLoggable(Level.FINEST))
176         {
177             log.finest("service begin");
178         }
179 
180         // Acquire a FacesContext instance for this request.
181         FacesContext facesContext = prepareFacesContext(request, response);
182 
183         try
184         {
185             // jsf 2.0 : get the current ResourceHandler and
186             // check if it is a resource request, if true
187             // delegate to ResourceHandler, else continue with
188             // the lifecycle.
189             // Acquire the ResourceHandler for this request by calling Application.getResourceHandler(). 
190             ResourceHandler resourceHandler = facesContext.getApplication().getResourceHandler();
191 
192             // Call ResourceHandler.isResourceRequest(javax.faces.context.FacesContext).
193             if (resourceHandler.isResourceRequest(facesContext))
194             {
195                 // If this returns true call ResourceHandler.handleResourceRequest(javax.faces.context.FacesContext).
196                 resourceHandler.handleResourceRequest(facesContext);
197             }
198             else
199             {
200                 //JSF 2.2: attach window
201                 _lifecycle.attachWindow(facesContext);
202                 // If this returns false, handle as follows:
203                 // call Lifecycle.execute(javax.faces.context.FacesContext)
204                 _lifecycle.execute(facesContext);
205                 // followed by Lifecycle.render(javax.faces.context.FacesContext).
206                 _lifecycle.render(facesContext);
207             }
208         }
209         catch (FacesException e)
210         {
211             // If a FacesException is thrown in either case
212             
213             // extract the cause from the FacesException
214             Throwable cause = e.getCause();
215             if (cause == null)
216             {
217                 // If the cause is null extract the message from the FacesException, put it inside of a new 
218                 // ServletException instance, and pass the FacesException instance as the root cause, then 
219                 // rethrow the ServletException instance.
220                 throw new ServletException(e.getLocalizedMessage(), e);
221             }
222             else if (cause instanceof ServletException)
223             {
224                 // If the cause is an instance of ServletException, rethrow the cause.
225                 throw (ServletException)cause;
226             }
227             else if (cause instanceof IOException)
228             {
229                 // If the cause is an instance of IOException, rethrow the cause.
230                 throw (IOException)cause;
231             }
232             else
233             {
234                 // Otherwise, create a new ServletException instance, passing the message from the cause, 
235                 // as the first argument, and the cause itself as the second argument. 
236                 throw new ServletException(cause.getLocalizedMessage(), cause);
237             }
238         }
239         finally
240         {
241             // In a finally block, FacesContext.release() must be called. 
242             facesContext.release();
243         }
244         if (log.isLoggable(Level.FINEST))
245         {
246             log.finest("service end");
247         }
248     }
249 
250     private FacesContext prepareFacesContext(ServletRequest request, ServletResponse response)
251     {
252         FacesContext facesContext =
253                 _facesContextFactory.getFacesContext(_servletConfig.getServletContext(), request, response, _lifecycle);
254         return facesContext;
255     }
256 }