1
22
23 package com.liferay.portal.servlet;
24
25 import com.liferay.portal.NoSuchLayoutException;
26 import com.liferay.portal.deploy.hot.PluginPackageHotDeployListener;
27 import com.liferay.portal.events.EventsProcessor;
28 import com.liferay.portal.events.StartupAction;
29 import com.liferay.portal.kernel.deploy.hot.HotDeployUtil;
30 import com.liferay.portal.kernel.events.ActionException;
31 import com.liferay.portal.kernel.job.Scheduler;
32 import com.liferay.portal.kernel.plugin.PluginPackage;
33 import com.liferay.portal.kernel.pop.MessageListener;
34 import com.liferay.portal.kernel.servlet.HttpHeaders;
35 import com.liferay.portal.kernel.servlet.PortletSessionTracker;
36 import com.liferay.portal.kernel.servlet.ProtectedServletRequest;
37 import com.liferay.portal.kernel.util.ContentTypes;
38 import com.liferay.portal.kernel.util.GetterUtil;
39 import com.liferay.portal.kernel.util.HttpUtil;
40 import com.liferay.portal.kernel.util.InstancePool;
41 import com.liferay.portal.kernel.util.ParamUtil;
42 import com.liferay.portal.kernel.util.PortalInitableUtil;
43 import com.liferay.portal.kernel.util.ReleaseInfo;
44 import com.liferay.portal.kernel.util.StringPool;
45 import com.liferay.portal.kernel.util.Validator;
46 import com.liferay.portal.lastmodified.LastModifiedAction;
47 import com.liferay.portal.model.Company;
48 import com.liferay.portal.model.Portlet;
49 import com.liferay.portal.model.PortletApp;
50 import com.liferay.portal.model.PortletFilter;
51 import com.liferay.portal.model.PortletURLListener;
52 import com.liferay.portal.model.User;
53 import com.liferay.portal.pop.POPServerUtil;
54 import com.liferay.portal.security.auth.CompanyThreadLocal;
55 import com.liferay.portal.security.auth.PrincipalThreadLocal;
56 import com.liferay.portal.service.CompanyLocalServiceUtil;
57 import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
58 import com.liferay.portal.service.PortletLocalServiceUtil;
59 import com.liferay.portal.service.ThemeLocalServiceUtil;
60 import com.liferay.portal.service.UserLocalServiceUtil;
61 import com.liferay.portal.struts.PortletRequestProcessor;
62 import com.liferay.portal.struts.StrutsUtil;
63 import com.liferay.portal.util.ContentUtil;
64 import com.liferay.portal.util.DocumentUtil;
65 import com.liferay.portal.util.InitUtil;
66 import com.liferay.portal.util.Portal;
67 import com.liferay.portal.util.PortalInstances;
68 import com.liferay.portal.util.PortalUtil;
69 import com.liferay.portal.util.PropsKeys;
70 import com.liferay.portal.util.PropsUtil;
71 import com.liferay.portal.util.PropsValues;
72 import com.liferay.portal.util.ShutdownUtil;
73 import com.liferay.portal.util.WebKeys;
74 import com.liferay.portal.velocity.VelocityContextPool;
75 import com.liferay.portlet.PortletConfigFactory;
76 import com.liferay.portlet.PortletFilterFactory;
77 import com.liferay.portlet.PortletInstanceFactory;
78 import com.liferay.portlet.PortletURLListenerFactory;
79 import com.liferay.portlet.social.model.SocialActivityInterpreter;
80 import com.liferay.portlet.social.model.SocialRequestInterpreter;
81 import com.liferay.portlet.social.model.impl.SocialActivityInterpreterImpl;
82 import com.liferay.portlet.social.model.impl.SocialRequestInterpreterImpl;
83 import com.liferay.portlet.social.service.SocialActivityInterpreterLocalServiceUtil;
84 import com.liferay.portlet.social.service.SocialRequestInterpreterLocalServiceUtil;
85 import com.liferay.util.servlet.DynamicServletRequest;
86 import com.liferay.util.servlet.EncryptedServletRequest;
87
88 import java.io.IOException;
89
90 import java.util.HashSet;
91 import java.util.Iterator;
92 import java.util.List;
93 import java.util.Set;
94
95 import javax.portlet.PortletConfig;
96 import javax.portlet.PortletContext;
97 import javax.portlet.PortletException;
98
99 import javax.servlet.ServletContext;
100 import javax.servlet.ServletException;
101 import javax.servlet.http.HttpServletRequest;
102 import javax.servlet.http.HttpServletResponse;
103 import javax.servlet.http.HttpSession;
104 import javax.servlet.jsp.PageContext;
105
106 import org.apache.commons.logging.Log;
107 import org.apache.commons.logging.LogFactory;
108 import org.apache.struts.Globals;
109 import org.apache.struts.action.ActionMapping;
110 import org.apache.struts.action.ActionServlet;
111 import org.apache.struts.config.ModuleConfig;
112 import org.apache.struts.tiles.TilesUtilImpl;
113
114 import org.dom4j.Document;
115 import org.dom4j.DocumentException;
116 import org.dom4j.Element;
117
118
126 public class MainServlet extends ActionServlet {
127
128 static {
129 InitUtil.init();
130 }
131
132 public void init() throws ServletException {
133
134
136 if (_log.isDebugEnabled()) {
137 _log.debug("Initialize");
138 }
139
140 super.init();
141
142
144 if (_log.isDebugEnabled()) {
145 _log.debug("Process startup events");
146 }
147
148 try {
149 StartupAction startupAction = new StartupAction();
150
151 startupAction.run(null);
152 }
153 catch (RuntimeException re) {
154 ShutdownUtil.shutdown(0);
155
156 throw new ServletException(re);
157 }
158 catch (ActionException ae) {
159 _log.error(ae, ae);
160 }
161
162
164 String contextPath = PortalUtil.getPathContext();
165
166 ServletContext servletContext = getServletContext();
167
168 VelocityContextPool.put(contextPath, servletContext);
169
170
172 if (_log.isDebugEnabled()) {
173 _log.debug("Initialize plugin package");
174 }
175
176 PluginPackage pluginPackage = null;
177
178 try {
179 pluginPackage =
180 PluginPackageHotDeployListener.readPluginPackage(
181 servletContext);
182 }
183 catch (Exception e) {
184 _log.error(e, e);
185 }
186
187
189 if (_log.isDebugEnabled()) {
190 _log.debug("Initialize portlets");
191 }
192
193 List<Portlet> portlets = null;
194
195 try {
196 String[] xmls = new String[] {
197 HttpUtil.URLtoString(servletContext.getResource(
198 "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_CUSTOM)),
199 HttpUtil.URLtoString(servletContext.getResource(
200 "/WEB-INF/portlet-ext.xml")),
201 HttpUtil.URLtoString(servletContext.getResource(
202 "/WEB-INF/liferay-portlet.xml")),
203 HttpUtil.URLtoString(servletContext.getResource(
204 "/WEB-INF/liferay-portlet-ext.xml")),
205 HttpUtil.URLtoString(servletContext.getResource(
206 "/WEB-INF/web.xml"))
207 };
208
209 PortletLocalServiceUtil.initEAR(xmls, pluginPackage);
210
211 portlets = PortletLocalServiceUtil.getPortlets();
212
213 for (int i = 0; i < portlets.size(); i++) {
214 Portlet portlet = portlets.get(i);
215
216 if (i == 0) {
217 initPortletApp(portlet, servletContext);
218 }
219
220 PortletInstanceFactory.create(portlet, servletContext);
221 }
222 }
223 catch (Exception e) {
224 _log.error(e, e);
225 }
226
227
229 if (_log.isDebugEnabled()) {
230 _log.debug("Initialize layout templates");
231 }
232
233 try {
234 String[] xmls = new String[] {
235 HttpUtil.URLtoString(servletContext.getResource(
236 "/WEB-INF/liferay-layout-templates.xml")),
237 HttpUtil.URLtoString(servletContext.getResource(
238 "/WEB-INF/liferay-layout-templates-ext.xml"))
239 };
240
241 LayoutTemplateLocalServiceUtil.init(
242 servletContext, xmls, pluginPackage);
243 }
244 catch (Exception e) {
245 _log.error(e, e);
246 }
247
248
250 if (_log.isDebugEnabled()) {
251 _log.debug("Initialize look and feel");
252 }
253
254 try {
255 String[] xmls = new String[] {
256 HttpUtil.URLtoString(servletContext.getResource(
257 "/WEB-INF/liferay-look-and-feel.xml")),
258 HttpUtil.URLtoString(servletContext.getResource(
259 "/WEB-INF/liferay-look-and-feel-ext.xml"))
260 };
261
262 ThemeLocalServiceUtil.init(
263 servletContext, null, true, xmls, pluginPackage);
264 }
265 catch (Exception e) {
266 _log.error(e, e);
267 }
268
269
271 if (_log.isDebugEnabled()) {
272 _log.debug("Scheduler");
273 }
274
275 try {
276 if (PropsValues.SCHEDULER_ENABLED) {
277 for (String className : PropsValues.SCHEDULER_CLASSES) {
278 Scheduler scheduler = (Scheduler)InstancePool.get(
279 className);
280
281 scheduler.schedule();
282 }
283
284 Iterator<Portlet> itr = portlets.iterator();
285
286 while (itr.hasNext()) {
287 Portlet portlet = itr.next();
288
289 String className = portlet.getSchedulerClass();
290
291 if (portlet.isActive() && Validator.isNotNull(className)) {
292 Scheduler scheduler = (Scheduler)InstancePool.get(
293 className);
294
295 scheduler.schedule();
296 }
297 }
298 }
299 }
300 catch (Exception e) {
301 _log.error(e, e);
302 }
303
304
306 if (_log.isDebugEnabled()) {
307 _log.debug("POP message listener");
308 }
309
310 try {
311 Iterator<Portlet> itr = portlets.iterator();
312
313 while (itr.hasNext()) {
314 Portlet portlet = itr.next();
315
316 MessageListener popMessageListener =
317 portlet.getPopMessageListenerInstance();
318
319 if (portlet.isActive() && (popMessageListener != null)) {
320 POPServerUtil.addListener(popMessageListener);
321 }
322 }
323 }
324 catch (Exception e) {
325 _log.error(e, e);
326 }
327
328
330 if (_log.isDebugEnabled()) {
331 _log.debug("Social activity interpreter");
332 }
333
334 try {
335 Iterator<Portlet> itr = portlets.iterator();
336
337 while (itr.hasNext()) {
338 Portlet portlet = itr.next();
339
340 SocialActivityInterpreter socialActivityInterpreter =
341 portlet.getSocialActivityInterpreterInstance();
342
343 if (portlet.isActive() && (socialActivityInterpreter != null)) {
344 socialActivityInterpreter =
345 new SocialActivityInterpreterImpl(
346 portlet.getPortletId(), socialActivityInterpreter);
347
348 SocialActivityInterpreterLocalServiceUtil.
349 addActivityInterpreter(socialActivityInterpreter);
350 }
351 }
352 }
353 catch (Exception e) {
354 _log.error(e, e);
355 }
356
357
359 if (_log.isDebugEnabled()) {
360 _log.debug("Social request interpreter");
361 }
362
363 try {
364 Iterator<Portlet> itr = portlets.iterator();
365
366 while (itr.hasNext()) {
367 Portlet portlet = itr.next();
368
369 SocialRequestInterpreter socialRequestInterpreter =
370 portlet.getSocialRequestInterpreterInstance();
371
372 if (portlet.isActive() && (socialRequestInterpreter != null)) {
373 socialRequestInterpreter = new SocialRequestInterpreterImpl(
374 portlet.getPortletId(), socialRequestInterpreter);
375
376 SocialRequestInterpreterLocalServiceUtil.
377 addRequestInterpreter(socialRequestInterpreter);
378 }
379 }
380 }
381 catch (Exception e) {
382 _log.error(e, e);
383 }
384
385
387 if (_log.isDebugEnabled()) {
388 _log.debug("Check web settings");
389 }
390
391 try {
392 String xml = HttpUtil.URLtoString(
393 servletContext.getResource("/WEB-INF/web.xml"));
394
395 checkWebSettings(xml);
396 }
397 catch (Exception e) {
398 _log.error(e, e);
399 }
400
401
403 if (_log.isDebugEnabled()) {
404 _log.debug("Last modified paths");
405 }
406
407 if (_lastModifiedPaths == null) {
408 _lastModifiedPaths = new HashSet<String>();
409
410 for (String lastModifiedPath : PropsValues.LAST_MODIFIED_PATHS) {
411 _lastModifiedPaths.add(lastModifiedPath);
412 }
413 }
414
415
417 if (_log.isDebugEnabled()) {
418 _log.debug("Process global startup events");
419 }
420
421 try {
422 EventsProcessor.process(
423 PropsKeys.GLOBAL_STARTUP_EVENTS,
424 PropsValues.GLOBAL_STARTUP_EVENTS);
425 }
426 catch (Exception e) {
427 _log.error(e, e);
428 }
429
430
432 String[] webIds = PortalInstances.getWebIds();
433
434 for (int i = 0; i < webIds.length; i++) {
435 PortalInstances.initCompany(servletContext, webIds[i]);
436 }
437
438
441 PortalInitableUtil.flushInitables();
442 HotDeployUtil.flushEvents();
443 }
444
445 public void callParentService(
446 HttpServletRequest request, HttpServletResponse response)
447 throws IOException, ServletException {
448
449 super.service(request, response);
450 }
451
452 public void service(
453 HttpServletRequest request, HttpServletResponse response)
454 throws IOException, ServletException {
455
456 if (_log.isDebugEnabled()) {
457 _log.debug("Process service request");
458 }
459
460 if (ShutdownUtil.isShutdown()) {
461 response.setContentType(ContentTypes.TEXT_HTML_UTF8);
462
463 String html = ContentUtil.get(
464 "com/liferay/portal/dependencies/shutdown.html");
465
466 response.getOutputStream().print(html);
467
468 return;
469 }
470
471 HttpSession session = request.getSession();
472
473
475 long companyId = PortalInstances.getCompanyId(request);
476
477
479
481 PortalUtil.setPortalPort(request);
482
483
485 ServletContext servletContext = getServletContext();
486
487 request.setAttribute(WebKeys.CTX, servletContext);
488
489
491 ModuleConfig moduleConfig = getModuleConfig(request);
492
493
495 if (PropsValues.LAST_MODIFIED_CHECK) {
496 String path = request.getPathInfo();
497
498 if ((path != null) && _lastModifiedPaths.contains(path)) {
499 ActionMapping mapping =
500 (ActionMapping)moduleConfig.findActionConfig(path);
501
502 LastModifiedAction lastModifiedAction =
503 (LastModifiedAction)InstancePool.get(mapping.getType());
504
505 String lmKey = lastModifiedAction.getLastModifiedKey(request);
506
507 if (lmKey != null) {
508 long ifModifiedSince =
509 request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
510
511 if (ifModifiedSince <= 0) {
512 lastModifiedAction.setLastModifiedValue(lmKey, lmKey);
513 }
514 else {
515 String lmValue =
516 lastModifiedAction.getLastModifiedValue(lmKey);
517
518 if (lmValue != null) {
519 response.setStatus(
520 HttpServletResponse.SC_NOT_MODIFIED);
521
522 return;
523 }
524 else {
525 lastModifiedAction.setLastModifiedValue(
526 lmKey, lmKey);
527 }
528 }
529 }
530 }
531 }
532
533
535 if (session.getAttribute(WebKeys.PORTLET_SESSION_TRACKER) == null ) {
536 session.setAttribute(
537 WebKeys.PORTLET_SESSION_TRACKER,
538 PortletSessionTracker.getInstance());
539 }
540
541
543 PortletRequestProcessor portletReqProcessor =
544 (PortletRequestProcessor)servletContext.getAttribute(
545 WebKeys.PORTLET_STRUTS_PROCESSOR);
546
547 if (portletReqProcessor == null) {
548 portletReqProcessor =
549 PortletRequestProcessor.getInstance(this, moduleConfig);
550
551 servletContext.setAttribute(
552 WebKeys.PORTLET_STRUTS_PROCESSOR, portletReqProcessor);
553 }
554
555
557 if (servletContext.getAttribute(
558 TilesUtilImpl.DEFINITIONS_FACTORY) == null) {
559
560 servletContext.setAttribute(
561 TilesUtilImpl.DEFINITIONS_FACTORY,
562 servletContext.getAttribute(TilesUtilImpl.DEFINITIONS_FACTORY));
563 }
564
565 Object applicationAssociate = servletContext.getAttribute(
566 WebKeys.ASSOCIATE_KEY);
567
568 if (servletContext.getAttribute(WebKeys.ASSOCIATE_KEY) == null) {
569 servletContext.setAttribute(
570 WebKeys.ASSOCIATE_KEY, applicationAssociate);
571 }
572
573
575 if (ParamUtil.get(request, WebKeys.ENCRYPT, false)) {
576 try {
577 Company company = CompanyLocalServiceUtil.getCompanyById(
578 companyId);
579
580 request = new EncryptedServletRequest(
581 request, company.getKeyObj());
582 }
583 catch (Exception e) {
584 }
585 }
586
587
589 PortalUtil.getCurrentURL(request);
590
591
593 long userId = PortalUtil.getUserId(request);
594 String remoteUser = request.getRemoteUser();
595
596
598 if (!PropsValues.PORTAL_JAAS_ENABLE) {
599 String jRemoteUser = (String)session.getAttribute("j_remoteuser");
600
601 if (jRemoteUser != null) {
602 remoteUser = jRemoteUser;
603
604 session.removeAttribute("j_remoteuser");
605 }
606 }
607
608 if ((userId > 0) && (remoteUser == null)) {
609 remoteUser = String.valueOf(userId);
610 }
611
612
618 request = new ProtectedServletRequest(request, remoteUser);
619
620 if ((userId > 0) || (remoteUser != null)) {
621
622
624 String name = String.valueOf(userId);
625
626 if (remoteUser != null) {
627 name = remoteUser;
628 }
629
630 PrincipalThreadLocal.setName(name);
631 }
632
633 if ((userId <= 0) && (remoteUser != null)) {
634 try {
635
636
638 userId = GetterUtil.getLong(remoteUser);
639
640
642 EventsProcessor.process(
643 PropsKeys.LOGIN_EVENTS_PRE, PropsValues.LOGIN_EVENTS_PRE,
644 request, response);
645
646
648 User user = UserLocalServiceUtil.getUserById(userId);
649
650 if (PropsValues.USERS_UPDATE_LAST_LOGIN) {
651 UserLocalServiceUtil.updateLastLogin(
652 userId, request.getRemoteAddr());
653 }
654
655
657 session.setAttribute(WebKeys.USER_ID, new Long(userId));
658
659
661 session.setAttribute(Globals.LOCALE_KEY, user.getLocale());
662
663
665 EventsProcessor.process(
666 PropsKeys.LOGIN_EVENTS_POST, PropsValues.LOGIN_EVENTS_POST,
667 request, response);
668 }
669 catch (Exception e) {
670 _log.error(e, e);
671 }
672 }
673
674
676 try {
677 EventsProcessor.process(
678 PropsKeys.SERVLET_SERVICE_EVENTS_PRE,
679 PropsValues.SERVLET_SERVICE_EVENTS_PRE, request, response);
680 }
681 catch (Exception e) {
682 Throwable cause = e.getCause();
683
684 if (cause instanceof NoSuchLayoutException) {
685 DynamicServletRequest dynamicRequest =
686 new DynamicServletRequest(request);
687
688
690 dynamicRequest.setParameter("p_l_id", StringPool.BLANK);
691
692 PortalUtil.sendError(
693 HttpServletResponse.SC_NOT_FOUND,
694 (NoSuchLayoutException)cause, dynamicRequest, response);
695
696 return;
697 }
698
699 _log.error(e, e);
700
701 request.setAttribute(PageContext.EXCEPTION, e);
702
703 StrutsUtil.forward(
704 PropsValues.SERVLET_SERVICE_EVENTS_PRE_ERROR_PAGE,
705 servletContext, request, response);
706
707 return;
708 }
709
710 try {
711
712
714 callParentService(request, response);
715 }
716 finally {
717
718
720 try {
721 EventsProcessor.process(
722 PropsKeys.SERVLET_SERVICE_EVENTS_POST,
723 PropsValues.SERVLET_SERVICE_EVENTS_POST, request, response);
724 }
725 catch (Exception e) {
726 _log.error(e, e);
727 }
728
729 response.addHeader(
730 _LIFERAY_PORTAL_REQUEST_HEADER, ReleaseInfo.getReleaseInfo());
731
732
734 CompanyThreadLocal.setCompanyId(0);
735
736
738 PrincipalThreadLocal.setName(null);
739 }
740 }
741
742 public void destroy() {
743 List<Portlet> portlets = PortletLocalServiceUtil.getPortlets();
744
745
747 if (_log.isDebugEnabled()) {
748 _log.debug("Scheduler");
749 }
750
751 try {
752 if (PropsValues.SCHEDULER_ENABLED) {
753 for (String className : PropsValues.SCHEDULER_CLASSES) {
754 Scheduler scheduler = (Scheduler)InstancePool.get(
755 className);
756
757 scheduler.unschedule();
758 }
759
760 Iterator<Portlet> itr = portlets.iterator();
761
762 while (itr.hasNext()) {
763 Portlet portlet = itr.next();
764
765 String className = portlet.getSchedulerClass();
766
767 if (portlet.isActive() && Validator.isNotNull(className)) {
768 Scheduler scheduler = (Scheduler)InstancePool.get(
769 className);
770
771 scheduler.unschedule();
772 }
773 }
774 }
775 }
776 catch (Exception e) {
777 _log.error(e, e);
778 }
779
780
782 try {
783 Iterator<Portlet> itr = portlets.iterator();
784
785 while (itr.hasNext()) {
786 Portlet portlet = itr.next();
787
788 PortletInstanceFactory.destroy(portlet);
789 }
790 }
791 catch (Exception e) {
792 _log.error(e, e);
793 }
794
795
797 long[] companyIds = PortalInstances.getCompanyIds();
798
799 for (int i = 0; i < companyIds.length; i++) {
800 destroyCompany(companyIds[i]);
801 }
802
803
805 if (_log.isDebugEnabled()) {
806 _log.debug("Process global shutdown events");
807 }
808
809 try {
810 EventsProcessor.process(
811 PropsKeys.GLOBAL_SHUTDOWN_EVENTS,
812 PropsValues.GLOBAL_SHUTDOWN_EVENTS);
813 }
814 catch (Exception e) {
815 _log.error(e, e);
816 }
817
818 super.destroy();
819 }
820
821 protected void checkWebSettings(String xml) throws DocumentException {
822 Document doc = DocumentUtil.readDocumentFromXML(xml);
823
824 Element root = doc.getRootElement();
825
826 int timeout = PropsValues.SESSION_TIMEOUT;
827
828 Element sessionConfig = root.element("session-config");
829
830 if (sessionConfig != null) {
831 String sessionTimeout = sessionConfig.elementText(
832 "session-timeout");
833
834 timeout = GetterUtil.getInteger(sessionTimeout, timeout);
835 }
836
837 PropsUtil.set(PropsKeys.SESSION_TIMEOUT, String.valueOf(timeout));
838
839 PropsValues.SESSION_TIMEOUT = timeout;
840 }
841
842 protected void destroyCompany(long companyId) {
843 if (_log.isDebugEnabled()) {
844 _log.debug("Process shutdown events");
845 }
846
847 try {
848 EventsProcessor.process(
849 PropsKeys.APPLICATION_SHUTDOWN_EVENTS,
850 PropsValues.APPLICATION_SHUTDOWN_EVENTS,
851 new String[] {String.valueOf(companyId)});
852 }
853 catch (Exception e) {
854 _log.error(e, e);
855 }
856 }
857
858 protected void initPortletApp(
859 Portlet portlet, ServletContext servletContext)
860 throws PortletException {
861
862 PortletApp portletApp = portlet.getPortletApp();
863
864 PortletConfig portletConfig = PortletConfigFactory.create(
865 portlet, servletContext);
866
867 PortletContext portletContext = portletConfig.getPortletContext();
868
869 Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
870
871 for (PortletFilter portletFilter : portletFilters) {
872 PortletFilterFactory.create(portletFilter, portletContext);
873 }
874
875 Set<PortletURLListener> portletURLListeners =
876 portletApp.getPortletURLListeners();
877
878 for (PortletURLListener portletURLListener : portletURLListeners) {
879 PortletURLListenerFactory.create(portletURLListener);
880 }
881 }
882
883 private static final String _LIFERAY_PORTAL_REQUEST_HEADER =
884 "Liferay-Portal";
885
886 private static Log _log = LogFactory.getLog(MainServlet.class);
887
888 private Set<String> _lastModifiedPaths;
889
890 }