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