1/*2 * Copyright 2000-2004 The Apache Software Foundation.3 * 4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 * 8 * http://www.apache.org/licenses/LICENSE-2.09 * 10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */1617packageorg.apache.jetspeed.services.portletfactory;
1819//jetspeed stuff20import org.apache.jetspeed.portal.Portlet;
21import org.apache.jetspeed.portal.PortletConfig;
22import org.apache.jetspeed.portal.PortletException;
23import org.apache.jetspeed.portal.BasePortletConfig;
2425import org.apache.jetspeed.portal.security.portlets.PortletWrapper;
2627import org.apache.jetspeed.om.SecurityReference;
28import org.apache.jetspeed.om.profile.Entry;
29import org.apache.jetspeed.om.profile.Parameter;
30import org.apache.jetspeed.om.profile.MetaInfo;
31import org.apache.jetspeed.services.JetspeedSecurity;
32import org.apache.jetspeed.services.Registry;
33import org.apache.jetspeed.services.PortalToolkit;
34import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
35import org.apache.jetspeed.services.logging.JetspeedLogger;
36import org.apache.jetspeed.services.portletcache.PortletCache;
37import org.apache.jetspeed.services.portletcache.Cacheable;
38import org.apache.jetspeed.om.registry.PortletEntry;
39import org.apache.jetspeed.om.profile.Profile;
40import org.apache.jetspeed.util.MetaData;
41import org.apache.jetspeed.services.rundata.JetspeedRunDataService;
42import org.apache.jetspeed.services.rundata.JetspeedRunData;
4344import org.apache.turbine.services.TurbineServices;
45import org.apache.turbine.services.TurbineBaseService;
46import org.apache.turbine.services.InitializationException;
47import org.apache.turbine.services.resources.ResourceService;
48import org.apache.turbine.services.rundata.RunDataService;
4950import java.util.Hashtable;
51import java.util.Iterator;
52import java.util.Map;
53import java.util.HashMap;
54import javax.servlet.ServletConfig;
5556/***57 * Simple implementation of the PortalFactoryService.58 * 59 * @author <a href="mailto:raphael@apache.org">Raphaël Luta</a>60 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>61 * @version $Id: JetspeedPortletFactoryService.java,v 1.23 2004/02/23 03:36:42 jford Exp $62 */63publicclassJetspeedPortletFactoryServiceextends TurbineBaseService
64 implements PortletFactoryService65 {
66/***67 * Static initialization of the logger for this class68 */69privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedPortletFactoryService.class.getName());
7071/*** The default control to use when none is specified */72privateboolean enableCache = false;
7374/*** The JetspeedRunData Service. */75privateJetspeedRunDataService runDataService = null;
7677/***78 * This is the early initialization method called by the 79 * Turbine <code>Service</code> framework80 */81publicvoid init( ServletConfig conf ) throws InitializationException
82 {
8384 ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
85 .getResources(PortletFactoryService.SERVICE_NAME);
8687this.enableCache = serviceConf.getBoolean("enable.cache",true);
8889// get the runData service90this.runDataService =
91 (JetspeedRunDataService)TurbineServices.getInstance()
92 .getService(RunDataService.SERVICE_NAME);
9394 setInit(true);
95 }
9697/***98 * Given a PSML Entry return an instanciated Portlet.99 *100 * @param entry a PSML Entry describing a portlet101 * @param id the PSML entry's portlet id 102 * @return an instanciated portlet corresponding to this entry103 */104publicPortlet getPortlet( Entry entry ) throws PortletException
105 {
106PortletEntry regEntry = (PortletEntry)Registry.getEntry(Registry.PORTLET,
107 entry.getParent() );
108if (regEntry == null)
109 {
110thrownewPortletException("PortletFactory: unknown portlet entry in Registry: "+entry.getParent());
111 }
112113if (PortletEntry.TYPE_ABSTRACT.equals(regEntry.getType()))
114 {
115thrownewPortletException("PortletFactory: can't instanciate abstract registry entry: "+regEntry.getName());
116 }
117118PortletConfig pc = getPortletConfig(regEntry, entry.getId());
119120// Set portlet config with values from PSML Entry121 pc.getInitParameters().putAll(getParameters(entry));
122 pc.setPortletSkin( PortalToolkit.getSkin( entry.getSkin() ) );
123 pc.setSecurityRef( getSecurityReference(entry, regEntry));
124125return getPortlet( getClassname(regEntry), pc, entry.getId() );
126 }
127128/***129 * Given a Portlet registry entry name, instanciate it130 *131 * @param name the name of a portlet in the registry132 * @return an instanciated portlet corresponding to this entry133 */134publicPortlet getPortlet( String name, String id ) throws PortletException
135 {
136PortletEntry regEntry = (PortletEntry)Registry.getEntry(Registry.PORTLET, name );
137138if (regEntry == null)
139 {
140thrownewPortletException("PortletFactory: unknown portlet entry in Registry: "+name);
141 }
142143if (PortletEntry.TYPE_ABSTRACT.equals(regEntry.getType()))
144 {
145thrownewPortletException("PortletFactory: can't instanciate abstract registry entry: "+name);
146 }
147148PortletConfig pc = getPortletConfig(regEntry, id);
149150return getPortlet( getClassname(regEntry), pc, null );
151 }
152153/*** 154 * Instanciates or retrieve from memory cache a portlet corresponding to the 155 * passed parameters156 *157 * @param classname the classname of the portlet to instanciate158 * @param pc the PortletConfig object to be associated with this object159 * @param id the PSML entry's portlet id160 * @return the Portlet created or retrieve from cache161 */162protectedPortlet getPortlet( String classname, PortletConfig pc, String id )
163 throws PortletException164 {
165166//record the begining of the portlet creation167long begin = System.currentTimeMillis();
168169Portlet portlet = null;
170 Class portletClass = null;
171 String handle = null;
172173try174 {
175 portletClass = Class.forName(classname);
176 }
177catch (Exception e)
178 {
179thrownewPortletException( "PortletFactory: Unable to load class " + classname );
180 }
181182if (enableCache)
183 {
184try185 {
186// try to invoke a static getHandle() for this class187 Class[] signatureParams = { Object.class };
188 Object[] methodParams = { pc };
189 handle = (String)portletClass.getMethod("getHandle",signatureParams)
190 .invoke(null,methodParams);
191// make sure the handle is differenciated by class192 handle = String.valueOf(classname.hashCode())+handle;
193 }
194catch (NoSuchMethodException e)
195 {
196// ignore, this simply means the portlet is not cacheable197 }
198catch (Exception e)
199 {
200// invocation failed or security exception, in both case201// log the error and treat the class as non cacheable202 logger.error("PortletFactory: failed to get cache handle",e);
203 }
204 }
205206try {
207208if (enableCache && (handle != null))
209 {
210 portlet = (Portlet)PortletCache.getCacheable( handle );
211212//portlet in cache but expired, remove it from cache213if ((portlet!=null) && ((Cacheable)portlet).getExpire().isExpired() )
214 {
215 logger.info( "The portlet (" + handle + ") is expired" );
216 PortletCache.removeCacheable(handle);
217if ( logger.isDebugEnabled() )
218 {
219 logger.debug( "After removal of object(" + handle + ")." );
220 }
221 portlet = null;
222 }
223 }
224225// we found a portlet in the cache226if ( (portlet != null)
227 && ( portlet instanceof Cacheable )
228 && (! ((Cacheable)portlet).getExpire().isExpired()) )
229 {
230// update the config for the portlet to the current one231// Note: this is what was used to find the cached portlet.232// Note: the init params may have changed in the psml since caching,233// this will update the portlet to use them.234 portlet.setPortletConfig( pc );
235 portlet.setID( id );
236 portlet.setName( pc.getName() );
237238//FIXME: we now avoid to override metainfo when nothing is set239//in the markup, so that cached portlets can keep their metainfo240//This may lead to an incorrect metainfo retrieved if the first241//instance of the portlet, which is put in the cache, has some242//special metainfo defined in the markup 243244MetaData meta = pc.getMetainfo();
245246if ( meta != null)
247 {
248249if (! MetaData.DEFAULT_TITLE.equals( meta.getTitle() ) )
250 {
251 portlet.setTitle( meta.getTitle() );
252 }
253254if (! MetaData.DEFAULT_DESCRIPTION.equals( meta.getDescription() ) )
255 {
256 portlet.setDescription( meta.getDescription() );
257 }
258 }
259260//FIXME: Notice here we are putting the portlet without wrapper261//in the cache, and we must wrap it on return.262//Security implications: the portletcache should not be263//publicly accessible.264//Alternative: we could wrap the portlet before putting265//it in the cache.266267//now compute the time it took to instantate and log it...268// time in millis, sugested by Thomas Schaeck (schaeck@de.ibm.com)269long milliseconds = ( System.currentTimeMillis() - begin );
270271if (logger.isDebugEnabled())
272 logger.debug( "PortletFactory.getPortlet(): found in cache in "273 + milliseconds + " ms - handle: " + handle );
274275return PortletWrapper.wrap( portlet );
276 }
277278// if not found in the cache, instanciate a new Portlet279 portlet = (Portlet)portletClass.newInstance();
280281 }
282catch ( Throwable t )
283 {
284 logger.error("Throwable", t);
285thrownewPortletException( t.getMessage() );
286 }
287288// save the current meta-info289 String title = null;
290 String description = null;
291MetaData metainfo = pc.getMetainfo();
292293if ( metainfo != null ) {
294 title=metainfo.getTitle();
295 description=metainfo.getDescription();
296 }
297298299// init the portlet, it may override its PSML defined markup if300// it doesn't check for it301 portlet.setID( id );
302 portlet.setName( pc.getName() );
303 portlet.setPortletConfig( pc );
304 portlet.setCreationTime( System.currentTimeMillis() );
305 portlet.init();
306307//force the title and description from markup metadata308//in case the portlet overwrote some values309310if ( metainfo != null)
311 {
312if (!MetaData.DEFAULT_TITLE.equals(title) )
313 {
314 portlet.setTitle( title );
315 }
316317if (!MetaData.DEFAULT_DESCRIPTION.equals(description) )
318 {
319 portlet.setDescription( description );
320 }
321 }
322323if (enableCache && (portlet instanceof Cacheable))
324 {
325//place this portlet in a cache...326 ((Cacheable)portlet).setHandle( handle );
327 PortletCache.addCacheable( ((Cacheable)portlet) );
328//Expiration should be added to the portlet now, so that329//the watcher is created before file changes on disk.330 ((Cacheable)portlet).getExpire();
331332 }
333334//now compute the time it took to instantate and log it...335// time in millis, sugested by Thomas Schaeck (schaeck@de.ibm.com)336long milliseconds = ( System.currentTimeMillis() - begin );
337338if (logger.isDebugEnabled())
339 logger.debug( "PortletFactory.getPortlet(): constructed in "340 + milliseconds + " ms - handle: " + handle );
341342return PortletWrapper.wrap( portlet );
343344 }
345346/***347 * Given a Registry Entry, get the value of what its PortletConfig would be.348 *349 * @param entry the PSML Entry containing the config350 * @param portletId the PSML entry's portlet id351 * @return the newly created PortletConfig object352 */353protectedPortletConfig getPortletConfig( PortletEntry portletEntry, String id)
354 {
355 Map map = new HashMap();
356 map.putAll(portletEntry.getParameterMap());
357358PortletConfig pc = newBasePortletConfig();
359 pc.setName( portletEntry.getName() );
360 addParentInitParameters(portletEntry, map);
361 pc.setInitParameters( map );
362 pc.setMetainfo( getMetaData( portletEntry ) );
363 pc.setURL( portletEntry.getURL() );
364 pc.setCachedOnURL( portletEntry.isCachedOnURL() );
365//pc.setSecurityRef(portletEntry.getSecurityRef());366 pc.setSecurityRef(getSecurityReference(null, portletEntry));
367368if (runDataService != null)
369 {
370JetspeedRunData rundata = runDataService.getCurrentRunData();
371if (rundata != null)
372 {
373Profile profile = rundata.getProfile();
374if (profile != null)
375 {
376 pc.setPageId(profile.getId());
377 }
378 }
379 }
380 pc.setPortletId(id);
381382return pc;
383 }
384385386/***387 * Fetches the parameters out of a PSML Entry388 * 389 * @param entry the Entry to check for parameters390 * @return a Map containing the parameters names/values, an empty Map 391 * is returned if there are no parameters392 */393protectedstatic Map getParameters( Entry entry )
394 {
395 Hashtable hash = new Hashtable();
396397Parameter[] props = entry.getParameter();
398399for(int i = 0; i < props.length; ++i)
400 {
401 hash.put(props[i].getName(), props[i].getValue() );
402 }
403404return hash;
405 }
406407/***408 Create a MetaData object from a PSML Metainfo object409410 @param meta the Metainfo to copy411412 @return the new MetaData object, empty if meta is null413 */414protectedstaticMetaData getMetaData(Entry entry)
415 {
416MetaData data = newMetaData();
417MetaInfo meta = entry.getMetaInfo();
418419if ( meta != null )
420 {
421if ( meta.getTitle() != null )
422 data.setTitle( meta.getTitle() );
423424if ( meta.getDescription() != null )
425 data.setDescription( meta.getDescription() );
426427if ( meta.getImage() != null )
428 data.setImage( meta.getImage() );
429 }
430431if ( entry.getParent() != null )
432 {
433434PortletEntry parent = (PortletEntry)Registry
435 .getEntry( Registry.PORTLET, entry.getParent() );
436437if (parent != null)
438 {
439MetaData parentData = getMetaData( parent );
440 parentData.merge(data);
441return parentData;
442 }
443444 }
445446return data;
447448 }
449450/***451 Create a MetaData object from a registry Metainfo object452453 @param meta the Metainfo to copy454455 @return the new MetaData object, empty if meta is null456 */457protectedstaticMetaData getMetaData(PortletEntry entry)
458 {
459MetaData data = newMetaData();
460461if ( entry.getTitle() != null )
462 data.setTitle( entry.getTitle() );
463464if ( entry.getDescription() != null )
465 data.setDescription( entry.getDescription() );
466467if ( entry.getMetaInfo() != null && entry.getMetaInfo().getImage() != null )
468 data.setImage( entry.getMetaInfo().getImage() );
469470return data;
471 }
472473/***474 * @param Entry entry Entry whose parent we want475 * @return PortletEntry Parent of Entry476 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>477 */478protectedstaticPortletEntry getParentEntry(PortletEntry entry)
479 {
480PortletEntry result = null;
481 String parent = entry.getParent();
482if (parent != null)
483 {
484 result = (PortletEntry) Registry.getEntry(Registry.PORTLET, parent);
485 }
486487return result;
488 }
489490/***491 * Retruns the classname defined for this PortletEntry.492 * If no classname was defined, the parent is queried493 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>494 */495protected String getClassname(PortletEntry entry)
496 {
497 String className = entry.getClassname();
498if (className == null)
499 {
500PortletEntry parent = getParentEntry(entry);
501if (parent != null)
502 {
503// We must walk up the hierarchy just to be safe504 className = getClassname(parent);
505 }
506 }
507508return className;
509 }
510511/***512 * Maps all parameters, not found within the <code>entry</code>, from513 * the <code>entry</code>'s parent into the entry514 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>515 */516protectedvoid addParentInitParameters(PortletEntry entry, Map hash)
517 {
518// Now map any parameters from the parent that the child does not have519PortletEntry parent = getParentEntry(entry);
520if (parent != null)
521 {
522 Map parentMap = parent.getParameterMap();
523 Iterator names = parent.getParameterNames();
524525while (names.hasNext())
526 {
527 String key = (String) names.next();
528if (!hash.containsKey(key))
529 {
530 hash.put(key, parentMap.get(key));
531 }
532 }
533534// Always make sure to get the entire inheritence chain535 addParentInitParameters(parent, hash);
536 }
537 }
538539/***540 * Figures out how to produce a security reference for541 * this portlet.542 */543protectedSecurityReference getSecurityReference(Entry entry, PortletEntry pEntry)
544 {
545// If something happended during init() that prevented this546if (runDataService == null)
547 {
548this.runDataService =
549 (JetspeedRunDataService) TurbineServices.getInstance().getService(
550 RunDataService.SERVICE_NAME);
551 }
552JetspeedRunData rundata = runDataService.getCurrentRunData();
553554return JetspeedSecurity.getSecurityReference(entry, rundata);
555 }
556557 }