1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.deploy.hot;
24  
25  import com.liferay.portal.apache.bridges.struts.LiferayServletContextProvider;
26  import com.liferay.portal.job.Scheduler;
27  import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
28  import com.liferay.portal.kernel.deploy.hot.HotDeployException;
29  import com.liferay.portal.kernel.deploy.hot.HotDeployListener;
30  import com.liferay.portal.kernel.language.LanguageUtil;
31  import com.liferay.portal.kernel.lar.PortletDataHandler;
32  import com.liferay.portal.kernel.pop.MessageListener;
33  import com.liferay.portal.kernel.portlet.ConfigurationAction;
34  import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
35  import com.liferay.portal.kernel.portlet.PortletLayoutListener;
36  import com.liferay.portal.kernel.search.Indexer;
37  import com.liferay.portal.kernel.servlet.PortletServlet;
38  import com.liferay.portal.kernel.servlet.ServletContextProvider;
39  import com.liferay.portal.kernel.servlet.URLEncoder;
40  import com.liferay.portal.kernel.util.ClassUtil;
41  import com.liferay.portal.kernel.util.GetterUtil;
42  import com.liferay.portal.kernel.util.LocaleUtil;
43  import com.liferay.portal.kernel.util.ObjectValuePair;
44  import com.liferay.portal.kernel.util.PropertiesUtil;
45  import com.liferay.portal.kernel.util.StringUtil;
46  import com.liferay.portal.kernel.util.Validator;
47  import com.liferay.portal.model.ActivityTrackerInterpreter;
48  import com.liferay.portal.model.Portlet;
49  import com.liferay.portal.model.PortletCategory;
50  import com.liferay.portal.model.impl.ActivityTrackerInterpreterImpl;
51  import com.liferay.portal.pop.POPServerUtil;
52  import com.liferay.portal.security.permission.ResourceActionsUtil;
53  import com.liferay.portal.service.PortletLocalServiceUtil;
54  import com.liferay.portal.service.ServiceComponentLocalServiceUtil;
55  import com.liferay.portal.servlet.PortletContextPool;
56  import com.liferay.portal.servlet.PortletContextWrapper;
57  import com.liferay.portal.util.ActivityTrackerInterpreterUtil;
58  import com.liferay.portal.util.PortalInstances;
59  import com.liferay.portal.util.PortalUtil;
60  import com.liferay.portal.util.PropsValues;
61  import com.liferay.portal.util.WebAppPool;
62  import com.liferay.portal.util.WebKeys;
63  import com.liferay.portlet.PortletInstanceFactory;
64  import com.liferay.portlet.PortletPreferencesSerializer;
65  import com.liferay.portlet.PortletResourceBundles;
66  import com.liferay.util.CollectionFactory;
67  import com.liferay.util.Http;
68  
69  import java.util.HashSet;
70  import java.util.Iterator;
71  import java.util.List;
72  import java.util.Locale;
73  import java.util.Map;
74  import java.util.MissingResourceException;
75  import java.util.Properties;
76  import java.util.ResourceBundle;
77  import java.util.Set;
78  
79  import javax.portlet.PreferencesValidator;
80  
81  import javax.servlet.ServletContext;
82  
83  import org.apache.commons.logging.Log;
84  import org.apache.commons.logging.LogFactory;
85  import org.apache.portals.bridges.struts.StrutsPortlet;
86  
87  /**
88   * <a href="PortletHotDeployListener.java.html"><b><i>View Source</i></b></a>
89   *
90   * @author Brian Wing Shun Chan
91   * @author Brian Myunghun Kim
92   * @author Ivica Cardic
93   *
94   */
95  public class PortletHotDeployListener implements HotDeployListener {
96  
97      public void invokeDeploy(HotDeployEvent event) throws HotDeployException {
98          String servletContextName = null;
99  
100         try {
101 
102             // Servlet context
103 
104             ServletContext ctx = event.getServletContext();
105 
106             servletContextName = ctx.getServletContextName();
107 
108             if (_log.isDebugEnabled()) {
109                 _log.debug("Invoking deploy for " + servletContextName);
110             }
111 
112             // Company ids
113 
114             long[] companyIds = PortalInstances.getCompanyIds();
115 
116             // Initialize portlets
117 
118             String[] xmls = new String[] {
119                 Http.URLtoString(ctx.getResource(
120                     "/WEB-INF/" + PortalUtil.PORTLET_XML_FILE_NAME_STANDARD)),
121                 Http.URLtoString(ctx.getResource(
122                     "/WEB-INF/" + PortalUtil.PORTLET_XML_FILE_NAME_CUSTOM)),
123                 Http.URLtoString(ctx.getResource(
124                     "/WEB-INF/liferay-portlet.xml")),
125                 Http.URLtoString(ctx.getResource("/WEB-INF/web.xml"))
126             };
127 
128             if (xmls[0] == null) {
129                 return;
130             }
131 
132             if (_log.isInfoEnabled()) {
133                 _log.info("Registering portlets for " + servletContextName);
134             }
135 
136             List portlets = PortletLocalServiceUtil.initWAR(
137                 servletContextName, xmls, event.getPluginPackage());
138 
139             // Class loader
140 
141             ClassLoader portletClassLoader = event.getContextClassLoader();
142 
143             ctx.setAttribute(
144                 PortletServlet.PORTLET_CLASS_LOADER, portletClassLoader);
145 
146             // Portlet context wrapper
147 
148             boolean strutsBridges = false;
149 
150             Iterator itr1 = portlets.iterator();
151 
152             while (itr1.hasNext()) {
153                 Portlet portlet = (Portlet)itr1.next();
154 
155                 Class portletClass = portletClassLoader.loadClass(
156                     portlet.getPortletClass());
157 
158                 javax.portlet.Portlet portletInstance =
159                     (javax.portlet.Portlet)portletClass.newInstance();
160 
161                 if (ClassUtil.isSubclass(portletClass,
162                     StrutsPortlet.class.getName())) {
163 
164                     strutsBridges = true;
165                 }
166 
167                 ConfigurationAction configurationActionInstance = null;
168 
169                 if (Validator.isNotNull(
170                         portlet.getConfigurationActionClass())) {
171 
172                     configurationActionInstance =
173                         (ConfigurationAction)portletClassLoader.loadClass(
174                             portlet.getConfigurationActionClass()).
175                                 newInstance();
176                 }
177 
178                 Indexer indexerInstance = null;
179 
180                 if (Validator.isNotNull(portlet.getIndexerClass())) {
181                     indexerInstance = (Indexer)portletClassLoader.loadClass(
182                         portlet.getIndexerClass()).newInstance();
183                 }
184 
185                 Scheduler schedulerInstance = null;
186 
187                 if (Validator.isNotNull(portlet.getSchedulerClass())) {
188                     schedulerInstance = (Scheduler)portletClassLoader.loadClass(
189                         portlet.getSchedulerClass()).newInstance();
190                 }
191 
192                 FriendlyURLMapper friendlyURLMapperInstance = null;
193 
194                 if (Validator.isNotNull(portlet.getFriendlyURLMapperClass())) {
195                     friendlyURLMapperInstance =
196                         (FriendlyURLMapper)portletClassLoader.loadClass(
197                             portlet.getFriendlyURLMapperClass()).newInstance();
198                 }
199 
200                 URLEncoder urlEncoderInstance = null;
201 
202                 if (Validator.isNotNull(portlet.getURLEncoderClass())) {
203                     urlEncoderInstance =
204                         (URLEncoder)portletClassLoader.loadClass(
205                             portlet.getURLEncoderClass()).newInstance();
206                 }
207 
208                 PortletDataHandler portletDataHandlerInstance = null;
209 
210                 if (Validator.isNotNull(portlet.getPortletDataHandlerClass())) {
211                     portletDataHandlerInstance =
212                         (PortletDataHandler)portletClassLoader.loadClass(
213                             portlet.getPortletDataHandlerClass()).newInstance();
214                 }
215 
216                 PortletLayoutListener portletLayoutListenerInstance = null;
217 
218                 if (Validator.isNotNull(
219                         portlet.getPortletLayoutListenerClass())) {
220 
221                     portletLayoutListenerInstance =
222                         (PortletLayoutListener)portletClassLoader.loadClass(
223                             portlet.getPortletLayoutListenerClass()).
224                                 newInstance();
225                 }
226 
227                 ActivityTrackerInterpreter activityTrackerInterpreterInstance =
228                     null;
229 
230                 if (Validator.isNotNull(
231                         portlet.getActivityTrackerInterpreterClass())) {
232 
233                     activityTrackerInterpreterInstance =
234                         (ActivityTrackerInterpreter)
235                             portletClassLoader.loadClass(
236                                 portlet.getActivityTrackerInterpreterClass()).
237                                     newInstance();
238 
239                     activityTrackerInterpreterInstance =
240                         new ActivityTrackerInterpreterImpl(
241                             activityTrackerInterpreterInstance);
242 
243                     ActivityTrackerInterpreterUtil.
244                         addActivityTrackerInterpreter(
245                             activityTrackerInterpreterInstance);
246                 }
247 
248                 MessageListener popMessageListenerInstance = null;
249 
250                 if (Validator.isNotNull(
251                         portlet.getPopMessageListenerClass())) {
252 
253                     popMessageListenerInstance =
254                         (MessageListener)portletClassLoader.loadClass(
255                             portlet.getPopMessageListenerClass()).
256                                 newInstance();
257 
258                     POPServerUtil.addListener(popMessageListenerInstance);
259                 }
260 
261                 PreferencesValidator prefsValidatorInstance = null;
262 
263                 if (Validator.isNotNull(portlet.getPreferencesValidator())) {
264                     prefsValidatorInstance =
265                         (PreferencesValidator)portletClassLoader.loadClass(
266                             portlet.getPreferencesValidator()).newInstance();
267 
268                     try {
269                         if (PropsValues.PREFERENCE_VALIDATE_ON_STARTUP) {
270                             prefsValidatorInstance.validate(
271                                 PortletPreferencesSerializer.fromDefaultXML(
272                                     portlet.getDefaultPreferences()));
273                         }
274                     }
275                     catch (Exception e1) {
276                         _log.warn(
277                             "Portlet with the name " + portlet.getPortletId() +
278                                 " does not have valid default preferences");
279                     }
280                 }
281 
282                 Map resourceBundles = null;
283 
284                 if (Validator.isNotNull(portlet.getResourceBundle())) {
285                     resourceBundles = CollectionFactory.getHashMap();
286 
287                     Iterator itr2 = portlet.getSupportedLocales().iterator();
288 
289                     while (itr2.hasNext()) {
290                         String supportedLocale = (String)itr2.next();
291 
292                         Locale locale = LocaleUtil.fromLanguageId(
293                             supportedLocale);
294 
295                         try {
296                             ResourceBundle resourceBundle =
297                                 ResourceBundle.getBundle(
298                                     portlet.getResourceBundle(), locale,
299                                     portletClassLoader);
300 
301                             resourceBundles.put(
302                                 LocaleUtil.toLanguageId(locale),
303                                 resourceBundle);
304                         }
305                         catch (MissingResourceException mre) {
306                             _log.warn(mre.getMessage());
307                         }
308                     }
309                 }
310 
311                 Map customUserAttributes = CollectionFactory.getHashMap();
312 
313                 Iterator itr2 =
314                     portlet.getCustomUserAttributes().entrySet().iterator();
315 
316                 while (itr2.hasNext()) {
317                     Map.Entry entry = (Map.Entry)itr2.next();
318 
319                     String attrCustomClass = (String)entry.getValue();
320 
321                     customUserAttributes.put(
322                         attrCustomClass,
323                         portletClassLoader.loadClass(
324                             attrCustomClass).newInstance());
325                 }
326 
327                 PortletContextWrapper pcw = new PortletContextWrapper(
328                     portlet.getPortletId(), ctx, portletInstance,
329                     configurationActionInstance, indexerInstance,
330                     schedulerInstance, friendlyURLMapperInstance,
331                     urlEncoderInstance, portletDataHandlerInstance,
332                     portletLayoutListenerInstance,
333                     activityTrackerInterpreterInstance,
334                     popMessageListenerInstance, prefsValidatorInstance,
335                     resourceBundles, customUserAttributes);
336 
337                 PortletContextPool.put(portlet.getPortletId(), pcw);
338 
339                 try {
340                     PortletInstanceFactory.create(portlet, ctx);
341                 }
342                 catch (Exception e1) {
343                     if (_log.isWarnEnabled()) {
344                         _log.warn(e1.getMessage());
345                     }
346                 }
347             }
348 
349             // Struts bridges
350 
351             if (!strutsBridges) {
352                 strutsBridges = GetterUtil.getBoolean(
353                     ctx.getInitParameter("struts-bridges-context-provider"));
354             }
355 
356             if (strutsBridges) {
357                 ctx.setAttribute(
358                     ServletContextProvider.STRUTS_BRIDGES_CONTEXT_PROVIDER,
359                     new LiferayServletContextProvider());
360             }
361 
362             // Portlet display
363 
364             String xml = Http.URLtoString(ctx.getResource(
365                 "/WEB-INF/liferay-display.xml"));
366 
367             PortletCategory newPortletCategory =
368                 PortletLocalServiceUtil.getWARDisplay(servletContextName, xml);
369 
370             for (int i = 0; i < companyIds.length; i++) {
371                 long companyId = companyIds[i];
372 
373                 PortletCategory portletCategory =
374                     (PortletCategory)WebAppPool.get(
375                         String.valueOf(companyId), WebKeys.PORTLET_CATEGORY);
376 
377                 if (portletCategory != null) {
378                     portletCategory.merge(newPortletCategory);
379                 }
380                 else {
381                     _log.error(
382                         "Unable to register portlet for company " + companyId +
383                             " because it does not exist");
384                 }
385             }
386 
387             // Portlet properties
388 
389             String portletPropsName = ctx.getInitParameter(
390                 "portlet_properties");
391 
392             if (Validator.isNotNull(portletPropsName)) {
393                 if (_log.isDebugEnabled()) {
394                     _log.debug(
395                         "Loading portlet properties " + portletPropsName);
396                 }
397 
398                 Properties portletProps = new Properties();
399 
400                 PropertiesUtil.load(
401                     portletProps,
402                     StringUtil.read(portletClassLoader, portletPropsName));
403 
404                 if (_log.isDebugEnabled()) {
405                     String portletPropsString = PropertiesUtil.list(
406                         portletProps);
407 
408                     _log.debug(portletPropsString);
409                 }
410 
411                 processProperties(
412                     servletContextName, portletClassLoader, portletProps);
413             }
414 
415             // Service builder properties
416 
417             processServiceBuilder(ctx, portletClassLoader);
418 
419             // Variables
420 
421             _vars.put(
422                 servletContextName, new ObjectValuePair(companyIds, portlets));
423 
424             if (_log.isInfoEnabled()) {
425                 _log.info(
426                     "Portlets for " + servletContextName +
427                         " registered successfully");
428             }
429         }
430         catch (Exception e2) {
431             throw new HotDeployException(
432                 "Error registering portlets for " + servletContextName, e2);
433         }
434     }
435 
436     public void invokeUndeploy(HotDeployEvent event) throws HotDeployException {
437         String servletContextName = null;
438 
439         try {
440             ServletContext ctx = event.getServletContext();
441 
442             servletContextName = ctx.getServletContextName();
443 
444             if (_log.isDebugEnabled()) {
445                 _log.debug("Invoking undeploy for " + servletContextName);
446             }
447 
448             ObjectValuePair ovp = (ObjectValuePair)_vars.remove(
449                 servletContextName);
450 
451             if (ovp == null) {
452                 return;
453             }
454 
455             long[] companyIds = (long[])ovp.getKey();
456             List portlets = (List)ovp.getValue();
457 
458             Set portletIds = new HashSet();
459 
460             if (portlets != null) {
461                 if (_log.isInfoEnabled()) {
462                     _log.info(
463                         "Unregistering portlets for " + servletContextName);
464                 }
465 
466                 Iterator itr = portlets.iterator();
467 
468                 while (itr.hasNext()) {
469                     Portlet portlet = (Portlet)itr.next();
470 
471                     ActivityTrackerInterpreterUtil.
472                         deleteActivityTrackerInterpreter(
473                             portlet.getActivityTrackerInterpreterInstance());
474 
475                     POPServerUtil.deleteListener(
476                         portlet.getPopMessageListenerInstance());
477 
478                     PortletInstanceFactory.destroy(portlet);
479 
480                     portletIds.add(portlet.getPortletId());
481                 }
482             }
483 
484             if (portletIds.size() > 0) {
485                 for (int i = 0; i < companyIds.length; i++) {
486                     long companyId = companyIds[i];
487 
488                     PortletCategory portletCategory =
489                         (PortletCategory)WebAppPool.get(
490                             String.valueOf(companyId),
491                             WebKeys.PORTLET_CATEGORY);
492 
493                     portletCategory.separate(portletIds);
494                 }
495             }
496 
497             PortletResourceBundles.remove(servletContextName);
498 
499             if (_log.isInfoEnabled()) {
500                 _log.info(
501                     "Portlets for " + servletContextName +
502                         " unregistered successfully");
503             }
504         }
505         catch (Exception e) {
506             throw new HotDeployException(
507                 "Error unregistering portlets for " + servletContextName, e);
508         }
509     }
510 
511     protected void processProperties(
512             String servletContextName, ClassLoader portletClassLoader,
513             Properties portletProps)
514         throws Exception {
515 
516         String languageBundleName = portletProps.getProperty("language.bundle");
517 
518         if (Validator.isNotNull(languageBundleName)) {
519             Locale[] locales = LanguageUtil.getAvailableLocales();
520 
521             for (int i = 0; i < locales.length; i++) {
522                 ResourceBundle bundle = ResourceBundle.getBundle(
523                     languageBundleName, locales[i], portletClassLoader);
524 
525                 PortletResourceBundles.put(
526                     servletContextName, LocaleUtil.toLanguageId(locales[i]),
527                     bundle);
528             }
529         }
530 
531         String[] resourceActionConfigs = StringUtil.split(
532             portletProps.getProperty("resource.actions.configs"));
533 
534         for (int i = 0; i < resourceActionConfigs.length; i++) {
535             ResourceActionsUtil.read(
536                 servletContextName, portletClassLoader,
537                 resourceActionConfigs[i]);
538         }
539     }
540 
541     protected void processServiceBuilder(
542             ServletContext ctx, ClassLoader portletClassLoader)
543         throws Exception {
544 
545         if (portletClassLoader.getResourceAsStream(
546                 "portlet-service.properties") == null) {
547 
548             return;
549         }
550 
551         Properties serviceBuilderProps = new Properties();
552 
553         PropertiesUtil.load(
554             serviceBuilderProps,
555             StringUtil.read(portletClassLoader, "portlet-service.properties"));
556 
557         String buildNamespace = GetterUtil.getString(
558             serviceBuilderProps.getProperty("build.namespace"));
559         long buildNumber = GetterUtil.getLong(
560             serviceBuilderProps.getProperty("build.number"));
561         long buildDate = GetterUtil.getLong(
562             serviceBuilderProps.getProperty("build.date"));
563 
564         if (_log.isDebugEnabled()) {
565             _log.debug("Build namespace " + buildNamespace);
566             _log.debug("Build number " + buildNumber);
567             _log.debug("Build date " + buildDate);
568         }
569 
570         ServiceComponentLocalServiceUtil.updateServiceComponent(
571             ctx, portletClassLoader, buildNamespace, buildNumber, buildDate);
572     }
573 
574     private static Log _log = LogFactory.getLog(PortletHotDeployListener.class);
575 
576     private static Map _vars = CollectionFactory.getHashMap();
577 
578 }