1   /**
2    * Copyright (c) 2000-2007 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.servlet;
24  
25  import com.liferay.portal.deploy.hot.PluginPackageHotDeployListener;
26  import com.liferay.portal.events.EventsProcessor;
27  import com.liferay.portal.events.StartupAction;
28  import com.liferay.portal.kernel.deploy.hot.HotDeployUtil;
29  import com.liferay.portal.kernel.plugin.PluginPackage;
30  import com.liferay.portal.kernel.servlet.HttpHeaders;
31  import com.liferay.portal.kernel.servlet.PortletSessionTracker;
32  import com.liferay.portal.kernel.util.ContentTypes;
33  import com.liferay.portal.kernel.util.GetterUtil;
34  import com.liferay.portal.kernel.util.InstancePool;
35  import com.liferay.portal.kernel.util.ParamUtil;
36  import com.liferay.portal.kernel.util.PortalInitableUtil;
37  import com.liferay.portal.lastmodified.LastModifiedAction;
38  import com.liferay.portal.model.Company;
39  import com.liferay.portal.model.Portlet;
40  import com.liferay.portal.model.User;
41  import com.liferay.portal.security.auth.CompanyThreadLocal;
42  import com.liferay.portal.security.auth.PrincipalThreadLocal;
43  import com.liferay.portal.service.CompanyLocalServiceUtil;
44  import com.liferay.portal.service.PortletLocalServiceUtil;
45  import com.liferay.portal.service.UserLocalServiceUtil;
46  import com.liferay.portal.service.impl.LayoutTemplateLocalUtil;
47  import com.liferay.portal.service.impl.ThemeLocalUtil;
48  import com.liferay.portal.struts.ActionException;
49  import com.liferay.portal.struts.PortletRequestProcessor;
50  import com.liferay.portal.struts.StrutsUtil;
51  import com.liferay.portal.util.ContentUtil;
52  import com.liferay.portal.util.InitUtil;
53  import com.liferay.portal.util.PortalInstances;
54  import com.liferay.portal.util.PortalUtil;
55  import com.liferay.portal.util.PropsUtil;
56  import com.liferay.portal.util.ReleaseInfo;
57  import com.liferay.portal.util.ShutdownUtil;
58  import com.liferay.portal.util.WebKeys;
59  import com.liferay.portal.velocity.VelocityContextPool;
60  import com.liferay.portlet.PortletInstanceFactory;
61  import com.liferay.util.CollectionFactory;
62  import com.liferay.util.Http;
63  import com.liferay.util.servlet.EncryptedServletRequest;
64  import com.liferay.util.servlet.ProtectedServletRequest;
65  
66  import java.io.IOException;
67  
68  import java.util.Iterator;
69  import java.util.Set;
70  
71  import javax.servlet.ServletContext;
72  import javax.servlet.ServletException;
73  import javax.servlet.http.HttpServletRequest;
74  import javax.servlet.http.HttpServletResponse;
75  import javax.servlet.http.HttpSession;
76  import javax.servlet.jsp.PageContext;
77  
78  import org.apache.commons.logging.Log;
79  import org.apache.commons.logging.LogFactory;
80  import org.apache.struts.Globals;
81  import org.apache.struts.action.ActionMapping;
82  import org.apache.struts.action.ActionServlet;
83  import org.apache.struts.config.ModuleConfig;
84  import org.apache.struts.tiles.TilesUtilImpl;
85  
86  import org.dom4j.Document;
87  import org.dom4j.DocumentException;
88  import org.dom4j.Element;
89  
90  /**
91   * <a href="MainServlet.java.html"><b><i>View Source</i></b></a>
92   *
93   * @author Brian Wing Shun Chan
94   * @author Jorge Ferrer
95   * @author Brian Myunghun Kim
96   *
97   */
98  public class MainServlet extends ActionServlet {
99  
100     static {
101         InitUtil.init();
102     }
103 
104     public void init() throws ServletException {
105 
106         // Initialize
107 
108         if (_log.isDebugEnabled()) {
109             _log.debug("Initialize");
110         }
111 
112         super.init();
113 
114         // Process startup events
115 
116         if (_log.isDebugEnabled()) {
117             _log.debug("Process startup events");
118         }
119 
120         try {
121             EventsProcessor.process(
122                 new String[] {
123                     StartupAction.class.getName()
124                 },
125                 true);
126         }
127         catch (RuntimeException re) {
128             ShutdownUtil.shutdown(0);
129 
130             throw new ServletException(re);
131         }
132         catch (ActionException ae) {
133             _log.error(ae, ae);
134         }
135 
136         // Velocity
137 
138         String contextPath = PortalUtil.getPathContext();
139 
140         ServletContext ctx = getServletContext();
141 
142         VelocityContextPool.put(contextPath, ctx);
143 
144         // Initialize plugin package
145 
146         if (_log.isDebugEnabled()) {
147             _log.debug("Initialize plugin package");
148         }
149 
150         PluginPackage pluginPackage = null;
151 
152         try {
153             pluginPackage =
154                 PluginPackageHotDeployListener.readPluginPackage(ctx);
155         }
156         catch (Exception e) {
157             _log.error(e, e);
158         }
159 
160         // Initialize portlets
161 
162         if (_log.isDebugEnabled()) {
163             _log.debug("Initialize portlets");
164         }
165 
166         try {
167             String[] xmls = new String[] {
168                 Http.URLtoString(ctx.getResource(
169                     "/WEB-INF/" + PortalUtil.PORTLET_XML_FILE_NAME_CUSTOM)),
170                 Http.URLtoString(ctx.getResource(
171                     "/WEB-INF/portlet-ext.xml")),
172                 Http.URLtoString(ctx.getResource(
173                     "/WEB-INF/liferay-portlet.xml")),
174                 Http.URLtoString(ctx.getResource(
175                     "/WEB-INF/liferay-portlet-ext.xml")),
176                 Http.URLtoString(ctx.getResource("/WEB-INF/web.xml"))
177             };
178 
179             PortletLocalServiceUtil.initEAR(xmls, pluginPackage);
180         }
181         catch (Exception e) {
182             _log.error(e, e);
183         }
184 
185         // Initialize layout templates
186 
187         if (_log.isDebugEnabled()) {
188             _log.debug("Initialize layout templates");
189         }
190 
191         try {
192             String[] xmls = new String[] {
193                 Http.URLtoString(ctx.getResource(
194                     "/WEB-INF/liferay-layout-templates.xml")),
195                 Http.URLtoString(ctx.getResource(
196                     "/WEB-INF/liferay-layout-templates-ext.xml"))
197             };
198 
199             LayoutTemplateLocalUtil.init(ctx, xmls, pluginPackage);
200         }
201         catch (Exception e) {
202             _log.error(e, e);
203         }
204 
205         // Initialize look and feel
206 
207         if (_log.isDebugEnabled()) {
208             _log.debug("Initialize look and feel");
209         }
210 
211         try {
212             String[] xmls = new String[] {
213                 Http.URLtoString(ctx.getResource(
214                     "/WEB-INF/liferay-look-and-feel.xml")),
215                 Http.URLtoString(ctx.getResource(
216                     "/WEB-INF/liferay-look-and-feel-ext.xml"))
217             };
218 
219             ThemeLocalUtil.init(ctx, null, true, xmls, pluginPackage);
220         }
221         catch (Exception e) {
222             _log.error(e, e);
223         }
224 
225         // Check web settings
226 
227         if (_log.isDebugEnabled()) {
228             _log.debug("Check web settings");
229         }
230 
231         try {
232             String xml = Http.URLtoString(ctx.getResource("/WEB-INF/web.xml"));
233 
234             checkWebSettings(xml);
235         }
236         catch (Exception e) {
237             _log.error(e, e);
238         }
239 
240         // Last modified paths
241 
242         if (_log.isDebugEnabled()) {
243             _log.debug("Last modified paths");
244         }
245 
246         if (_lastModifiedPaths == null) {
247             _lastModifiedPaths = CollectionFactory.getHashSet();
248 
249             String[] pathsArray = PropsUtil.getArray(
250                 PropsUtil.LAST_MODIFIED_PATHS);
251 
252             for (int i = 0; i < pathsArray.length; i++) {
253                 _lastModifiedPaths.add(pathsArray[i]);
254             }
255         }
256 
257         // Process global startup events
258 
259         if (_log.isDebugEnabled()) {
260             _log.debug("Process global startup events");
261         }
262 
263         try {
264             EventsProcessor.process(PropsUtil.getArray(
265                 PropsUtil.GLOBAL_STARTUP_EVENTS), true);
266         }
267         catch (Exception e) {
268             _log.error(e, e);
269         }
270 
271         // Initialize companies
272 
273         String[] webIds = PortalInstances.getWebIds();
274 
275         for (int i = 0; i < webIds.length; i++) {
276             PortalInstances.initCompany(ctx, webIds[i]);
277         }
278 
279         // See LEP-2885. Don't flush hot deploy events until after the portal
280         // has initialized.
281 
282         PortalInitableUtil.flushInitables();
283         HotDeployUtil.flushEvents();
284     }
285 
286     public void callParentService(
287             HttpServletRequest req, HttpServletResponse res)
288         throws IOException, ServletException {
289 
290         super.service(req, res);
291     }
292 
293     public void service(HttpServletRequest req, HttpServletResponse res)
294         throws IOException, ServletException {
295 
296         if (_log.isDebugEnabled()) {
297             _log.debug("Process service request");
298         }
299 
300         if (ShutdownUtil.isShutdown()) {
301             res.setContentType(ContentTypes.TEXT_HTML_UTF8);
302 
303             String html = ContentUtil.get(
304                 "com/liferay/portal/dependencies/shutdown.html");
305 
306             res.getOutputStream().print(html);
307 
308             return;
309         }
310 
311         HttpSession ses = req.getSession();
312 
313         // Company id
314 
315         long companyId = PortalInstances.getCompanyId(req);
316 
317         //CompanyThreadLocal.setCompanyId(companyId);
318 
319         // CTX
320 
321         ServletContext ctx = getServletContext();
322 
323         req.setAttribute(WebKeys.CTX, ctx);
324 
325         // Struts module config
326 
327         ModuleConfig moduleConfig = getModuleConfig(req);
328 
329         // Last modified check
330 
331         if (GetterUtil.getBoolean(
332                 PropsUtil.get(PropsUtil.LAST_MODIFIED_CHECK))) {
333 
334             String path = req.getPathInfo();
335 
336             if ((path != null) && _lastModifiedPaths.contains(path)) {
337                 ActionMapping mapping =
338                     (ActionMapping)moduleConfig.findActionConfig(path);
339 
340                 LastModifiedAction lastModifiedAction =
341                     (LastModifiedAction)InstancePool.get(mapping.getType());
342 
343                 String lmKey = lastModifiedAction.getLastModifiedKey(req);
344 
345                 if (lmKey != null) {
346                     long ifModifiedSince =
347                         req.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
348 
349                     if (ifModifiedSince <= 0) {
350                         lastModifiedAction.setLastModifiedValue(lmKey, lmKey);
351                     }
352                     else {
353                         String lmValue =
354                             lastModifiedAction.getLastModifiedValue(lmKey);
355 
356                         if (lmValue != null) {
357                             res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
358 
359                             return;
360                         }
361                         else {
362                             lastModifiedAction.setLastModifiedValue(
363                                 lmKey, lmKey);
364                         }
365                     }
366                 }
367             }
368         }
369 
370         // Portlet session tracker
371 
372         if (ses.getAttribute(WebKeys.PORTLET_SESSION_TRACKER) == null ) {
373             ses.setAttribute(
374                 WebKeys.PORTLET_SESSION_TRACKER,
375                 PortletSessionTracker.getInstance());
376         }
377 
378         // Portlet Request Processor
379 
380         PortletRequestProcessor portletReqProcessor =
381             (PortletRequestProcessor)ctx.getAttribute(
382                 WebKeys.PORTLET_STRUTS_PROCESSOR);
383 
384         if (portletReqProcessor == null) {
385             portletReqProcessor =
386                 PortletRequestProcessor.getInstance(this, moduleConfig);
387 
388             ctx.setAttribute(
389                 WebKeys.PORTLET_STRUTS_PROCESSOR, portletReqProcessor);
390         }
391 
392         // Tiles definitions factory
393 
394         if (ctx.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY) == null) {
395             ctx.setAttribute(
396                 TilesUtilImpl.DEFINITIONS_FACTORY,
397                 ctx.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY));
398         }
399 
400         Object applicationAssociate = ctx.getAttribute(WebKeys.ASSOCIATE_KEY);
401 
402         if (ctx.getAttribute(WebKeys.ASSOCIATE_KEY) == null) {
403             ctx.setAttribute(WebKeys.ASSOCIATE_KEY, applicationAssociate);
404         }
405 
406         // Encrypt request
407 
408         if (ParamUtil.get(req, WebKeys.ENCRYPT, false)) {
409             try {
410                 Company company = CompanyLocalServiceUtil.getCompanyById(
411                     companyId);
412 
413                 req = new EncryptedServletRequest(req, company.getKeyObj());
414             }
415             catch (Exception e) {
416             }
417         }
418 
419         // Current URL
420 
421         PortalUtil.getCurrentURL(req);
422 
423         // Login
424 
425         long userId = PortalUtil.getUserId(req);
426         String remoteUser = req.getRemoteUser();
427 
428         // Is JAAS enabled?
429 
430         if (!GetterUtil.getBoolean(
431                 PropsUtil.get(PropsUtil.PORTAL_JAAS_ENABLE))) {
432 
433             String jRemoteUser = (String)ses.getAttribute("j_remoteuser");
434 
435             if (jRemoteUser != null) {
436                 remoteUser = jRemoteUser;
437 
438                 ses.removeAttribute("j_remoteuser");
439             }
440         }
441 
442         if ((userId > 0) && (remoteUser == null)) {
443             remoteUser = String.valueOf(userId);
444         }
445 
446         // WebSphere will not return the remote user unless you are
447         // authenticated AND accessing a protected path. Other servers will
448         // return the remote user for all threads associated with an
449         // authenticated user. We use ProtectedServletRequest to ensure we get
450         // similar behavior across all servers.
451 
452         req = new ProtectedServletRequest(req, remoteUser);
453 
454         if ((userId > 0) || (remoteUser != null)) {
455 
456             // Set the principal associated with this thread
457 
458             String name = String.valueOf(userId);
459 
460             if (remoteUser != null) {
461                 name = remoteUser;
462             }
463 
464             PrincipalThreadLocal.setName(name);
465         }
466 
467         if ((userId <= 0) && (remoteUser != null)) {
468             try {
469 
470                 // User id
471 
472                 userId = GetterUtil.getLong(remoteUser);
473 
474                 // Pre login events
475 
476                 EventsProcessor.process(PropsUtil.getArray(
477                     PropsUtil.LOGIN_EVENTS_PRE), req, res);
478 
479                 // User
480 
481                 User user = UserLocalServiceUtil.getUserById(userId);
482 
483                 UserLocalServiceUtil.updateLastLogin(
484                     userId, req.getRemoteAddr());
485 
486                 // User id
487 
488                 ses.setAttribute(WebKeys.USER_ID, new Long(userId));
489 
490                 // User locale
491 
492                 ses.setAttribute(Globals.LOCALE_KEY, user.getLocale());
493 
494                 // Post login events
495 
496                 EventsProcessor.process(PropsUtil.getArray(
497                     PropsUtil.LOGIN_EVENTS_POST), req, res);
498             }
499             catch (Exception e) {
500                 _log.error(e, e);
501             }
502         }
503 
504         // Process pre service events
505 
506         try {
507             EventsProcessor.process(PropsUtil.getArray(
508                 PropsUtil.SERVLET_SERVICE_EVENTS_PRE), req, res);
509         }
510         catch (Exception e) {
511             _log.error(e, e);
512 
513             req.setAttribute(PageContext.EXCEPTION, e);
514 
515             StrutsUtil.forward(
516                 PropsUtil.get(
517                     PropsUtil.SERVLET_SERVICE_EVENTS_PRE_ERROR_PAGE),
518                 ctx, req, res);
519         }
520 
521         try {
522 
523             // Struts service
524 
525             callParentService(req, res);
526         }
527         finally {
528 
529             // Process post service events
530 
531             try {
532                 EventsProcessor.process(PropsUtil.getArray(
533                     PropsUtil.SERVLET_SERVICE_EVENTS_POST), req, res);
534             }
535             catch (Exception e) {
536                 _log.error(e, e);
537             }
538 
539             res.addHeader(
540                 _LIFERAY_PORTAL_REQUEST_HEADER, ReleaseInfo.getReleaseInfo());
541 
542             // Clear the company id associated with this thread
543 
544             CompanyThreadLocal.setCompanyId(0);
545 
546             // Clear the principal associated with this thread
547 
548             PrincipalThreadLocal.setName(null);
549         }
550     }
551 
552     public void destroy() {
553         long[] companyIds = PortalInstances.getCompanyIds();
554 
555         for (int i = 0; i < companyIds.length; i++) {
556             destroyCompany(companyIds[i]);
557         }
558 
559         try {
560             EventsProcessor.process(PropsUtil.getArray(
561                 PropsUtil.GLOBAL_SHUTDOWN_EVENTS), true);
562         }
563         catch (Exception e) {
564             _log.error(e, e);
565         }
566 
567         // Parent
568 
569         super.destroy();
570     }
571 
572     protected void checkWebSettings(String xml) throws DocumentException {
573         Document doc = PortalUtil.readDocumentFromXML(xml);
574 
575         Element root = doc.getRootElement();
576 
577         int timeout = GetterUtil.getInteger(
578             PropsUtil.get(PropsUtil.SESSION_TIMEOUT));
579 
580         Element sessionConfig = root.element("session-config");
581 
582         if (sessionConfig != null) {
583             String sessionTimeout =
584                 sessionConfig.elementText("session-timeout");
585 
586             timeout = GetterUtil.getInteger(sessionTimeout, timeout);
587         }
588 
589         PropsUtil.set(PropsUtil.SESSION_TIMEOUT, Integer.toString(timeout));
590     }
591 
592     protected void destroyCompany(long companyId) {
593 
594         // Destroy portlets
595 
596         try {
597             Iterator itr = PortletLocalServiceUtil.getPortlets(
598                 companyId).iterator();
599 
600             while (itr.hasNext()) {
601                 Portlet portlet = (Portlet)itr.next();
602 
603                 PortletInstanceFactory.destroy(portlet);
604             }
605         }
606         catch (Exception e) {
607             _log.error(e, e);
608         }
609 
610         // Process shutdown events
611 
612         if (_log.isDebugEnabled()) {
613             _log.debug("Process shutdown events");
614         }
615 
616         try {
617             EventsProcessor.process(PropsUtil.getArray(
618                 PropsUtil.APPLICATION_SHUTDOWN_EVENTS),
619                 new String[] {String.valueOf(companyId)});
620         }
621         catch (Exception e) {
622             _log.error(e, e);
623         }
624     }
625 
626     private static final String _LIFERAY_PORTAL_REQUEST_HEADER =
627         "Liferay-Portal";
628 
629     private static Log _log = LogFactory.getLog(MainServlet.class);
630 
631     private Set _lastModifiedPaths;
632 
633 }