1   /**
2    * Copyright (c) 2000-2008 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.job.Scheduler;
29  import com.liferay.portal.kernel.deploy.hot.HotDeployUtil;
30  import com.liferay.portal.kernel.events.ActionException;
31  import com.liferay.portal.kernel.plugin.PluginPackage;
32  import com.liferay.portal.kernel.pop.MessageListener;
33  import com.liferay.portal.kernel.servlet.HttpHeaders;
34  import com.liferay.portal.kernel.servlet.PortletSessionTracker;
35  import com.liferay.portal.kernel.util.ContentTypes;
36  import com.liferay.portal.kernel.util.GetterUtil;
37  import com.liferay.portal.kernel.util.InstancePool;
38  import com.liferay.portal.kernel.util.ParamUtil;
39  import com.liferay.portal.kernel.util.PortalInitableUtil;
40  import com.liferay.portal.kernel.util.ReleaseInfo;
41  import com.liferay.portal.kernel.util.Validator;
42  import com.liferay.portal.lastmodified.LastModifiedAction;
43  import com.liferay.portal.model.ActivityTrackerInterpreter;
44  import com.liferay.portal.model.Company;
45  import com.liferay.portal.model.Portlet;
46  import com.liferay.portal.model.User;
47  import com.liferay.portal.model.impl.ActivityTrackerInterpreterImpl;
48  import com.liferay.portal.pop.POPServerUtil;
49  import com.liferay.portal.security.auth.CompanyThreadLocal;
50  import com.liferay.portal.security.auth.PrincipalThreadLocal;
51  import com.liferay.portal.service.CompanyLocalServiceUtil;
52  import com.liferay.portal.service.PortletLocalServiceUtil;
53  import com.liferay.portal.service.UserLocalServiceUtil;
54  import com.liferay.portal.service.impl.LayoutTemplateLocalUtil;
55  import com.liferay.portal.service.impl.ThemeLocalUtil;
56  import com.liferay.portal.struts.PortletRequestProcessor;
57  import com.liferay.portal.struts.StrutsUtil;
58  import com.liferay.portal.util.ActivityTrackerInterpreterUtil;
59  import com.liferay.portal.util.ContentUtil;
60  import com.liferay.portal.util.InitUtil;
61  import com.liferay.portal.util.PortalInstances;
62  import com.liferay.portal.util.PortalUtil;
63  import com.liferay.portal.util.PropsUtil;
64  import com.liferay.portal.util.PropsValues;
65  import com.liferay.portal.util.ShutdownUtil;
66  import com.liferay.portal.util.WebKeys;
67  import com.liferay.portal.velocity.VelocityContextPool;
68  import com.liferay.portlet.PortletInstanceFactory;
69  import com.liferay.util.CollectionFactory;
70  import com.liferay.util.Http;
71  import com.liferay.util.servlet.EncryptedServletRequest;
72  import com.liferay.util.servlet.ProtectedServletRequest;
73  
74  import java.io.IOException;
75  
76  import java.util.Iterator;
77  import java.util.List;
78  import java.util.Set;
79  
80  import javax.servlet.ServletContext;
81  import javax.servlet.ServletException;
82  import javax.servlet.http.HttpServletRequest;
83  import javax.servlet.http.HttpServletResponse;
84  import javax.servlet.http.HttpSession;
85  import javax.servlet.jsp.PageContext;
86  
87  import org.apache.commons.logging.Log;
88  import org.apache.commons.logging.LogFactory;
89  import org.apache.struts.Globals;
90  import org.apache.struts.action.ActionMapping;
91  import org.apache.struts.action.ActionServlet;
92  import org.apache.struts.config.ModuleConfig;
93  import org.apache.struts.tiles.TilesUtilImpl;
94  
95  import org.dom4j.Document;
96  import org.dom4j.DocumentException;
97  import org.dom4j.Element;
98  
99  import org.quartz.ObjectAlreadyExistsException;
100 
101 /**
102  * <a href="MainServlet.java.html"><b><i>View Source</i></b></a>
103  *
104  * @author Brian Wing Shun Chan
105  * @author Jorge Ferrer
106  * @author Brian Myunghun Kim
107  *
108  */
109 public class MainServlet extends ActionServlet {
110 
111     static {
112         InitUtil.init();
113     }
114 
115     public void init() throws ServletException {
116 
117         // Initialize
118 
119         if (_log.isDebugEnabled()) {
120             _log.debug("Initialize");
121         }
122 
123         super.init();
124 
125         // Process startup events
126 
127         if (_log.isDebugEnabled()) {
128             _log.debug("Process startup events");
129         }
130 
131         try {
132             EventsProcessor.process(
133                 new String[] {
134                     StartupAction.class.getName()
135                 },
136                 true);
137         }
138         catch (RuntimeException re) {
139             ShutdownUtil.shutdown(0);
140 
141             throw new ServletException(re);
142         }
143         catch (ActionException ae) {
144             _log.error(ae, ae);
145         }
146 
147         // Velocity
148 
149         String contextPath = PortalUtil.getPathContext();
150 
151         ServletContext ctx = getServletContext();
152 
153         VelocityContextPool.put(contextPath, ctx);
154 
155         // Initialize plugin package
156 
157         if (_log.isDebugEnabled()) {
158             _log.debug("Initialize plugin package");
159         }
160 
161         PluginPackage pluginPackage = null;
162 
163         try {
164             pluginPackage =
165                 PluginPackageHotDeployListener.readPluginPackage(ctx);
166         }
167         catch (Exception e) {
168             _log.error(e, e);
169         }
170 
171         // Initialize portlets
172 
173         if (_log.isDebugEnabled()) {
174             _log.debug("Initialize portlets");
175         }
176 
177         List portlets = null;
178 
179         try {
180             String[] xmls = new String[] {
181                 Http.URLtoString(ctx.getResource(
182                     "/WEB-INF/" + PortalUtil.PORTLET_XML_FILE_NAME_CUSTOM)),
183                 Http.URLtoString(ctx.getResource(
184                     "/WEB-INF/portlet-ext.xml")),
185                 Http.URLtoString(ctx.getResource(
186                     "/WEB-INF/liferay-portlet.xml")),
187                 Http.URLtoString(ctx.getResource(
188                     "/WEB-INF/liferay-portlet-ext.xml")),
189                 Http.URLtoString(ctx.getResource("/WEB-INF/web.xml"))
190             };
191 
192             PortletLocalServiceUtil.initEAR(xmls, pluginPackage);
193 
194             portlets = PortletLocalServiceUtil.getPortlets();
195 
196             Iterator itr = portlets.iterator();
197 
198             while (itr.hasNext()) {
199                 Portlet portlet = (Portlet)itr.next();
200 
201                 PortletInstanceFactory.create(portlet, ctx);
202             }
203         }
204         catch (Exception e) {
205             _log.error(e, e);
206         }
207 
208         // Initialize layout templates
209 
210         if (_log.isDebugEnabled()) {
211             _log.debug("Initialize layout templates");
212         }
213 
214         try {
215             String[] xmls = new String[] {
216                 Http.URLtoString(ctx.getResource(
217                     "/WEB-INF/liferay-layout-templates.xml")),
218                 Http.URLtoString(ctx.getResource(
219                     "/WEB-INF/liferay-layout-templates-ext.xml"))
220             };
221 
222             LayoutTemplateLocalUtil.init(ctx, xmls, pluginPackage);
223         }
224         catch (Exception e) {
225             _log.error(e, e);
226         }
227 
228         // Initialize look and feel
229 
230         if (_log.isDebugEnabled()) {
231             _log.debug("Initialize look and feel");
232         }
233 
234         try {
235             String[] xmls = new String[] {
236                 Http.URLtoString(ctx.getResource(
237                     "/WEB-INF/liferay-look-and-feel.xml")),
238                 Http.URLtoString(ctx.getResource(
239                     "/WEB-INF/liferay-look-and-feel-ext.xml"))
240             };
241 
242             ThemeLocalUtil.init(ctx, null, true, xmls, pluginPackage);
243         }
244         catch (Exception e) {
245             _log.error(e, e);
246         }
247 
248         // Scheduler
249 
250         if (_log.isDebugEnabled()) {
251             _log.debug("Scheduler");
252         }
253 
254         try {
255             if (GetterUtil.getBoolean(PropsUtil.get(
256                     PropsUtil.SCHEDULER_ENABLED))) {
257 
258                 Iterator itr = portlets.iterator();
259 
260                 while (itr.hasNext()) {
261                     Portlet portlet = (Portlet)itr.next();
262 
263                     String className = portlet.getSchedulerClass();
264 
265                     if (portlet.isActive() && Validator.isNotNull(className)) {
266                         Scheduler scheduler =
267                             (Scheduler)InstancePool.get(className);
268 
269                         scheduler.schedule();
270                     }
271                 }
272             }
273         }
274         catch (ObjectAlreadyExistsException oaee) {
275         }
276         catch (Exception e) {
277             _log.error(e, e);
278         }
279 
280         // Activity tracker interpreter
281 
282         if (_log.isDebugEnabled()) {
283             _log.debug("Activity tracker interpreter");
284         }
285 
286         try {
287             Iterator itr = portlets.iterator();
288 
289             while (itr.hasNext()) {
290                 Portlet portlet = (Portlet)itr.next();
291 
292                 ActivityTrackerInterpreter activityTrackerInterpreter =
293                     portlet.getActivityTrackerInterpreterInstance();
294 
295                 if (portlet.isActive() &&
296                     (activityTrackerInterpreter != null)) {
297 
298                     activityTrackerInterpreter =
299                         new ActivityTrackerInterpreterImpl(
300                             activityTrackerInterpreter);
301 
302                     ActivityTrackerInterpreterUtil.
303                         addActivityTrackerInterpreter(
304                             activityTrackerInterpreter);
305                 }
306             }
307         }
308         catch (Exception e) {
309             _log.error(e, e);
310         }
311 
312         // POP message listener
313 
314         if (_log.isDebugEnabled()) {
315             _log.debug("POP message listener");
316         }
317 
318         try {
319             Iterator itr = portlets.iterator();
320 
321             while (itr.hasNext()) {
322                 Portlet portlet = (Portlet)itr.next();
323 
324                 MessageListener popMessageListener =
325                     portlet.getPopMessageListenerInstance();
326 
327                 if (portlet.isActive() && (popMessageListener != null)) {
328                     POPServerUtil.addListener(popMessageListener);
329                 }
330             }
331         }
332         catch (Exception e) {
333             _log.error(e, e);
334         }
335 
336         // Check web settings
337 
338         if (_log.isDebugEnabled()) {
339             _log.debug("Check web settings");
340         }
341 
342         try {
343             String xml = Http.URLtoString(ctx.getResource("/WEB-INF/web.xml"));
344 
345             checkWebSettings(xml);
346         }
347         catch (Exception e) {
348             _log.error(e, e);
349         }
350 
351         // Last modified paths
352 
353         if (_log.isDebugEnabled()) {
354             _log.debug("Last modified paths");
355         }
356 
357         if (_lastModifiedPaths == null) {
358             _lastModifiedPaths = CollectionFactory.getHashSet();
359 
360             String[] pathsArray = PropsUtil.getArray(
361                 PropsUtil.LAST_MODIFIED_PATHS);
362 
363             for (int i = 0; i < pathsArray.length; i++) {
364                 _lastModifiedPaths.add(pathsArray[i]);
365             }
366         }
367 
368         // Process global startup events
369 
370         if (_log.isDebugEnabled()) {
371             _log.debug("Process global startup events");
372         }
373 
374         try {
375             EventsProcessor.process(PropsUtil.getArray(
376                 PropsUtil.GLOBAL_STARTUP_EVENTS), true);
377         }
378         catch (Exception e) {
379             _log.error(e, e);
380         }
381 
382         // Initialize companies
383 
384         String[] webIds = PortalInstances.getWebIds();
385 
386         for (int i = 0; i < webIds.length; i++) {
387             PortalInstances.initCompany(ctx, webIds[i]);
388         }
389 
390         // See LEP-2885. Don't flush hot deploy events until after the portal
391         // has initialized.
392 
393         PortalInitableUtil.flushInitables();
394         HotDeployUtil.flushEvents();
395     }
396 
397     public void callParentService(
398             HttpServletRequest req, HttpServletResponse res)
399         throws IOException, ServletException {
400 
401         super.service(req, res);
402     }
403 
404     public void service(HttpServletRequest req, HttpServletResponse res)
405         throws IOException, ServletException {
406 
407         if (_log.isDebugEnabled()) {
408             _log.debug("Process service request");
409         }
410 
411         if (ShutdownUtil.isShutdown()) {
412             res.setContentType(ContentTypes.TEXT_HTML_UTF8);
413 
414             String html = ContentUtil.get(
415                 "com/liferay/portal/dependencies/shutdown.html");
416 
417             res.getOutputStream().print(html);
418 
419             return;
420         }
421 
422         HttpSession ses = req.getSession();
423 
424         // Company id
425 
426         long companyId = PortalInstances.getCompanyId(req);
427 
428         //CompanyThreadLocal.setCompanyId(companyId);
429 
430         // CTX
431 
432         ServletContext ctx = getServletContext();
433 
434         req.setAttribute(WebKeys.CTX, ctx);
435 
436         // Struts module config
437 
438         ModuleConfig moduleConfig = getModuleConfig(req);
439 
440         // Last modified check
441 
442         if (PropsValues.LAST_MODIFIED_CHECK) {
443             String path = req.getPathInfo();
444 
445             if ((path != null) && _lastModifiedPaths.contains(path)) {
446                 ActionMapping mapping =
447                     (ActionMapping)moduleConfig.findActionConfig(path);
448 
449                 LastModifiedAction lastModifiedAction =
450                     (LastModifiedAction)InstancePool.get(mapping.getType());
451 
452                 String lmKey = lastModifiedAction.getLastModifiedKey(req);
453 
454                 if (lmKey != null) {
455                     long ifModifiedSince =
456                         req.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
457 
458                     if (ifModifiedSince <= 0) {
459                         lastModifiedAction.setLastModifiedValue(lmKey, lmKey);
460                     }
461                     else {
462                         String lmValue =
463                             lastModifiedAction.getLastModifiedValue(lmKey);
464 
465                         if (lmValue != null) {
466                             res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
467 
468                             return;
469                         }
470                         else {
471                             lastModifiedAction.setLastModifiedValue(
472                                 lmKey, lmKey);
473                         }
474                     }
475                 }
476             }
477         }
478 
479         // Portlet session tracker
480 
481         if (ses.getAttribute(WebKeys.PORTLET_SESSION_TRACKER) == null ) {
482             ses.setAttribute(
483                 WebKeys.PORTLET_SESSION_TRACKER,
484                 PortletSessionTracker.getInstance());
485         }
486 
487         // Portlet Request Processor
488 
489         PortletRequestProcessor portletReqProcessor =
490             (PortletRequestProcessor)ctx.getAttribute(
491                 WebKeys.PORTLET_STRUTS_PROCESSOR);
492 
493         if (portletReqProcessor == null) {
494             portletReqProcessor =
495                 PortletRequestProcessor.getInstance(this, moduleConfig);
496 
497             ctx.setAttribute(
498                 WebKeys.PORTLET_STRUTS_PROCESSOR, portletReqProcessor);
499         }
500 
501         // Tiles definitions factory
502 
503         if (ctx.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY) == null) {
504             ctx.setAttribute(
505                 TilesUtilImpl.DEFINITIONS_FACTORY,
506                 ctx.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY));
507         }
508 
509         Object applicationAssociate = ctx.getAttribute(WebKeys.ASSOCIATE_KEY);
510 
511         if (ctx.getAttribute(WebKeys.ASSOCIATE_KEY) == null) {
512             ctx.setAttribute(WebKeys.ASSOCIATE_KEY, applicationAssociate);
513         }
514 
515         // Encrypt request
516 
517         if (ParamUtil.get(req, WebKeys.ENCRYPT, false)) {
518             try {
519                 Company company = CompanyLocalServiceUtil.getCompanyById(
520                     companyId);
521 
522                 req = new EncryptedServletRequest(req, company.getKeyObj());
523             }
524             catch (Exception e) {
525             }
526         }
527 
528         // Current URL
529 
530         PortalUtil.getCurrentURL(req);
531 
532         // Login
533 
534         long userId = PortalUtil.getUserId(req);
535         String remoteUser = req.getRemoteUser();
536 
537         // Is JAAS enabled?
538 
539         if (!PropsValues.PORTAL_JAAS_ENABLE) {
540             String jRemoteUser = (String)ses.getAttribute("j_remoteuser");
541 
542             if (jRemoteUser != null) {
543                 remoteUser = jRemoteUser;
544 
545                 ses.removeAttribute("j_remoteuser");
546             }
547         }
548 
549         if ((userId > 0) && (remoteUser == null)) {
550             remoteUser = String.valueOf(userId);
551         }
552 
553         // WebSphere will not return the remote user unless you are
554         // authenticated AND accessing a protected path. Other servers will
555         // return the remote user for all threads associated with an
556         // authenticated user. We use ProtectedServletRequest to ensure we get
557         // similar behavior across all servers.
558 
559         req = new ProtectedServletRequest(req, remoteUser);
560 
561         if ((userId > 0) || (remoteUser != null)) {
562 
563             // Set the principal associated with this thread
564 
565             String name = String.valueOf(userId);
566 
567             if (remoteUser != null) {
568                 name = remoteUser;
569             }
570 
571             PrincipalThreadLocal.setName(name);
572         }
573 
574         if ((userId <= 0) && (remoteUser != null)) {
575             try {
576 
577                 // User id
578 
579                 userId = GetterUtil.getLong(remoteUser);
580 
581                 // Pre login events
582 
583                 EventsProcessor.process(PropsValues.LOGIN_EVENTS_PRE, req, res);
584 
585                 // User
586 
587                 User user = UserLocalServiceUtil.getUserById(userId);
588 
589                 UserLocalServiceUtil.updateLastLogin(
590                     userId, req.getRemoteAddr());
591 
592                 // User id
593 
594                 ses.setAttribute(WebKeys.USER_ID, new Long(userId));
595 
596                 // User locale
597 
598                 ses.setAttribute(Globals.LOCALE_KEY, user.getLocale());
599 
600                 // Post login events
601 
602                 EventsProcessor.process(
603                     PropsValues.LOGIN_EVENTS_POST, req, res);
604             }
605             catch (Exception e) {
606                 _log.error(e, e);
607             }
608         }
609 
610         // Process pre service events
611 
612         try {
613             EventsProcessor.process(
614                 PropsValues.SERVLET_SERVICE_EVENTS_PRE, req, res);
615         }
616         catch (Exception e) {
617             _log.error(e, e);
618 
619             req.setAttribute(PageContext.EXCEPTION, e);
620 
621             StrutsUtil.forward(
622                 PropsValues.SERVLET_SERVICE_EVENTS_PRE_ERROR_PAGE, ctx, req,
623                 res);
624         }
625 
626         try {
627 
628             // Struts service
629 
630             callParentService(req, res);
631         }
632         finally {
633 
634             // Process post service events
635 
636             try {
637                 EventsProcessor.process(
638                     PropsValues.SERVLET_SERVICE_EVENTS_POST, req, res);
639             }
640             catch (Exception e) {
641                 _log.error(e, e);
642             }
643 
644             res.addHeader(
645                 _LIFERAY_PORTAL_REQUEST_HEADER, ReleaseInfo.getReleaseInfo());
646 
647             // Clear the company id associated with this thread
648 
649             CompanyThreadLocal.setCompanyId(0);
650 
651             // Clear the principal associated with this thread
652 
653             PrincipalThreadLocal.setName(null);
654         }
655     }
656 
657     public void destroy() {
658         try {
659             Iterator itr = PortletLocalServiceUtil.getPortlets().iterator();
660 
661             while (itr.hasNext()) {
662                 Portlet portlet = (Portlet)itr.next();
663 
664                 PortletInstanceFactory.destroy(portlet);
665             }
666         }
667         catch (Exception e) {
668             _log.error(e, e);
669         }
670 
671         long[] companyIds = PortalInstances.getCompanyIds();
672 
673         for (int i = 0; i < companyIds.length; i++) {
674             destroyCompany(companyIds[i]);
675         }
676 
677         try {
678             EventsProcessor.process(PropsUtil.getArray(
679                 PropsUtil.GLOBAL_SHUTDOWN_EVENTS), true);
680         }
681         catch (Exception e) {
682             _log.error(e, e);
683         }
684 
685         super.destroy();
686     }
687 
688     protected void checkWebSettings(String xml) throws DocumentException {
689         Document doc = PortalUtil.readDocumentFromXML(xml);
690 
691         Element root = doc.getRootElement();
692 
693         int timeout = PropsValues.SESSION_TIMEOUT;
694 
695         Element sessionConfig = root.element("session-config");
696 
697         if (sessionConfig != null) {
698             String sessionTimeout =
699                 sessionConfig.elementText("session-timeout");
700 
701             timeout = GetterUtil.getInteger(sessionTimeout, timeout);
702         }
703 
704         PropsUtil.set(PropsUtil.SESSION_TIMEOUT, String.valueOf(timeout));
705 
706         PropsValues.SESSION_TIMEOUT = timeout;
707     }
708 
709     protected void destroyCompany(long companyId) {
710         if (_log.isDebugEnabled()) {
711             _log.debug("Process shutdown events");
712         }
713 
714         try {
715             EventsProcessor.process(PropsUtil.getArray(
716                 PropsUtil.APPLICATION_SHUTDOWN_EVENTS),
717                 new String[] {String.valueOf(companyId)});
718         }
719         catch (Exception e) {
720             _log.error(e, e);
721         }
722     }
723 
724     private static final String _LIFERAY_PORTAL_REQUEST_HEADER =
725         "Liferay-Portal";
726 
727     private static Log _log = LogFactory.getLog(MainServlet.class);
728 
729     private Set _lastModifiedPaths;
730 
731 }