001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.deploy.hot;
016    
017    import com.liferay.portal.apache.bridges.struts.LiferayServletContextProvider;
018    import com.liferay.portal.kernel.configuration.Configuration;
019    import com.liferay.portal.kernel.configuration.ConfigurationFactoryUtil;
020    import com.liferay.portal.kernel.deploy.hot.BaseHotDeployListener;
021    import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
022    import com.liferay.portal.kernel.deploy.hot.HotDeployException;
023    import com.liferay.portal.kernel.language.LanguageUtil;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.portlet.PortletBag;
027    import com.liferay.portal.kernel.scheduler.SchedulerEngineUtil;
028    import com.liferay.portal.kernel.scheduler.SchedulerEntry;
029    import com.liferay.portal.kernel.search.Indexer;
030    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
031    import com.liferay.portal.kernel.servlet.PortletServlet;
032    import com.liferay.portal.kernel.servlet.ServletContextProvider;
033    import com.liferay.portal.kernel.util.ClassUtil;
034    import com.liferay.portal.kernel.util.GetterUtil;
035    import com.liferay.portal.kernel.util.HttpUtil;
036    import com.liferay.portal.kernel.util.LocaleUtil;
037    import com.liferay.portal.kernel.util.ObjectValuePair;
038    import com.liferay.portal.kernel.util.StringUtil;
039    import com.liferay.portal.kernel.util.Validator;
040    import com.liferay.portal.kernel.webdav.WebDAVUtil;
041    import com.liferay.portal.kernel.workflow.WorkflowHandler;
042    import com.liferay.portal.kernel.workflow.WorkflowHandlerRegistryUtil;
043    import com.liferay.portal.model.Portlet;
044    import com.liferay.portal.model.PortletApp;
045    import com.liferay.portal.model.PortletCategory;
046    import com.liferay.portal.model.PortletFilter;
047    import com.liferay.portal.model.PortletURLListener;
048    import com.liferay.portal.poller.PollerProcessorUtil;
049    import com.liferay.portal.pop.POPServerUtil;
050    import com.liferay.portal.security.permission.ResourceActionsUtil;
051    import com.liferay.portal.service.PortletLocalServiceUtil;
052    import com.liferay.portal.service.ResourceActionLocalServiceUtil;
053    import com.liferay.portal.service.ResourceCodeLocalServiceUtil;
054    import com.liferay.portal.util.Portal;
055    import com.liferay.portal.util.PortalInstances;
056    import com.liferay.portal.util.PropsValues;
057    import com.liferay.portal.util.WebAppPool;
058    import com.liferay.portal.util.WebKeys;
059    import com.liferay.portal.xmlrpc.XmlRpcServlet;
060    import com.liferay.portlet.CustomUserAttributes;
061    import com.liferay.portlet.PortletBagFactory;
062    import com.liferay.portlet.PortletConfigFactoryUtil;
063    import com.liferay.portlet.PortletContextBag;
064    import com.liferay.portlet.PortletContextBagPool;
065    import com.liferay.portlet.PortletFilterFactory;
066    import com.liferay.portlet.PortletInstanceFactoryUtil;
067    import com.liferay.portlet.PortletResourceBundles;
068    import com.liferay.portlet.PortletURLListenerFactory;
069    import com.liferay.portlet.asset.AssetRendererFactoryRegistryUtil;
070    import com.liferay.portlet.asset.model.AssetRendererFactory;
071    import com.liferay.portlet.social.service.SocialActivityInterpreterLocalServiceUtil;
072    import com.liferay.portlet.social.service.SocialRequestInterpreterLocalServiceUtil;
073    import com.liferay.util.log4j.Log4JUtil;
074    
075    import java.util.HashMap;
076    import java.util.HashSet;
077    import java.util.Iterator;
078    import java.util.List;
079    import java.util.Locale;
080    import java.util.Map;
081    import java.util.Properties;
082    import java.util.ResourceBundle;
083    import java.util.Set;
084    
085    import javax.portlet.PortletConfig;
086    import javax.portlet.PortletContext;
087    import javax.portlet.PortletURLGenerationListener;
088    
089    import javax.servlet.ServletContext;
090    
091    import org.apache.portals.bridges.struts.StrutsPortlet;
092    
093    /**
094     * @author Brian Wing Shun Chan
095     * @author Brian Myunghun Kim
096     * @author Ivica Cardic
097     * @author Raymond Augé
098     */
099    public class PortletHotDeployListener extends BaseHotDeployListener {
100    
101            public void invokeDeploy(HotDeployEvent event) throws HotDeployException {
102                    try {
103                            doInvokeDeploy(event);
104                    }
105                    catch (Throwable t) {
106                            throwHotDeployException(
107                                    event, "Error registering portlets for ", t);
108                    }
109            }
110    
111            public void invokeUndeploy(HotDeployEvent event) throws HotDeployException {
112                    try {
113                            doInvokeUndeploy(event);
114                    }
115                    catch (Throwable t) {
116                            throwHotDeployException(
117                                    event, "Error unregistering portlets for ", t);
118                    }
119            }
120    
121            protected void destroyPortlet(Portlet portlet, Set<String> portletIds)
122                    throws Exception {
123    
124                    PortletApp portletApp = portlet.getPortletApp();
125    
126                    Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
127    
128                    for (PortletFilter portletFilter : portletFilters) {
129                            PortletFilterFactory.destroy(portletFilter);
130                    }
131    
132                    Set<PortletURLListener> portletURLListeners =
133                            portletApp.getPortletURLListeners();
134    
135                    for (PortletURLListener portletURLListener : portletURLListeners) {
136                            PortletURLListenerFactory.destroy(portletURLListener);
137                    }
138    
139                    Indexer indexer = portlet.getIndexerInstance();
140    
141                    if (indexer != null) {
142                            IndexerRegistryUtil.unregister(indexer);
143                    }
144    
145                    if (PropsValues.SCHEDULER_ENABLED){
146                            List<SchedulerEntry> schedulerEntries =
147                                    portlet.getSchedulerEntries();
148    
149                            if ((schedulerEntries != null) && !schedulerEntries.isEmpty()) {
150                                    for (SchedulerEntry schedulerEntry : schedulerEntries) {
151                                            SchedulerEngineUtil.unschedule(schedulerEntry);
152                                    }
153                            }
154                    }
155    
156                    PollerProcessorUtil.deletePollerProcessor(portlet.getPortletId());
157    
158                    POPServerUtil.deleteListener(portlet.getPopMessageListenerInstance());
159    
160                    SocialActivityInterpreterLocalServiceUtil.deleteActivityInterpreter(
161                            portlet.getSocialActivityInterpreterInstance());
162    
163                    SocialRequestInterpreterLocalServiceUtil.deleteRequestInterpreter(
164                            portlet.getSocialRequestInterpreterInstance());
165    
166                    WebDAVUtil.deleteStorage(portlet.getWebDAVStorageInstance());
167    
168                    XmlRpcServlet.unregisterMethod(portlet.getXmlRpcMethodInstance());
169    
170                    List<AssetRendererFactory> assetRendererFactories =
171                            portlet.getAssetRendererFactoryInstances();
172    
173                    if (assetRendererFactories != null) {
174                            AssetRendererFactoryRegistryUtil.unregister(assetRendererFactories);
175                    }
176    
177                    List<WorkflowHandler> workflowHandlers =
178                            portlet.getWorkflowHandlerInstances();
179    
180                    if (workflowHandlers != null) {
181                            WorkflowHandlerRegistryUtil.unregister(workflowHandlers);
182                    }
183    
184                    PortletInstanceFactoryUtil.destroy(portlet);
185    
186                    portletIds.add(portlet.getPortletId());
187            }
188    
189            protected void doInvokeDeploy(HotDeployEvent event) throws Exception {
190    
191                    // Servlet context
192    
193                    ServletContext servletContext = event.getServletContext();
194    
195                    String servletContextName = servletContext.getServletContextName();
196    
197                    if (_log.isDebugEnabled()) {
198                            _log.debug("Invoking deploy for " + servletContextName);
199                    }
200    
201                    // Company ids
202    
203                    long[] companyIds = PortalInstances.getCompanyIds();
204    
205                    // Initialize portlets
206    
207                    String[] xmls = new String[] {
208                            HttpUtil.URLtoString(servletContext.getResource(
209                                    "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD)),
210                            HttpUtil.URLtoString(servletContext.getResource(
211                                    "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_CUSTOM)),
212                            HttpUtil.URLtoString(servletContext.getResource(
213                                    "/WEB-INF/liferay-portlet.xml")),
214                            HttpUtil.URLtoString(servletContext.getResource("/WEB-INF/web.xml"))
215                    };
216    
217                    if (xmls[0] == null) {
218                            return;
219                    }
220    
221                    if (_log.isInfoEnabled()) {
222                            _log.info("Registering portlets for " + servletContextName);
223                    }
224    
225                    List<Portlet> portlets = PortletLocalServiceUtil.initWAR(
226                            servletContextName, servletContext, xmls, event.getPluginPackage());
227    
228                    // Class loader
229    
230                    ClassLoader portletClassLoader = event.getContextClassLoader();
231    
232                    servletContext.setAttribute(
233                            PortletServlet.PORTLET_CLASS_LOADER, portletClassLoader);
234    
235                    // Logger
236    
237                    initLogger(portletClassLoader);
238    
239                    // Portlet context wrapper
240    
241                    _portletAppInitialized = false;
242                    _strutsBridges = false;
243    
244                    PortletBagFactory portletBagFactory = new PortletBagFactory();
245    
246                    portletBagFactory.setClassLoader(portletClassLoader);
247                    portletBagFactory.setServletContext(servletContext);
248                    portletBagFactory.setWARFile(true);
249    
250                    Iterator<Portlet> itr = portlets.iterator();
251    
252                    while (itr.hasNext()) {
253                            Portlet portlet = itr.next();
254    
255                            PortletBag portletBag = initPortlet(portlet, portletBagFactory);
256    
257                            if (portletBag == null) {
258                                    itr.remove();
259                            }
260                            else {
261                                    if (!_portletAppInitialized) {
262                                            initPortletApp(
263                                                    portlet, servletContextName, servletContext,
264                                                    portletClassLoader);
265    
266                                            _portletAppInitialized = true;
267                                    }
268                            }
269                    }
270    
271                    // Struts bridges
272    
273                    if (!_strutsBridges) {
274                            _strutsBridges = GetterUtil.getBoolean(
275                                    servletContext.getInitParameter(
276                                            "struts-bridges-context-provider"));
277                    }
278    
279                    if (_strutsBridges) {
280                            servletContext.setAttribute(
281                                    ServletContextProvider.STRUTS_BRIDGES_CONTEXT_PROVIDER,
282                                    new LiferayServletContextProvider());
283                    }
284    
285                    // Portlet display
286    
287                    String xml = HttpUtil.URLtoString(servletContext.getResource(
288                            "/WEB-INF/liferay-display.xml"));
289    
290                    PortletCategory newPortletCategory =
291                            PortletLocalServiceUtil.getWARDisplay(servletContextName, xml);
292    
293                    for (int i = 0; i < companyIds.length; i++) {
294                            long companyId = companyIds[i];
295    
296                            PortletCategory portletCategory =
297                                    (PortletCategory)WebAppPool.get(
298                                            String.valueOf(companyId), WebKeys.PORTLET_CATEGORY);
299    
300                            if (portletCategory != null) {
301                                    portletCategory.merge(newPortletCategory);
302                            }
303                            else {
304                                    _log.error(
305                                            "Unable to register portlet for company " + companyId +
306                                                    " because it does not exist");
307                            }
308                    }
309    
310                    // Portlet properties
311    
312                    processPortletProperties(servletContextName, portletClassLoader);
313    
314                    // Resource actions, resource codes, and check
315    
316                    itr = portlets.iterator();
317    
318                    while (itr.hasNext()) {
319                            Portlet portlet = itr.next();
320    
321                            List<String> modelNames =
322                                    ResourceActionsUtil.getPortletModelResources(
323                                            portlet.getPortletId());
324    
325                            for (long companyId : companyIds) {
326                                    ResourceCodeLocalServiceUtil.checkResourceCodes(
327                                            companyId, portlet.getPortletId());
328    
329                                    for (String modelName : modelNames) {
330                                            ResourceCodeLocalServiceUtil.checkResourceCodes(
331                                                    companyId, modelName);
332                                    }
333                            }
334    
335                            List<String> portletActions =
336                                    ResourceActionsUtil.getPortletResourceActions(
337                                            portlet.getPortletId());
338    
339                            ResourceActionLocalServiceUtil.checkResourceActions(
340                                    portlet.getPortletId(), portletActions);
341    
342                            for (String modelName : modelNames) {
343                                    List<String> modelActions =
344                                            ResourceActionsUtil.getModelResourceActions(modelName);
345    
346                                    ResourceActionLocalServiceUtil.checkResourceActions(
347                                            modelName, modelActions);
348                            }
349    
350                            for (long companyId : companyIds) {
351                                    Portlet curPortlet = PortletLocalServiceUtil.getPortletById(
352                                            companyId, portlet.getPortletId());
353    
354                                    PortletLocalServiceUtil.checkPortlet(curPortlet);
355                            }
356                    }
357    
358                    // ClpMessageListener
359    
360                    registerClpMessageListeners(servletContext, portletClassLoader);
361    
362                    // Variables
363    
364                    _vars.put(
365                            servletContextName,
366                            new ObjectValuePair<long[], List<Portlet>>(
367                                    companyIds, portlets));
368    
369                    if (_log.isInfoEnabled()) {
370                            if (portlets.size() == 1) {
371                                    _log.info(
372                                            "1 portlet for " + servletContextName +
373                                                    " is available for use");
374                            }
375                            else {
376                                    _log.info(
377                                            portlets.size() + " portlets for " + servletContextName +
378                                                    " are available for use");
379                            }
380                    }
381            }
382    
383            protected void doInvokeUndeploy(HotDeployEvent event) throws Exception {
384                    ServletContext servletContext = event.getServletContext();
385    
386                    String servletContextName = servletContext.getServletContextName();
387    
388                    if (_log.isDebugEnabled()) {
389                            _log.debug("Invoking undeploy for " + servletContextName);
390                    }
391    
392                    ObjectValuePair<long[], List<Portlet>> ovp =
393                            _vars.remove(servletContextName);
394    
395                    if (ovp == null) {
396                            return;
397                    }
398    
399                    long[] companyIds = ovp.getKey();
400                    List<Portlet> portlets = ovp.getValue();
401    
402                    Set<String> portletIds = new HashSet<String>();
403    
404                    if (portlets != null) {
405                            if (_log.isInfoEnabled()) {
406                                    _log.info(
407                                            "Unregistering portlets for " + servletContextName);
408                            }
409    
410                            Iterator<Portlet> itr = portlets.iterator();
411    
412                            while (itr.hasNext()) {
413                                    Portlet portlet = itr.next();
414    
415                                    destroyPortlet(portlet, portletIds);
416                            }
417                    }
418    
419                    if (portletIds.size() > 0) {
420                            for (int i = 0; i < companyIds.length; i++) {
421                                    long companyId = companyIds[i];
422    
423                                    PortletCategory portletCategory =
424                                            (PortletCategory)WebAppPool.get(
425                                                    String.valueOf(companyId), WebKeys.PORTLET_CATEGORY);
426    
427                                    portletCategory.separate(portletIds);
428                            }
429                    }
430    
431                    PortletResourceBundles.remove(servletContextName);
432    
433                    unregisterClpMessageListeners(servletContext);
434    
435                    if (_log.isInfoEnabled()) {
436                            if (portlets.size() == 1) {
437                                    _log.info(
438                                            "1 portlet for " + servletContextName +
439                                                    " was unregistered");
440                            }
441                            else {
442                                    _log.info(
443                                            portlets.size() + " portlets for " + servletContextName +
444                                                    " was unregistered");
445                            }
446                    }
447            }
448    
449            protected void initLogger(ClassLoader portletClassLoader) {
450                    Log4JUtil.configureLog4J(
451                            portletClassLoader.getResource("META-INF/portal-log4j.xml"));
452            }
453    
454            protected PortletBag initPortlet(
455                            Portlet portlet, PortletBagFactory portletBagFactory)
456                    throws Exception {
457    
458                    PortletBag portletBag = portletBagFactory.create(portlet);
459    
460                    if (portletBag == null) {
461                            return null;
462                    }
463    
464                    javax.portlet.Portlet portletInstance = portletBag.getPortletInstance();
465    
466                    if (ClassUtil.isSubclass(
467                                    portletInstance.getClass(), StrutsPortlet.class.getName())) {
468    
469                            _strutsBridges = true;
470                    }
471    
472                    return portletBag;
473            }
474    
475            protected void initPortletApp(
476                            Portlet portlet, String servletContextName,
477                            ServletContext servletContext, ClassLoader portletClassLoader)
478                    throws Exception {
479    
480                    PortletConfig portletConfig = PortletConfigFactoryUtil.create(
481                            portlet, servletContext);
482    
483                    PortletContext portletContext = portletConfig.getPortletContext();
484    
485                    PortletContextBag portletContextBag = new PortletContextBag(
486                            servletContextName);
487    
488                    PortletContextBagPool.put(servletContextName, portletContextBag);
489    
490                    PortletApp portletApp = portlet.getPortletApp();
491    
492                    servletContext.setAttribute(PortletServlet.PORTLET_APP, portletApp);
493    
494                    Map<String, String> customUserAttributes =
495                            portletApp.getCustomUserAttributes();
496    
497                    for (Map.Entry<String, String> entry :
498                                    customUserAttributes.entrySet()) {
499    
500                            String attrCustomClass = entry.getValue();
501    
502                            CustomUserAttributes customUserAttributesInstance =
503                                    (CustomUserAttributes)portletClassLoader.loadClass(
504                                            attrCustomClass).newInstance();
505    
506                            portletContextBag.getCustomUserAttributes().put(
507                                    attrCustomClass, customUserAttributesInstance);
508                    }
509    
510                    Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
511    
512                    for (PortletFilter portletFilter : portletFilters) {
513                            javax.portlet.filter.PortletFilter portletFilterInstance =
514                                    (javax.portlet.filter.PortletFilter)newInstance(
515                                            portletClassLoader,
516                                            new Class<?>[] {
517                                                    javax.portlet.filter.ActionFilter.class,
518                                                    javax.portlet.filter.EventFilter.class,
519                                                    javax.portlet.filter.PortletFilter.class,
520                                                    javax.portlet.filter.RenderFilter.class,
521                                                    javax.portlet.filter.ResourceFilter.class
522                                            },
523                                            portletFilter.getFilterClass());
524    
525                            portletContextBag.getPortletFilters().put(
526                                    portletFilter.getFilterName(), portletFilterInstance);
527    
528                            PortletFilterFactory.create(portletFilter, portletContext);
529                    }
530    
531                    Set<PortletURLListener> portletURLListeners =
532                            portletApp.getPortletURLListeners();
533    
534                    for (PortletURLListener portletURLListener : portletURLListeners) {
535                            PortletURLGenerationListener portletURLListenerInstance =
536                                    (PortletURLGenerationListener)newInstance(
537                                            portletClassLoader, PortletURLGenerationListener.class,
538                                            portletURLListener.getListenerClass());
539    
540                            portletContextBag.getPortletURLListeners().put(
541                                    portletURLListener.getListenerClass(),
542                                    portletURLListenerInstance);
543    
544                            PortletURLListenerFactory.create(portletURLListener);
545                    }
546            }
547    
548            protected void processPortletProperties(
549                            String servletContextName, ClassLoader portletClassLoader)
550                    throws Exception {
551    
552                    Configuration portletPropertiesConfiguration = null;
553    
554                    try {
555                            portletPropertiesConfiguration =
556                                    ConfigurationFactoryUtil.getConfiguration(
557                                            portletClassLoader, "portlet");
558                    }
559                    catch (Exception e) {
560                            if (_log.isDebugEnabled()) {
561                                    _log.debug("Unable to read portlet.properties");
562                            }
563    
564                            return;
565                    }
566    
567                    Properties portletProperties =
568                            portletPropertiesConfiguration.getProperties();
569    
570                    if (portletProperties.size() == 0) {
571                            return;
572                    }
573    
574                    String languageBundleName = portletProperties.getProperty(
575                            "language.bundle");
576    
577                    if (Validator.isNotNull(languageBundleName)) {
578                            Locale[] locales = LanguageUtil.getAvailableLocales();
579    
580                            for (int i = 0; i < locales.length; i++) {
581                                    ResourceBundle resourceBundle = ResourceBundle.getBundle(
582                                            languageBundleName, locales[i], portletClassLoader);
583    
584                                    PortletResourceBundles.put(
585                                            servletContextName, LocaleUtil.toLanguageId(locales[i]),
586                                            resourceBundle);
587                            }
588                    }
589    
590                    String[] resourceActionConfigs = StringUtil.split(
591                            portletProperties.getProperty("resource.actions.configs"));
592    
593                    for (int i = 0; i < resourceActionConfigs.length; i++) {
594                            ResourceActionsUtil.read(
595                                    servletContextName, portletClassLoader,
596                                    resourceActionConfigs[i]);
597                    }
598            }
599    
600            private static Log _log = LogFactoryUtil.getLog(
601                    PortletHotDeployListener.class);
602    
603            private static Map<String, ObjectValuePair<long[], List<Portlet>>> _vars =
604                    new HashMap<String, ObjectValuePair<long[], List<Portlet>>>();
605    
606            private boolean _portletAppInitialized;
607            private boolean _strutsBridges;
608    
609    }