001    /**
002     * Copyright (c) 2000-present 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.javadoc.JavadocManagerUtil;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.model.Portlet;
027    import com.liferay.portal.kernel.model.PortletApp;
028    import com.liferay.portal.kernel.model.PortletCategory;
029    import com.liferay.portal.kernel.model.PortletFilter;
030    import com.liferay.portal.kernel.model.PortletURLListener;
031    import com.liferay.portal.kernel.portlet.CustomUserAttributes;
032    import com.liferay.portal.kernel.portlet.InvokerPortlet;
033    import com.liferay.portal.kernel.portlet.PortletBag;
034    import com.liferay.portal.kernel.portlet.PortletBagPool;
035    import com.liferay.portal.kernel.portlet.PortletInstanceFactoryUtil;
036    import com.liferay.portal.kernel.security.permission.ResourceActionsUtil;
037    import com.liferay.portal.kernel.service.PortletLocalServiceUtil;
038    import com.liferay.portal.kernel.service.ResourceActionLocalServiceUtil;
039    import com.liferay.portal.kernel.servlet.DirectServletRegistryUtil;
040    import com.liferay.portal.kernel.servlet.FileTimestampUtil;
041    import com.liferay.portal.kernel.servlet.PortletServlet;
042    import com.liferay.portal.kernel.servlet.ServletContextPool;
043    import com.liferay.portal.kernel.servlet.ServletContextProvider;
044    import com.liferay.portal.kernel.util.ClassUtil;
045    import com.liferay.portal.kernel.util.GetterUtil;
046    import com.liferay.portal.kernel.util.HttpUtil;
047    import com.liferay.portal.kernel.util.Portal;
048    import com.liferay.portal.kernel.util.PropsKeys;
049    import com.liferay.portal.kernel.util.ResourceBundleLoader;
050    import com.liferay.portal.kernel.util.ResourceBundleUtil;
051    import com.liferay.portal.kernel.util.StringUtil;
052    import com.liferay.portal.kernel.util.Validator;
053    import com.liferay.portal.kernel.util.WebKeys;
054    import com.liferay.portal.util.PortalInstances;
055    import com.liferay.portal.util.WebAppPool;
056    import com.liferay.portlet.PortletContextBag;
057    import com.liferay.portlet.PortletContextBagPool;
058    import com.liferay.portlet.PortletFilterFactory;
059    import com.liferay.portlet.PortletURLListenerFactory;
060    import com.liferay.registry.Registry;
061    import com.liferay.registry.RegistryUtil;
062    import com.liferay.registry.ServiceRegistration;
063    
064    import java.util.HashMap;
065    import java.util.HashSet;
066    import java.util.Iterator;
067    import java.util.List;
068    import java.util.Map;
069    import java.util.Properties;
070    import java.util.Set;
071    
072    import javax.naming.Context;
073    import javax.naming.InitialContext;
074    import javax.naming.NamingException;
075    
076    import javax.portlet.PortletURLGenerationListener;
077    
078    import javax.servlet.ServletContext;
079    
080    import org.apache.portals.bridges.struts.StrutsPortlet;
081    
082    /**
083     * @author Brian Wing Shun Chan
084     * @author Brian Myunghun Kim
085     * @author Ivica Cardic
086     * @author Raymond Aug??
087     */
088    public class PortletHotDeployListener extends BaseHotDeployListener {
089    
090            @Override
091            public void invokeDeploy(HotDeployEvent hotDeployEvent)
092                    throws HotDeployException {
093    
094                    try {
095                            doInvokeDeploy(hotDeployEvent);
096                    }
097                    catch (Throwable t) {
098                            throwHotDeployException(
099                                    hotDeployEvent, "Error registering portlets for ", t);
100                    }
101            }
102    
103            @Override
104            public void invokeUndeploy(HotDeployEvent hotDeployEvent)
105                    throws HotDeployException {
106    
107                    try {
108                            doInvokeUndeploy(hotDeployEvent);
109                    }
110                    catch (Throwable t) {
111                            throwHotDeployException(
112                                    hotDeployEvent, "Error unregistering portlets for ", t);
113                    }
114            }
115    
116            protected void checkResourceBundles(
117                    ClassLoader classLoader, Portlet portlet) {
118    
119                    if (Validator.isNull(portlet.getResourceBundle())) {
120                            return;
121                    }
122    
123                    Registry registry = RegistryUtil.getRegistry();
124    
125                    ResourceBundleLoader resourceBundleLoader =
126                            ResourceBundleUtil.getResourceBundleLoader(
127                                    portlet.getResourceBundle(), classLoader);
128    
129                    Map<String, Object> properties = new HashMap<>();
130    
131                    properties.put(
132                            "resource.bundle.base.name", portlet.getResourceBundle());
133                    properties.put("service.ranking", Integer.MIN_VALUE);
134                    properties.put("servlet.context.name", portlet.getContextName());
135    
136                    _resourceBundleLoaderServiceRegistrations.put(
137                            portlet.getPortletId(),
138                            registry.registerService(
139                                    ResourceBundleLoader.class, resourceBundleLoader, properties));
140            }
141    
142            protected void destroyPortlet(Portlet portlet, Set<String> portletIds)
143                    throws Exception {
144    
145                    PortletApp portletApp = portlet.getPortletApp();
146    
147                    Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
148    
149                    for (PortletFilter portletFilter : portletFilters) {
150                            PortletFilterFactory.destroy(portletFilter);
151                    }
152    
153                    Set<PortletURLListener> portletURLListeners =
154                            portletApp.getPortletURLListeners();
155    
156                    for (PortletURLListener portletURLListener : portletURLListeners) {
157                            PortletURLListenerFactory.destroy(portletURLListener);
158                    }
159    
160                    PortletInstanceFactoryUtil.destroy(portlet);
161    
162                    portletIds.add(portlet.getPortletId());
163    
164                    ServiceRegistration<ResourceBundleLoader>
165                            resourceBundleLoaderServiceRegistration =
166                                    _resourceBundleLoaderServiceRegistrations.remove(
167                                            portlet.getPortletId());
168    
169                    if (resourceBundleLoaderServiceRegistration != null) {
170                            resourceBundleLoaderServiceRegistration.unregister();
171                    }
172            }
173    
174            protected void doInvokeDeploy(HotDeployEvent hotDeployEvent)
175                    throws Exception {
176    
177                    ServletContext servletContext = hotDeployEvent.getServletContext();
178    
179                    String servletContextName = servletContext.getServletContextName();
180    
181                    if (_log.isDebugEnabled()) {
182                            _log.debug("Invoking deploy for " + servletContextName);
183                    }
184    
185                    String[] xmls = new String[] {
186                            HttpUtil.URLtoString(
187                                    servletContext.getResource(
188                                            "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD)),
189                            HttpUtil.URLtoString(
190                                    servletContext.getResource(
191                                            "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_CUSTOM)),
192                            HttpUtil.URLtoString(
193                                    servletContext.getResource("/WEB-INF/liferay-portlet.xml")),
194                            HttpUtil.URLtoString(servletContext.getResource("/WEB-INF/web.xml"))
195                    };
196    
197                    if ((xmls[0] == null) && (xmls[1] == null)) {
198                            return;
199                    }
200    
201                    if (_log.isInfoEnabled()) {
202                            _log.info("Registering portlets for " + servletContextName);
203                    }
204    
205                    PortletContextBag portletContextBag = new PortletContextBag(
206                            servletContextName);
207    
208                    PortletContextBagPool.put(servletContextName, portletContextBag);
209    
210                    List<Portlet> portlets = PortletLocalServiceUtil.initWAR(
211                            servletContextName, servletContext, xmls,
212                            hotDeployEvent.getPluginPackage());
213    
214                    boolean portletAppInitialized = false;
215    
216                    boolean strutsBridges = false;
217    
218                    ClassLoader classLoader = hotDeployEvent.getContextClassLoader();
219    
220                    Iterator<Portlet> itr = portlets.iterator();
221    
222                    while (itr.hasNext()) {
223                            Portlet portlet = itr.next();
224    
225                            PortletBag portletBag = PortletBagPool.get(
226                                    portlet.getRootPortletId());
227    
228                            if (!portletAppInitialized) {
229                                    initPortletApp(
230                                            servletContextName, servletContext, classLoader, portlet);
231    
232                                    portletAppInitialized = true;
233                            }
234    
235                            javax.portlet.Portlet portletInstance =
236                                    portletBag.getPortletInstance();
237    
238                            if (ClassUtil.isSubclass(
239                                            portletInstance.getClass(),
240                                            StrutsPortlet.class.getName())) {
241    
242                                    strutsBridges = true;
243                            }
244                    }
245    
246                    if (!strutsBridges) {
247                            strutsBridges = GetterUtil.getBoolean(
248                                    servletContext.getInitParameter(
249                                            "struts-bridges-context-provider"));
250                    }
251    
252                    if (strutsBridges) {
253                            servletContext.setAttribute(
254                                    ServletContextProvider.STRUTS_BRIDGES_CONTEXT_PROVIDER,
255                                    new LiferayServletContextProvider());
256                    }
257    
258                    String xml = HttpUtil.URLtoString(
259                            servletContext.getResource("/WEB-INF/liferay-display.xml"));
260    
261                    PortletCategory newPortletCategory =
262                            PortletLocalServiceUtil.getWARDisplay(servletContextName, xml);
263    
264                    long[] companyIds = PortalInstances.getCompanyIds();
265    
266                    for (long companyId : companyIds) {
267                            PortletCategory portletCategory = (PortletCategory)WebAppPool.get(
268                                    companyId, WebKeys.PORTLET_CATEGORY);
269    
270                            if (portletCategory != null) {
271                                    portletCategory.merge(newPortletCategory);
272                            }
273                            else {
274                                    _log.error(
275                                            "Unable to register portlet for company " + companyId +
276                                                    " because it does not exist");
277                            }
278                    }
279    
280                    processPortletProperties(servletContextName, classLoader);
281    
282                    for (Portlet portlet : portlets) {
283                            List<String> modelNames =
284                                    ResourceActionsUtil.getPortletModelResources(
285                                            portlet.getPortletId());
286    
287                            List<String> portletActions =
288                                    ResourceActionsUtil.getPortletResourceActions(
289                                            portlet.getPortletId());
290    
291                            ResourceActionLocalServiceUtil.checkResourceActions(
292                                    portlet.getPortletId(), portletActions);
293    
294                            checkResourceBundles(classLoader, portlet);
295    
296                            for (String modelName : modelNames) {
297                                    List<String> modelActions =
298                                            ResourceActionsUtil.getModelResourceActions(modelName);
299    
300                                    ResourceActionLocalServiceUtil.checkResourceActions(
301                                            modelName, modelActions);
302                            }
303    
304                            for (long companyId : companyIds) {
305                                    Portlet curPortlet = PortletLocalServiceUtil.getPortletById(
306                                            companyId, portlet.getPortletId());
307    
308                                    PortletLocalServiceUtil.checkPortlet(curPortlet);
309                            }
310                    }
311    
312                    for (Portlet portlet : portlets) {
313                            boolean ready = GetterUtil.getBoolean(
314                                    servletContext.getInitParameter("portlets-ready-by-default"),
315                                    true);
316    
317                            portlet.setReady(ready);
318                    }
319    
320                    JavadocManagerUtil.load(servletContextName, classLoader);
321    
322                    DirectServletRegistryUtil.clearServlets();
323                    FileTimestampUtil.reset();
324    
325                    _portlets.put(servletContextName, portlets);
326    
327                    servletContext.setAttribute(WebKeys.PLUGIN_PORTLETS, portlets);
328    
329                    if (_log.isInfoEnabled()) {
330                            if (portlets.size() == 1) {
331                                    _log.info(
332                                            "1 portlet for " + servletContextName +
333                                                    " is available for use");
334                            }
335                            else {
336                                    _log.info(
337                                            portlets.size() + " portlets for " + servletContextName +
338                                                    " are available for use");
339                            }
340                    }
341            }
342    
343            protected void doInvokeUndeploy(HotDeployEvent hotDeployEvent)
344                    throws Exception {
345    
346                    ServletContext servletContext = hotDeployEvent.getServletContext();
347    
348                    String servletContextName = servletContext.getServletContextName();
349    
350                    if (_log.isDebugEnabled()) {
351                            _log.debug("Invoking undeploy for " + servletContextName);
352                    }
353    
354                    List<Portlet> portlets = _portlets.remove(servletContextName);
355    
356                    if (portlets == null) {
357                            return;
358                    }
359    
360                    Set<String> portletIds = new HashSet<>();
361    
362                    if (portlets != null) {
363                            if (_log.isInfoEnabled()) {
364                                    _log.info("Unregistering portlets for " + servletContextName);
365                            }
366    
367                            for (Portlet portlet : portlets) {
368                                    destroyPortlet(portlet, portletIds);
369                            }
370                    }
371    
372                    ServletContextPool.remove(servletContextName);
373    
374                    if (!portletIds.isEmpty()) {
375                            long[] companyIds = PortalInstances.getCompanyIds();
376    
377                            for (long companyId : companyIds) {
378                                    PortletCategory portletCategory =
379                                            (PortletCategory)WebAppPool.get(
380                                                    companyId, WebKeys.PORTLET_CATEGORY);
381    
382                                    portletCategory.separate(portletIds);
383                            }
384                    }
385    
386                    PortletContextBagPool.remove(servletContextName);
387    
388                    JavadocManagerUtil.unload(servletContextName);
389    
390                    DirectServletRegistryUtil.clearServlets();
391    
392                    if (_log.isInfoEnabled()) {
393                            if (portlets.size() == 1) {
394                                    _log.info(
395                                            "1 portlet for " + servletContextName +
396                                                    " was unregistered");
397                            }
398                            else {
399                                    _log.info(
400                                            portlets.size() + " portlets for " + servletContextName +
401                                                    " were unregistered");
402                            }
403                    }
404            }
405    
406            protected void initPortletApp(
407                            String servletContextName, ServletContext servletContext,
408                            ClassLoader classLoader, Portlet portlet)
409                    throws Exception {
410    
411                    PortletContextBag portletContextBag = PortletContextBagPool.get(
412                            servletContextName);
413    
414                    PortletApp portletApp = portlet.getPortletApp();
415    
416                    servletContext.setAttribute(PortletServlet.PORTLET_APP, portletApp);
417    
418                    Map<String, String> customUserAttributes =
419                            portletApp.getCustomUserAttributes();
420    
421                    for (Map.Entry<String, String> entry :
422                                    customUserAttributes.entrySet()) {
423    
424                            String attrCustomClass = entry.getValue();
425    
426                            Class<?> clazz = classLoader.loadClass(attrCustomClass);
427    
428                            CustomUserAttributes customUserAttributesInstance =
429                                    (CustomUserAttributes)clazz.newInstance();
430    
431                            portletContextBag.getCustomUserAttributes().put(
432                                    attrCustomClass, customUserAttributesInstance);
433                    }
434    
435                    Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
436    
437                    for (PortletFilter portletFilter : portletFilters) {
438                            javax.portlet.filter.PortletFilter portletFilterInstance =
439                                    (javax.portlet.filter.PortletFilter)newInstance(
440                                            classLoader,
441                                            new Class<?>[] {
442                                                    javax.portlet.filter.ActionFilter.class,
443                                                    javax.portlet.filter.EventFilter.class,
444                                                    javax.portlet.filter.PortletFilter.class,
445                                                    javax.portlet.filter.RenderFilter.class,
446                                                    javax.portlet.filter.ResourceFilter.class
447                                            },
448                                            portletFilter.getFilterClass());
449    
450                            portletContextBag.getPortletFilters().put(
451                                    portletFilter.getFilterName(), portletFilterInstance);
452                    }
453    
454                    InvokerPortlet invokerPortlet = PortletInstanceFactoryUtil.create(
455                            portlet, servletContext);
456    
457                    invokerPortlet.setPortletFilters();
458    
459                    Set<PortletURLListener> portletURLListeners =
460                            portletApp.getPortletURLListeners();
461    
462                    for (PortletURLListener portletURLListener : portletURLListeners) {
463                            PortletURLGenerationListener portletURLListenerInstance =
464                                    (PortletURLGenerationListener)newInstance(
465                                            classLoader, PortletURLGenerationListener.class,
466                                            portletURLListener.getListenerClass());
467    
468                            portletContextBag.getPortletURLListeners().put(
469                                    portletURLListener.getListenerClass(),
470                                    portletURLListenerInstance);
471    
472                            PortletURLListenerFactory.create(portletURLListener);
473                    }
474            }
475    
476            protected void processPortletProperties(
477                            String servletContextName, ClassLoader classLoader)
478                    throws Exception {
479    
480                    Configuration portletPropertiesConfiguration = null;
481    
482                    try {
483                            portletPropertiesConfiguration =
484                                    ConfigurationFactoryUtil.getConfiguration(
485                                            classLoader, "portlet");
486                    }
487                    catch (Exception e) {
488                            if (_log.isDebugEnabled()) {
489                                    _log.debug("Unable to read portlet.properties");
490                            }
491    
492                            return;
493                    }
494    
495                    Properties portletProperties =
496                            portletPropertiesConfiguration.getProperties();
497    
498                    if (portletProperties.isEmpty()) {
499                            return;
500                    }
501    
502                    String[] resourceActionConfigs = StringUtil.split(
503                            portletProperties.getProperty(PropsKeys.RESOURCE_ACTIONS_CONFIGS));
504    
505                    for (String resourceActionConfig : resourceActionConfigs) {
506                            ResourceActionsUtil.read(
507                                    servletContextName, classLoader, resourceActionConfig);
508                    }
509            }
510    
511            protected void unbindDataSource(String servletContextName) {
512                    Boolean dataSourceBindState = _dataSourceBindStates.remove(
513                            servletContextName);
514    
515                    if (dataSourceBindState == null) {
516                            return;
517                    }
518    
519                    try {
520                            if (_log.isDebugEnabled()) {
521                                    _log.debug("Dynamically unbinding the Liferay data source");
522                            }
523    
524                            Context context = new InitialContext();
525    
526                            try {
527                                    context.lookup(_JNDI_JDBC_LIFERAY_POOL);
528    
529                                    context.unbind(_JNDI_JDBC_LIFERAY_POOL);
530                            }
531                            catch (NamingException ne) {
532                            }
533    
534                            try {
535                                    context.lookup(_JNDI_JDBC);
536    
537                                    context.destroySubcontext(_JNDI_JDBC);
538                            }
539                            catch (NamingException ne) {
540                            }
541                    }
542                    catch (Exception e) {
543                            if (_log.isWarnEnabled()) {
544                                    _log.warn(
545                                            "Unable to dynamically unbind the Liferay data source: " +
546                                                    e.getMessage());
547                            }
548                    }
549            }
550    
551            private static final String _JNDI_JDBC = "java_liferay:jdbc";
552    
553            private static final String _JNDI_JDBC_LIFERAY_POOL =
554                    _JNDI_JDBC + "/LiferayPool";
555    
556            private static final Log _log = LogFactoryUtil.getLog(
557                    PortletHotDeployListener.class);
558    
559            private static final Map<String, Boolean> _dataSourceBindStates =
560                    new HashMap<>();
561            private static final Map<String, List<Portlet>> _portlets = new HashMap<>();
562    
563            private final Map<String, ServiceRegistration<ResourceBundleLoader>>
564                    _resourceBundleLoaderServiceRegistrations = new HashMap<>();
565    
566    }