001    /*
002     * Copyright 2004 The Apache Software Foundation.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.esupportail.commons.web.servlet;
017    
018    import java.io.IOException;
019    import java.util.HashMap;
020    import java.util.Map;
021    
022    import javax.faces.FactoryFinder;
023    import javax.faces.application.Application;
024    import javax.faces.application.ViewHandler;
025    import javax.faces.component.UIViewRoot;
026    import javax.faces.context.FacesContext;
027    import javax.faces.context.FacesContextFactory;
028    import javax.faces.el.VariableResolver;
029    import javax.faces.lifecycle.Lifecycle;
030    import javax.faces.lifecycle.LifecycleFactory;
031    import javax.servlet.ServletConfig;
032    import javax.servlet.ServletException;
033    import javax.servlet.http.HttpServlet;
034    import javax.servlet.http.HttpServletRequest;
035    import javax.servlet.http.HttpServletResponse;
036    
037    import org.esupportail.commons.dao.HibernateUtils;
038    import org.esupportail.commons.exceptions.ConfigException;
039    import org.esupportail.commons.services.application.ApplicationService;
040    import org.esupportail.commons.services.application.ApplicationUtils;
041    import org.esupportail.commons.services.application.VersionningUtils;
042    import org.esupportail.commons.services.exceptionHandling.ExceptionService;
043    import org.esupportail.commons.services.exceptionHandling.ExceptionUtils;
044    import org.esupportail.commons.services.logging.Logger;
045    import org.esupportail.commons.services.logging.LoggerImpl;
046    import org.esupportail.commons.services.urlGeneration.AbstractUrlGenerator;
047    import org.esupportail.commons.services.urlGeneration.ServletUrlGeneratorImpl;
048    import org.esupportail.commons.web.deepLinking.AbstractDeepLinkingRedirector;
049    import org.esupportail.commons.web.deepLinking.DeepLinkingRedirector;
050    import org.springframework.util.StringUtils;
051    
052    /**
053     * A JSF-based servlet that catches exception and gives them to an exception service.
054     * see Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a>
055     */
056    public class FacesServlet extends HttpServlet {
057            
058            /**
059             * The id for serialization.
060             */
061            private static final long serialVersionUID = 6668301918264753450L;
062    
063            /**
064             * The default name of the redirector.
065             */
066            private static final String DEFAULT_REDIRECTOR_NAME = "deepLinkingRedirector";
067    
068            /**
069             * The name of the servlet parameter that gives the name of the redirector.
070             */
071            private static final String REDIRECTOR_NAME_PARAM = "deep-linking-redirector";
072    
073            /**
074             * The default default view.
075             */
076            private static final String DEFAULT_DEFAULT_VIEW = "/stylesheets/welcome.faces";
077    
078            /**
079             * The name of the servlet parameter that gives the name of the redirector.
080             */
081            private static final String DEFAULT_VIEW_PARAM = "default-view";
082    
083            /**
084             * The servlet info.
085             */
086        private static final String SERVLET_INFO = "FacesServlet";
087        
088            /**
089             * A logger.
090             */
091            private final Logger logger = new LoggerImpl(getClass());
092            
093        /**
094         * The context factory.
095         */
096        private FacesContextFactory facesContextFactory;
097        
098        /**
099         * The lifecycle.
100         */
101        private Lifecycle lifecycle;
102    
103            /**
104             * The name of the redirector.
105             */
106            private String redirectorName;
107            
108            /**
109             * The default view.
110             */
111            private String defaultView;
112    
113        /**
114         * Constructor.
115         */
116        public FacesServlet() {
117            super();
118        }
119    
120        /**
121         * @see javax.servlet.Servlet#destroy()
122         */
123        @Override
124            public void destroy() {
125            facesContextFactory = null;
126            lifecycle = null;
127        }
128    
129        /**
130         * @see javax.servlet.Servlet#getServletInfo()
131         */
132        @Override
133            public String getServletInfo() {
134            return SERVLET_INFO;
135        }
136    
137        private String getLifecycleId() {
138            String lifecycleId = getServletConfig().getInitParameter(javax.faces.webapp.FacesServlet.LIFECYCLE_ID_ATTR);
139            if (lifecycleId == null) {
140                    lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
141            }
142            return lifecycleId;
143        }
144    
145        /**
146         * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
147         */
148        @Override
149            public void init(final ServletConfig servletConfig) throws ServletException {
150            try {
151                    super.init(servletConfig);
152                    facesContextFactory = 
153                            (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
154                    LifecycleFactory lifecycleFactory = 
155                            (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
156                    lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
157                    ApplicationService applicationService = ApplicationUtils.createApplicationService();
158                    logger.info("starting " + applicationService.getName() + " v" 
159                                    + applicationService.getVersion() + "...");
160                    defaultView = servletConfig.getInitParameter(DEFAULT_VIEW_PARAM);
161                    if (!StringUtils.hasText(defaultView)) {
162                            defaultView = DEFAULT_DEFAULT_VIEW;
163                            logger.warn("property " + DEFAULT_VIEW_PARAM
164                                            + " is not set, using default value [" + defaultView + "]");
165                    }
166                    redirectorName = servletConfig.getInitParameter(REDIRECTOR_NAME_PARAM);
167                    if (!StringUtils.hasText(redirectorName)) {
168                            redirectorName = DEFAULT_REDIRECTOR_NAME;
169                            logger.warn("property " + REDIRECTOR_NAME_PARAM 
170                                            + " is not set, using default value [" + redirectorName + "]");
171                    }
172            } catch (Exception e) {
173                    ExceptionUtils.catchException(e);
174                    throw new ServletException(e);
175            }
176        }
177    
178            /**
179             * Check the version of the application and throw a ConfigException
180             * if the servlet can not be executed (for instance if the version
181             * of the application and the one of the database do not match).
182             * @throws ConfigException
183             */
184            protected void checkVersion() throws ConfigException {
185                    // This method can be overriden by particular implementations
186                    VersionningUtils.checkVersion(true, false);
187            }
188    
189            /**
190             * Wrap exceptions thrown when already catching an exception.
191             */
192            private void handleExceptionHandlingException(
193                            final Exception e) 
194            throws ServletException, IOException {
195                    logger.error(
196                                    "An exception was thrown while already catching a previous exception:", 
197                                    e); 
198            if (e instanceof IOException) {
199                throw (IOException) e;
200            } else if (e instanceof ServletException) {
201                throw (ServletException) e;
202            } else if (e.getMessage() != null) {
203                throw new ServletException(e.getMessage(), e);
204            } else {
205                throw new ServletException(e);
206            }
207            }
208    
209            /**
210             * Catch an exception.
211             * @param exception
212             * @param request 
213             * @param response 
214             * @throws ServletException 
215             */
216            protected void catchException(
217                            final Exception exception, 
218                            final HttpServletRequest request, 
219                            final HttpServletResponse response) throws ServletException, IOException {
220                    ExceptionUtils.markExceptionCaught(request); 
221                    ExceptionService exceptionService = null;
222                    try {
223                            exceptionService = ExceptionUtils.catchException(getServletContext(), exception, request);
224                    } catch (Exception e) {
225                            handleExceptionHandlingException(e);
226                            // never reached, prevent from warnings
227                            return;
228                    }
229                    ExceptionUtils.markExceptionCaught(request, exceptionService); 
230                    try {
231                            String exceptionUrl = request.getContextPath() 
232                            + exceptionService.getExceptionView().replace(".jsp", ".faces");
233                            response.sendRedirect(exceptionUrl);
234                            logger.info("redirected the browser to [" + exceptionUrl + "]");
235                            return;
236                    } catch (Exception e) {
237                            handleExceptionHandlingException(e);
238                    }
239            }
240    
241            /**
242             * @return the redirector
243             */
244            private DeepLinkingRedirector getRedirector(
245                            final FacesContext facesContext) {
246            VariableResolver vr = facesContext.getApplication().getVariableResolver();
247            DeepLinkingRedirector deepLinkingRedirector = 
248                    (DeepLinkingRedirector) vr.resolveVariable(facesContext, redirectorName);
249            if (deepLinkingRedirector == null) {
250                    throw new ConfigException("bean [" + redirectorName + "] not found!");
251            }
252            return deepLinkingRedirector;
253            }
254    
255            /**
256             * @return the parameters of the current request.
257             */
258            private Map<String, String> getParams(
259                            final HttpServletRequest request) {
260                    if (request == null) {
261                            return null;
262                    }
263                    Map<String, String>  params = AbstractUrlGenerator.decodeArgToParams(
264                                    request.getParameter(ServletUrlGeneratorImpl.ARGS_PARAM));
265                    if (request.getParameter("enter") != null) {
266                            if (params == null) {
267                                    params = new HashMap<String, String>();
268                            }
269                            params.put(AbstractDeepLinkingRedirector.ENTER_PARAM, "");
270                    }
271                    return params;
272            }
273    
274        /**
275         * @see javax.servlet.http.HttpServlet#service(
276         * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
277         */
278        @Override
279            public void service(final HttpServletRequest request,
280                            final HttpServletResponse response) 
281        throws IOException, ServletException {
282    
283            FacesContext facesContext = null;
284            try {
285                String pathInfo = request.getPathInfo();
286    
287                // if it is a prefix mapping ...
288                if (pathInfo != null && (pathInfo.startsWith("/WEB-INF") || pathInfo .startsWith("/META-INF"))) {
289                    StringBuffer buffer = new StringBuffer();
290                    buffer.append(" Someone is trying to access a secure resource : " + pathInfo);
291                    buffer.append("\n remote address is " + request.getRemoteAddr());
292                    buffer.append("\n remote host is " + request.getRemoteHost());
293                    buffer.append("\n remote user is " + request.getRemoteUser());
294                    buffer.append("\n request URI is " + request.getRequestURI());
295                    logger.warn(buffer.toString());
296                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
297                    return;
298                }
299                HibernateUtils.open(getServletContext());
300                            if (!ExceptionUtils.exceptionAlreadyCaught(request)) {
301                                    VersionningUtils.checkVersion(getServletContext(), true, false);
302                            }
303                facesContext = facesContextFactory.getFacesContext(
304                            getServletConfig().getServletContext(), request, response, lifecycle);
305                    Map<String, String> params = getParams(request); 
306                    if (params != null) {
307                            String view = getRedirector(facesContext).redirect(params);
308                            if (view == null) {
309                                    view = defaultView;
310                            }
311                    Application application = facesContext.getApplication();
312                    ViewHandler viewHandler = application.getViewHandler();
313                    UIViewRoot viewRoot = viewHandler.createView(facesContext, view);
314                    viewRoot.setViewId(view);
315                    facesContext.setViewRoot(viewRoot);
316                    } else {
317                            lifecycle.execute(facesContext);
318                    }
319                            lifecycle.render(facesContext);
320                HibernateUtils.close(true);
321                    } catch (Exception e) {
322                HibernateUtils.close(false);
323                            catchException(e, request, response);
324            } finally {
325                    if (facesContext != null) {
326                            facesContext.release();
327                    }
328            }
329        }
330    }