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