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.scheduler.SchedulerEngineHelperUtil;
029    import com.liferay.portal.kernel.scheduler.SchedulerEntry;
030    import com.liferay.portal.kernel.scheduler.StorageType;
031    import com.liferay.portal.kernel.servlet.DirectServletRegistryUtil;
032    import com.liferay.portal.kernel.servlet.FileTimestampUtil;
033    import com.liferay.portal.kernel.servlet.PortletServlet;
034    import com.liferay.portal.kernel.servlet.ServletContextPool;
035    import com.liferay.portal.kernel.servlet.ServletContextProvider;
036    import com.liferay.portal.kernel.settings.SettingsFactoryUtil;
037    import com.liferay.portal.kernel.util.ClassUtil;
038    import com.liferay.portal.kernel.util.GetterUtil;
039    import com.liferay.portal.kernel.util.HttpUtil;
040    import com.liferay.portal.kernel.util.InfrastructureUtil;
041    import com.liferay.portal.kernel.util.LocaleUtil;
042    import com.liferay.portal.kernel.util.PropsKeys;
043    import com.liferay.portal.kernel.util.ServerDetector;
044    import com.liferay.portal.kernel.util.StringUtil;
045    import com.liferay.portal.kernel.util.Validator;
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.PropsValues;
057    import com.liferay.portal.util.WebAppPool;
058    import com.liferay.portal.util.WebKeys;
059    import com.liferay.portlet.CustomUserAttributes;
060    import com.liferay.portlet.InvokerPortlet;
061    import com.liferay.portlet.PortletBagFactory;
062    import com.liferay.portlet.PortletContextBag;
063    import com.liferay.portlet.PortletContextBagPool;
064    import com.liferay.portlet.PortletFilterFactory;
065    import com.liferay.portlet.PortletInstanceFactoryUtil;
066    import com.liferay.portlet.PortletResourceBundles;
067    import com.liferay.portlet.PortletURLListenerFactory;
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 destroyPortlet(Portlet portlet, Set<String> portletIds)
176                    throws Exception {
177    
178                    PortletApp portletApp = portlet.getPortletApp();
179    
180                    Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
181    
182                    for (PortletFilter portletFilter : portletFilters) {
183                            PortletFilterFactory.destroy(portletFilter);
184                    }
185    
186                    Set<PortletURLListener> portletURLListeners =
187                            portletApp.getPortletURLListeners();
188    
189                    for (PortletURLListener portletURLListener : portletURLListeners) {
190                            PortletURLListenerFactory.destroy(portletURLListener);
191                    }
192    
193                    if (PropsValues.SCHEDULER_ENABLED) {
194                            List<SchedulerEntry> schedulerEntries =
195                                    portlet.getSchedulerEntries();
196    
197                            if ((schedulerEntries != null) && !schedulerEntries.isEmpty()) {
198                                    for (SchedulerEntry schedulerEntry : schedulerEntries) {
199                                            SchedulerEngineHelperUtil.unschedule(
200                                                    schedulerEntry, StorageType.MEMORY_CLUSTERED);
201                                    }
202                            }
203                    }
204    
205                    PortletInstanceFactoryUtil.destroy(portlet);
206    
207                    portletIds.add(portlet.getPortletId());
208            }
209    
210            protected void doInvokeDeploy(HotDeployEvent hotDeployEvent)
211                    throws Exception {
212    
213                    ServletContext servletContext = hotDeployEvent.getServletContext();
214    
215                    String servletContextName = servletContext.getServletContextName();
216    
217                    if (_log.isDebugEnabled()) {
218                            _log.debug("Invoking deploy for " + servletContextName);
219                    }
220    
221                    String[] xmls = new String[] {
222                            HttpUtil.URLtoString(
223                                    servletContext.getResource(
224                                            "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD)),
225                            HttpUtil.URLtoString(
226                                    servletContext.getResource(
227                                            "/WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_CUSTOM)),
228                            HttpUtil.URLtoString(
229                                    servletContext.getResource("/WEB-INF/liferay-portlet.xml")),
230                            HttpUtil.URLtoString(servletContext.getResource("/WEB-INF/web.xml"))
231                    };
232    
233                    if ((xmls[0] == null) && (xmls[1] == null)) {
234                            return;
235                    }
236    
237                    if (_log.isInfoEnabled()) {
238                            _log.info("Registering portlets for " + servletContextName);
239                    }
240    
241                    List<Portlet> portlets = PortletLocalServiceUtil.initWAR(
242                            servletContextName, servletContext, xmls,
243                            hotDeployEvent.getPluginPackage());
244    
245                    boolean portletAppInitialized = false;
246    
247                    boolean phpPortlet = false;
248                    boolean strutsBridges = false;
249    
250                    PortletBagFactory portletBagFactory = new PortletBagFactory();
251    
252                    ClassLoader classLoader = hotDeployEvent.getContextClassLoader();
253    
254                    portletBagFactory.setClassLoader(classLoader);
255    
256                    portletBagFactory.setServletContext(servletContext);
257                    portletBagFactory.setWARFile(true);
258    
259                    Iterator<Portlet> itr = portlets.iterator();
260    
261                    while (itr.hasNext()) {
262                            Portlet portlet = itr.next();
263    
264                            PortletBag portletBag = portletBagFactory.create(portlet);
265    
266                            if (portletBag == null) {
267                                    itr.remove();
268                            }
269                            else {
270                                    if (!portletAppInitialized) {
271                                            initPortletApp(
272                                                    servletContextName, servletContext, classLoader,
273                                                    portlet);
274    
275                                            portletAppInitialized = true;
276                                    }
277    
278                                    javax.portlet.Portlet portletInstance =
279                                            portletBag.getPortletInstance();
280    
281                                    if (ClassUtil.isSubclass(
282                                                    portletInstance.getClass(),
283                                                    PHPPortlet.class.getName())) {
284    
285                                            phpPortlet = true;
286                                    }
287    
288                                    if (ClassUtil.isSubclass(
289                                                    portletInstance.getClass(),
290                                                    StrutsPortlet.class.getName())) {
291    
292                                            strutsBridges = true;
293                                    }
294                            }
295                    }
296    
297                    if (phpPortlet) {
298                            bindDataSource(servletContextName);
299                    }
300    
301                    if (!strutsBridges) {
302                            strutsBridges = GetterUtil.getBoolean(
303                                    servletContext.getInitParameter(
304                                            "struts-bridges-context-provider"));
305                    }
306    
307                    if (strutsBridges) {
308                            servletContext.setAttribute(
309                                    ServletContextProvider.STRUTS_BRIDGES_CONTEXT_PROVIDER,
310                                    new LiferayServletContextProvider());
311                    }
312    
313                    String xml = HttpUtil.URLtoString(
314                            servletContext.getResource("/WEB-INF/liferay-display.xml"));
315    
316                    PortletCategory newPortletCategory =
317                            PortletLocalServiceUtil.getWARDisplay(servletContextName, xml);
318    
319                    long[] companyIds = PortalInstances.getCompanyIds();
320    
321                    for (long companyId : companyIds) {
322                            PortletCategory portletCategory = (PortletCategory)WebAppPool.get(
323                                    companyId, WebKeys.PORTLET_CATEGORY);
324    
325                            if (portletCategory != null) {
326                                    portletCategory.merge(newPortletCategory);
327                            }
328                            else {
329                                    _log.error(
330                                            "Unable to register portlet for company " + companyId +
331                                                    " because it does not exist");
332                            }
333                    }
334    
335                    processPortletProperties(servletContextName, classLoader);
336    
337                    for (Portlet portlet : portlets) {
338                            List<String> modelNames =
339                                    ResourceActionsUtil.getPortletModelResources(
340                                            portlet.getPortletId());
341    
342                            List<String> portletActions =
343                                    ResourceActionsUtil.getPortletResourceActions(
344                                            portlet.getPortletId());
345    
346                            ResourceActionLocalServiceUtil.checkResourceActions(
347                                    portlet.getPortletId(), portletActions);
348    
349                            for (String modelName : modelNames) {
350                                    List<String> modelActions =
351                                            ResourceActionsUtil.getModelResourceActions(modelName);
352    
353                                    ResourceActionLocalServiceUtil.checkResourceActions(
354                                            modelName, modelActions);
355                            }
356    
357                            for (long companyId : companyIds) {
358                                    Portlet curPortlet = PortletLocalServiceUtil.getPortletById(
359                                            companyId, portlet.getPortletId());
360    
361                                    PortletLocalServiceUtil.checkPortlet(curPortlet);
362                            }
363                    }
364    
365                    for (Portlet portlet : portlets) {
366                            boolean ready = GetterUtil.getBoolean(
367                                    servletContext.getInitParameter("portlets-ready-by-default"),
368                                    true);
369    
370                            portlet.setReady(ready);
371                    }
372    
373                    registerClpMessageListeners(servletContext, classLoader);
374    
375                    JavadocManagerUtil.load(servletContextName, classLoader);
376    
377                    DirectServletRegistryUtil.clearServlets();
378                    FileTimestampUtil.reset();
379                    SettingsFactoryUtil.clearCache();
380    
381                    _portlets.put(servletContextName, portlets);
382    
383                    servletContext.setAttribute(WebKeys.PLUGIN_PORTLETS, portlets);
384    
385                    if (_log.isInfoEnabled()) {
386                            if (portlets.size() == 1) {
387                                    _log.info(
388                                            "1 portlet for " + servletContextName +
389                                                    " is available for use");
390                            }
391                            else {
392                                    _log.info(
393                                            portlets.size() + " portlets for " + servletContextName +
394                                                    " are available for use");
395                            }
396                    }
397            }
398    
399            protected void doInvokeUndeploy(HotDeployEvent hotDeployEvent)
400                    throws Exception {
401    
402                    ServletContext servletContext = hotDeployEvent.getServletContext();
403    
404                    String servletContextName = servletContext.getServletContextName();
405    
406                    if (_log.isDebugEnabled()) {
407                            _log.debug("Invoking undeploy for " + servletContextName);
408                    }
409    
410                    List<Portlet> portlets = _portlets.remove(servletContextName);
411    
412                    if (portlets == null) {
413                            return;
414                    }
415    
416                    Set<String> portletIds = new HashSet<String>();
417    
418                    if (portlets != null) {
419                            if (_log.isInfoEnabled()) {
420                                    _log.info("Unregistering portlets for " + servletContextName);
421                            }
422    
423                            for (Portlet portlet : portlets) {
424                                    destroyPortlet(portlet, portletIds);
425                            }
426                    }
427    
428                    ServletContextPool.remove(servletContextName);
429    
430                    if (!portletIds.isEmpty()) {
431                            long[] companyIds = PortalInstances.getCompanyIds();
432    
433                            for (long companyId : companyIds) {
434                                    PortletCategory portletCategory =
435                                            (PortletCategory)WebAppPool.get(
436                                                    companyId, WebKeys.PORTLET_CATEGORY);
437    
438                                    portletCategory.separate(portletIds);
439                            }
440                    }
441    
442                    PortletContextBagPool.remove(servletContextName);
443                    PortletResourceBundles.remove(servletContextName);
444    
445                    unbindDataSource(servletContextName);
446    
447                    unregisterClpMessageListeners(servletContext);
448    
449                    JavadocManagerUtil.unload(servletContextName);
450    
451                    DirectServletRegistryUtil.clearServlets();
452    
453                    if (_log.isInfoEnabled()) {
454                            if (portlets.size() == 1) {
455                                    _log.info(
456                                            "1 portlet for " + servletContextName +
457                                                    " was unregistered");
458                            }
459                            else {
460                                    _log.info(
461                                            portlets.size() + " portlets for " + servletContextName +
462                                                    " were unregistered");
463                            }
464                    }
465            }
466    
467            protected void initPortletApp(
468                            String servletContextName, ServletContext servletContext,
469                            ClassLoader classLoader, Portlet portlet)
470                    throws Exception {
471    
472                    PortletContextBag portletContextBag = new PortletContextBag(
473                            servletContextName);
474    
475                    PortletContextBagPool.put(servletContextName, portletContextBag);
476    
477                    PortletApp portletApp = portlet.getPortletApp();
478    
479                    servletContext.setAttribute(PortletServlet.PORTLET_APP, portletApp);
480    
481                    Map<String, String> customUserAttributes =
482                            portletApp.getCustomUserAttributes();
483    
484                    for (Map.Entry<String, String> entry :
485                                    customUserAttributes.entrySet()) {
486    
487                            String attrCustomClass = entry.getValue();
488    
489                            Class<?> clazz = classLoader.loadClass(attrCustomClass);
490    
491                            CustomUserAttributes customUserAttributesInstance =
492                                    (CustomUserAttributes)clazz.newInstance();
493    
494                            portletContextBag.getCustomUserAttributes().put(
495                                    attrCustomClass, customUserAttributesInstance);
496                    }
497    
498                    Set<PortletFilter> portletFilters = portletApp.getPortletFilters();
499    
500                    for (PortletFilter portletFilter : portletFilters) {
501                            javax.portlet.filter.PortletFilter portletFilterInstance =
502                                    (javax.portlet.filter.PortletFilter)newInstance(
503                                            classLoader,
504                                            new Class<?>[] {
505                                                    javax.portlet.filter.ActionFilter.class,
506                                                    javax.portlet.filter.EventFilter.class,
507                                                    javax.portlet.filter.PortletFilter.class,
508                                                    javax.portlet.filter.RenderFilter.class,
509                                                    javax.portlet.filter.ResourceFilter.class
510                                            },
511                                            portletFilter.getFilterClass());
512    
513                            portletContextBag.getPortletFilters().put(
514                                    portletFilter.getFilterName(), portletFilterInstance);
515                    }
516    
517                    InvokerPortlet invokerPortlet = PortletInstanceFactoryUtil.create(
518                            portlet, servletContext);
519    
520                    invokerPortlet.setPortletFilters();
521    
522                    Set<PortletURLListener> portletURLListeners =
523                            portletApp.getPortletURLListeners();
524    
525                    for (PortletURLListener portletURLListener : portletURLListeners) {
526                            PortletURLGenerationListener portletURLListenerInstance =
527                                    (PortletURLGenerationListener)newInstance(
528                                            classLoader, PortletURLGenerationListener.class,
529                                            portletURLListener.getListenerClass());
530    
531                            portletContextBag.getPortletURLListeners().put(
532                                    portletURLListener.getListenerClass(),
533                                    portletURLListenerInstance);
534    
535                            PortletURLListenerFactory.create(portletURLListener);
536                    }
537            }
538    
539            protected void processPortletProperties(
540                            String servletContextName, ClassLoader classLoader)
541                    throws Exception {
542    
543                    Configuration portletPropertiesConfiguration = null;
544    
545                    try {
546                            portletPropertiesConfiguration =
547                                    ConfigurationFactoryUtil.getConfiguration(
548                                            classLoader, "portlet");
549                    }
550                    catch (Exception e) {
551                            if (_log.isDebugEnabled()) {
552                                    _log.debug("Unable to read portlet.properties");
553                            }
554    
555                            return;
556                    }
557    
558                    Properties portletProperties =
559                            portletPropertiesConfiguration.getProperties();
560    
561                    if (portletProperties.isEmpty()) {
562                            return;
563                    }
564    
565                    String languageBundleName = portletProperties.getProperty(
566                            "language.bundle");
567    
568                    if (Validator.isNotNull(languageBundleName)) {
569                            Locale[] locales = LanguageUtil.getAvailableLocales();
570    
571                            for (Locale locale : locales) {
572                                    ResourceBundle resourceBundle = ResourceBundle.getBundle(
573                                            languageBundleName, locale, classLoader);
574    
575                                    PortletResourceBundles.put(
576                                            servletContextName, LocaleUtil.toLanguageId(locale),
577                                            resourceBundle);
578                            }
579                    }
580    
581                    String[] resourceActionConfigs = StringUtil.split(
582                            portletProperties.getProperty(PropsKeys.RESOURCE_ACTIONS_CONFIGS));
583    
584                    for (String resourceActionConfig : resourceActionConfigs) {
585                            ResourceActionsUtil.read(
586                                    servletContextName, classLoader, resourceActionConfig);
587                    }
588            }
589    
590            protected void unbindDataSource(String servletContextName) {
591                    Boolean dataSourceBindState = _dataSourceBindStates.remove(
592                            servletContextName);
593    
594                    if (dataSourceBindState == null) {
595                            return;
596                    }
597    
598                    try {
599                            if (_log.isDebugEnabled()) {
600                                    _log.debug("Dynamically unbinding the Liferay data source");
601                            }
602    
603                            Context context = new InitialContext();
604    
605                            try {
606                                    context.lookup(_JNDI_JDBC_LIFERAY_POOL);
607    
608                                    context.unbind(_JNDI_JDBC_LIFERAY_POOL);
609                            }
610                            catch (NamingException ne) {
611                            }
612    
613                            try {
614                                    context.lookup(_JNDI_JDBC);
615    
616                                    context.destroySubcontext(_JNDI_JDBC);
617                            }
618                            catch (NamingException ne) {
619                            }
620                    }
621                    catch (Exception e) {
622                            if (_log.isWarnEnabled()) {
623                                    _log.warn(
624                                            "Unable to dynamically unbind the Liferay data source: " +
625                                                    e.getMessage());
626                            }
627                    }
628            }
629    
630            private static final String _JNDI_JDBC = "java_liferay:jdbc";
631    
632            private static final String _JNDI_JDBC_LIFERAY_POOL =
633                    _JNDI_JDBC + "/LiferayPool";
634    
635            private static final Log _log = LogFactoryUtil.getLog(
636                    PortletHotDeployListener.class);
637    
638            private static final Map<String, Boolean> _dataSourceBindStates =
639                    new HashMap<String, Boolean>();
640            private static final Map<String, List<Portlet>> _portlets =
641                    new HashMap<String, List<Portlet>>();
642    
643    }