View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.factory;
18  
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.Map;
23  
24  import javax.portlet.Portlet;
25  import javax.portlet.PortletConfig;
26  import javax.portlet.PortletContext;
27  import javax.portlet.PortletException;
28  import javax.portlet.PreferencesValidator;
29  import javax.portlet.UnavailableException;
30  import javax.servlet.ServletContext;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.jetspeed.container.JetspeedPortletConfig;
35  import org.apache.jetspeed.container.PortalAccessor;
36  import org.apache.jetspeed.om.common.portlet.PortletApplication;
37  import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
38  import org.apache.jetspeed.portlet.PortletObjectProxy;
39  import org.apache.pluto.om.portlet.PortletDefinition;
40  
41  /***
42   * <p>
43   * JetspeedPortletFactory
44   * </p>
45   * <p>
46   *
47   * </p>
48   * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
49   * @version $Id: JetspeedPortletFactory.java 593513 2007-11-09 12:48:34Z woonsan $
50   *
51   */
52  public class JetspeedPortletFactory implements PortletFactory
53  {
54  
55      private Map portletCache;
56      private Map validatorCache;
57      
58      private static final Log log = LogFactory.getLog(JetspeedPortletFactory.class);
59      private final Map classLoaderMap;
60      
61      /***
62       * Flag whether this factory will create proxy instances for actual portlet instances or not.
63       */
64      private boolean portletProxyUsed;
65      
66      /***
67       * Flag whether the instantiated proxy will switch edit_defaults mode to edit mode automatically or not.
68       */
69      private boolean autoSwitchEditDefaultsModeToEditMode;
70      
71      /***
72       * Flag whether the instantiated proxy will switch config mode to built-in config edit page or not.
73       */
74      private boolean autoSwitchConfigMode;
75      
76      private String customConfigModePortletUniqueName;
77      
78      public JetspeedPortletFactory()
79      {
80          this(false, false);
81      }
82      
83      public JetspeedPortletFactory(boolean autoSwitchConfigMode, boolean autoSwitchEditDefaultsModeToEditMode)
84      {
85          this.portletCache =  Collections.synchronizedMap(new HashMap());
86          this.validatorCache = Collections.synchronizedMap(new HashMap());
87          classLoaderMap = Collections.synchronizedMap(new HashMap());
88          
89          this.autoSwitchConfigMode = autoSwitchConfigMode;
90          this.autoSwitchEditDefaultsModeToEditMode = autoSwitchEditDefaultsModeToEditMode;
91          
92          this.portletProxyUsed = (this.autoSwitchConfigMode || this.autoSwitchEditDefaultsModeToEditMode);
93      }
94      
95      public void setPortletProxyUsed(boolean portletProxyUsed)
96      {
97          this.portletProxyUsed = portletProxyUsed;
98      }
99      
100     public boolean getPortletProxyUsed()
101     {
102         return this.portletProxyUsed;
103     }
104     
105     public void setCustomConfigModePortletUniqueName(String customConfigModePortletUniqueName)
106     {
107         this.customConfigModePortletUniqueName = customConfigModePortletUniqueName;
108     }
109     
110     public String getCustomConfigModePortletUniqueName()
111     {
112         return this.customConfigModePortletUniqueName;
113     }
114 
115     public void registerPortletApplication(PortletApplication pa, ClassLoader cl)
116         {
117             synchronized (classLoaderMap)
118             {
119                 unregisterPortletApplication(pa);
120                 classLoaderMap.put(pa.getId(), cl);
121             }
122     }
123     
124     public void unregisterPortletApplication(PortletApplication pa)
125     {
126         synchronized (classLoaderMap)
127         {
128             synchronized (portletCache)
129             {
130                 ClassLoader cl = (ClassLoader) classLoaderMap.remove(pa.getId());
131                 if (cl != null)
132                 {
133                     ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
134 
135                     Iterator portletDefinitions = pa.getPortletDefinitions().iterator();
136                     while (portletDefinitions.hasNext())
137                     {
138                         PortletDefinition pd = (PortletDefinition) portletDefinitions.next();
139                         String pdId = pd.getId().toString();
140                         Portlet portlet = (Portlet) portletCache.remove(pdId);
141                         if (portlet != null)
142                         {
143                             try
144                             {
145                                 Thread.currentThread().setContextClassLoader(cl);
146                                 portlet.destroy();
147                             }
148                             finally
149                             {
150                                 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
151                             }
152                         }
153                         validatorCache.remove(pdId);
154                     }
155                 }
156             }
157         }
158     }
159     
160     public PreferencesValidator getPreferencesValidator(PortletDefinition pd)
161     {
162         PreferencesValidator validator = null;
163         try
164         {
165             String pdId = pd.getId().toString();
166             
167             synchronized (validatorCache)
168             {
169                 validator = (PreferencesValidator)validatorCache.get(pdId);
170                 if ( validator == null )
171                 {
172                     String className = ((PortletDefinitionComposite)pd).getPreferenceValidatorClassname();
173                     if ( className != null )
174                     {
175                         PortletApplication pa = (PortletApplication)pd.getPortletApplicationDefinition();
176                         ClassLoader paCl = (ClassLoader)classLoaderMap.get(pa.getId());
177                         if ( paCl == null )
178                         {
179                             throw new UnavailableException("Portlet Application "+pa.getName()+" not available");
180                         }
181                         
182                         ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
183                         try
184                         {
185                             Class clazz = paCl.loadClass(className);
186                             try
187                             {
188                                 Thread.currentThread().setContextClassLoader(paCl);
189                                 validator = (PreferencesValidator)clazz.newInstance();
190                                 validatorCache.put(pdId, validator);
191                             }
192                             finally
193                             {
194                                 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
195                             }
196                         }
197                         catch (Exception e)
198                         {
199                             String msg = "Cannot create PreferencesValidator instance "+className+" for Portlet "+pd.getName();
200                             log.error(msg,e);
201                         }
202                     }
203                 }
204             }
205         }
206         catch (Exception e)
207         {
208             log.error(e);
209         }
210         return validator;
211     }
212 
213     /***
214      * Gets a portlet by either creating it or returning a handle to it from the portlet 'cache'
215      * 
216      * @param portletDefinition The definition of the portlet
217      * @return PortletInstance 
218      * @throws PortletException
219      */
220     public PortletInstance getPortletInstance( ServletContext servletContext, PortletDefinition pd ) throws PortletException
221     {
222         PortletInstance portlet = null;
223         String pdId = pd.getId().toString();
224         PortletApplication pa = (PortletApplication)pd.getPortletApplicationDefinition();
225 
226         try
227         {                        
228           synchronized (portletCache)
229           {
230             portlet = (PortletInstance)portletCache.get(pdId);
231             if (null != portlet)
232             {
233                 return portlet;
234             }
235             
236             ClassLoader paCl = (ClassLoader)classLoaderMap.get(pa.getId());
237             if ( paCl == null )
238             {
239                 throw new UnavailableException("Portlet Application "+pa.getName()+" not available");
240             }
241             
242             ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();
243  
244             try
245             {
246               Class clazz = paCl.loadClass(pd.getClassName());
247               try
248             {
249                 Thread.currentThread().setContextClassLoader(paCl);
250                 // wrap new Portlet inside PortletInstance which ensures the destroy
251                 // method will wait for all its invocation threads to complete
252                 // and thereby releasing all its ClassLoader locks as needed for local portlets.
253                 
254                 if (this.portletProxyUsed && !PortletObjectProxy.isPortletObjectProxied())
255                 {
256                     portlet = new JetspeedPortletProxyInstance(pd.getName(), (Portlet)clazz.newInstance(), this.autoSwitchEditDefaultsModeToEditMode, this.autoSwitchConfigMode, this.customConfigModePortletUniqueName);
257                 }
258                 else
259                 {
260                     portlet = new JetspeedPortletInstance(pd.getName(), (Portlet)clazz.newInstance());
261                 }
262             }
263               finally
264             {
265                 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
266               }
267             }
268             catch (Exception e)
269             {
270                 String msg = "Cannot create Portlet instance "+pd.getClassName()+" for Portlet Application "+pa.getName();
271                 log.error(msg,e);
272                 throw new UnavailableException(msg);
273             }
274       
275             PortletContext portletContext = PortalAccessor.createPortletContext(servletContext, pa);            
276             PortletConfig portletConfig = PortalAccessor.createPortletConfig(portletContext, pd);
277             
278             try
279             {
280               try
281               {
282                 Thread.currentThread().setContextClassLoader(paCl);
283             portlet.init(portletConfig);            
284               }
285               finally
286               {
287                 Thread.currentThread().setContextClassLoader(currentContextClassLoader);
288               }
289             }
290             catch (PortletException e1)
291             {
292                 log.error("Failed to initialize Portlet "+pd.getClassName()+" for Portlet Application "+pa.getName(), e1);
293                 throw e1;
294             }            
295             portletCache.put(pdId, portlet);
296           }
297         }
298         catch (PortletException pe)
299         {
300             throw pe;
301         }
302         catch (Throwable e)
303         {
304             log.error("PortletFactory: Failed to load portlet "+pd.getClassName(), e);
305             throw new UnavailableException( "Failed to load portlet " + pd.getClassName() +": "+e.toString());
306         }
307         return portlet;
308     }
309     
310     public void updatePortletConfig(PortletDefinition pd)
311     {
312         if (pd != null)
313         {
314             //System.out.println("$$$$ updating portlet config for " + pd.getName());
315             String key = pd.getId().toString();
316             PortletInstance instance = (PortletInstance)portletCache.get(key);
317             if (instance != null)
318             {
319                 JetspeedPortletConfig config = (JetspeedPortletConfig)instance.getConfig();
320                 config.setPortletDefinition(pd);
321             }
322         }
323     }
324     
325     public ClassLoader getPortletApplicationClassLoader(PortletApplication pa)
326     {
327         synchronized (classLoaderMap)
328         {
329           if ( pa != null )
330         {
331               return (ClassLoader)classLoaderMap.get(pa.getId());
332         }
333           return null;
334         }
335     }
336     
337     public boolean isPortletApplicationRegistered(PortletApplication pa)
338     {
339         return getPortletApplicationClassLoader(pa) != null;
340     }
341 }