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