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