/* * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jetspeed.services.portletfactory; //jetspeed stuff import org.apache.jetspeed.portal.Portlet; import org.apache.jetspeed.portal.PortletConfig; import org.apache.jetspeed.portal.PortletException; import org.apache.jetspeed.portal.BasePortletConfig; import org.apache.jetspeed.portal.security.portlets.PortletWrapper; import org.apache.jetspeed.om.SecurityReference; import org.apache.jetspeed.om.profile.Entry; import org.apache.jetspeed.om.profile.Parameter; import org.apache.jetspeed.om.profile.MetaInfo; import org.apache.jetspeed.services.JetspeedSecurity; import org.apache.jetspeed.services.Registry; import org.apache.jetspeed.services.PortalToolkit; import org.apache.jetspeed.services.logging.JetspeedLogFactoryService; import org.apache.jetspeed.services.logging.JetspeedLogger; import org.apache.jetspeed.services.portletcache.PortletCache; import org.apache.jetspeed.services.portletcache.Cacheable; import org.apache.jetspeed.om.registry.PortletEntry; import org.apache.jetspeed.om.profile.Profile; import org.apache.jetspeed.util.MetaData; import org.apache.jetspeed.services.rundata.JetspeedRunDataService; import org.apache.jetspeed.services.rundata.JetspeedRunData; import org.apache.turbine.services.TurbineServices; import org.apache.turbine.services.TurbineBaseService; import org.apache.turbine.services.InitializationException; import org.apache.turbine.services.resources.ResourceService; import org.apache.turbine.services.rundata.RunDataService; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.HashMap; import javax.servlet.ServletConfig; /** * Simple implementation of the PortalFactoryService. * * @author <a href="mailto:raphael@apache.org">Rapha謖 Luta</a> * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a> * @version $Id: JetspeedPortletFactoryService.java,v 1.23 2004/02/23 03:36:42 jford Exp $ */ public class JetspeedPortletFactoryService extends TurbineBaseService implements PortletFactoryService { /** * Static initialization of the logger for this class */ private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedPortletFactoryService.class.getName()); /** The default control to use when none is specified */ private boolean enableCache = false; /** The JetspeedRunData Service. */ private JetspeedRunDataService runDataService = null; /** * This is the early initialization method called by the * Turbine <code>Service</code> framework */ public void init( ServletConfig conf ) throws InitializationException { ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance()) .getResources(PortletFactoryService.SERVICE_NAME); this.enableCache = serviceConf.getBoolean("enable.cache",true); // get the runData service this.runDataService = (JetspeedRunDataService)TurbineServices.getInstance() .getService(RunDataService.SERVICE_NAME); setInit(true); } /** * Given a PSML Entry return an instanciated Portlet. * * @param entry a PSML Entry describing a portlet * @param id the PSML entry's portlet id * @return an instanciated portlet corresponding to this entry */ public Portlet getPortlet( Entry entry ) throws PortletException { PortletEntry regEntry = (PortletEntry)Registry.getEntry(Registry.PORTLET, entry.getParent() ); if (regEntry == null) { throw new PortletException("PortletFactory: unknown portlet entry in Registry: "+entry.getParent()); } if (PortletEntry.TYPE_ABSTRACT.equals(regEntry.getType())) { throw new PortletException("PortletFactory: can't instanciate abstract registry entry: "+regEntry.getName()); } PortletConfig pc = getPortletConfig(regEntry, entry.getId()); // Set portlet config with values from PSML Entry pc.getInitParameters().putAll(getParameters(entry)); pc.setPortletSkin( PortalToolkit.getSkin( entry.getSkin() ) ); pc.setSecurityRef( getSecurityReference(entry, regEntry)); return getPortlet( getClassname(regEntry), pc, entry.getId() ); } /** * Given a Portlet registry entry name, instanciate it * * @param name the name of a portlet in the registry * @return an instanciated portlet corresponding to this entry */ public Portlet getPortlet( String name, String id ) throws PortletException { PortletEntry regEntry = (PortletEntry)Registry.getEntry(Registry.PORTLET, name ); if (regEntry == null) { throw new PortletException("PortletFactory: unknown portlet entry in Registry: "+name); } if (PortletEntry.TYPE_ABSTRACT.equals(regEntry.getType())) { throw new PortletException("PortletFactory: can't instanciate abstract registry entry: "+name); } PortletConfig pc = getPortletConfig(regEntry, id); return getPortlet( getClassname(regEntry), pc, null ); } /** * Instanciates or retrieve from memory cache a portlet corresponding to the * passed parameters * * @param classname the classname of the portlet to instanciate * @param pc the PortletConfig object to be associated with this object * @param id the PSML entry's portlet id * @return the Portlet created or retrieve from cache */ protected Portlet getPortlet( String classname, PortletConfig pc, String id ) throws PortletException { //record the begining of the portlet creation long begin = System.currentTimeMillis(); Portlet portlet = null; Class portletClass = null; String handle = null; try { portletClass = Class.forName(classname); } catch (Exception e) { throw new PortletException( "PortletFactory: Unable to load class " + classname ); } if (enableCache) { try { // try to invoke a static getHandle() for this class Class[] signatureParams = { Object.class }; Object[] methodParams = { pc }; handle = (String)portletClass.getMethod("getHandle",signatureParams) .invoke(null,methodParams); // make sure the handle is differenciated by class handle = String.valueOf(classname.hashCode())+handle; } catch (NoSuchMethodException e) { // ignore, this simply means the portlet is not cacheable } catch (Exception e) { // invocation failed or security exception, in both case // log the error and treat the class as non cacheable logger.error("PortletFactory: failed to get cache handle",e); } } try { if (enableCache && (handle != null)) { portlet = (Portlet)PortletCache.getCacheable( handle ); //portlet in cache but expired, remove it from cache if ((portlet!=null) && ((Cacheable)portlet).getExpire().isExpired() ) { logger.info( "The portlet (" + handle + ") is expired" ); PortletCache.removeCacheable(handle); if ( logger.isDebugEnabled() ) { logger.debug( "After removal of object(" + handle + ")." ); } portlet = null; } } // we found a portlet in the cache if ( (portlet != null) && ( portlet instanceof Cacheable ) && (! ((Cacheable)portlet).getExpire().isExpired()) ) { // update the config for the portlet to the current one // Note: this is what was used to find the cached portlet. // Note: the init params may have changed in the psml since caching, // this will update the portlet to use them. portlet.setPortletConfig( pc ); portlet.setID( id ); portlet.setName( pc.getName() ); //FIXME: we now avoid to override metainfo when nothing is set //in the markup, so that cached portlets can keep their metainfo //This may lead to an incorrect metainfo retrieved if the first //instance of the portlet, which is put in the cache, has some //special metainfo defined in the markup MetaData meta = pc.getMetainfo(); if ( meta != null) { if (! MetaData.DEFAULT_TITLE.equals( meta.getTitle() ) ) { portlet.setTitle( meta.getTitle() ); } if (! MetaData.DEFAULT_DESCRIPTION.equals( meta.getDescription() ) ) { portlet.setDescription( meta.getDescription() ); } } //FIXME: Notice here we are putting the portlet without wrapper //in the cache, and we must wrap it on return. //Security implications: the portletcache should not be //publicly accessible. //Alternative: we could wrap the portlet before putting //it in the cache. //now compute the time it took to instantate and log it... // time in millis, sugested by Thomas Schaeck (schaeck@de.ibm.com) long milliseconds = ( System.currentTimeMillis() - begin ); if (logger.isDebugEnabled()) logger.debug( "PortletFactory.getPortlet(): found in cache in " + milliseconds + " ms - handle: " + handle ); return PortletWrapper.wrap( portlet ); } // if not found in the cache, instanciate a new Portlet portlet = (Portlet)portletClass.newInstance(); } catch ( Throwable t ) { logger.error("Throwable", t); throw new PortletException( t.getMessage() ); } // save the current meta-info String title = null; String description = null; MetaData metainfo = pc.getMetainfo(); if ( metainfo != null ) { title=metainfo.getTitle(); description=metainfo.getDescription(); } // init the portlet, it may override its PSML defined markup if // it doesn't check for it portlet.setID( id ); portlet.setName( pc.getName() ); portlet.setPortletConfig( pc ); portlet.setCreationTime( System.currentTimeMillis() ); portlet.init(); //force the title and description from markup metadata //in case the portlet overwrote some values if ( metainfo != null) { if (!MetaData.DEFAULT_TITLE.equals(title) ) { portlet.setTitle( title ); } if (!MetaData.DEFAULT_DESCRIPTION.equals(description) ) { portlet.setDescription( description ); } } if (enableCache && (portlet instanceof Cacheable)) { //place this portlet in a cache... ((Cacheable)portlet).setHandle( handle ); PortletCache.addCacheable( ((Cacheable)portlet) ); //Expiration should be added to the portlet now, so that //the watcher is created before file changes on disk. ((Cacheable)portlet).getExpire(); } //now compute the time it took to instantate and log it... // time in millis, sugested by Thomas Schaeck (schaeck@de.ibm.com) long milliseconds = ( System.currentTimeMillis() - begin ); if (logger.isDebugEnabled()) logger.debug( "PortletFactory.getPortlet(): constructed in " + milliseconds + " ms - handle: " + handle ); return PortletWrapper.wrap( portlet ); } /** * Given a Registry Entry, get the value of what its PortletConfig would be. * * @param entry the PSML Entry containing the config * @param portletId the PSML entry's portlet id * @return the newly created PortletConfig object */ protected PortletConfig getPortletConfig( PortletEntry portletEntry, String id) { Map map = new HashMap(); map.putAll(portletEntry.getParameterMap()); PortletConfig pc = new BasePortletConfig(); pc.setName( portletEntry.getName() ); addParentInitParameters(portletEntry, map); pc.setInitParameters( map ); pc.setMetainfo( getMetaData( portletEntry ) ); pc.setURL( portletEntry.getURL() ); pc.setCachedOnURL( portletEntry.isCachedOnURL() ); //pc.setSecurityRef(portletEntry.getSecurityRef()); pc.setSecurityRef(getSecurityReference(null, portletEntry)); if (runDataService != null) { JetspeedRunData rundata = runDataService.getCurrentRunData(); if (rundata != null) { Profile profile = rundata.getProfile(); if (profile != null) { pc.setPageId(profile.getId()); } } } pc.setPortletId(id); return pc; } /** * Fetches the parameters out of a PSML Entry * * @param entry the Entry to check for parameters * @return a Map containing the parameters names/values, an empty Map * is returned if there are no parameters */ protected static Map getParameters( Entry entry ) { Hashtable hash = new Hashtable(); Parameter[] props = entry.getParameter(); for(int i = 0; i < props.length; ++i) { hash.put(props[i].getName(), props[i].getValue() ); } return hash; } /** Create a MetaData object from a PSML Metainfo object @param meta the Metainfo to copy @return the new MetaData object, empty if meta is null */ protected static MetaData getMetaData(Entry entry) { MetaData data = new MetaData(); MetaInfo meta = entry.getMetaInfo(); if ( meta != null ) { if ( meta.getTitle() != null ) data.setTitle( meta.getTitle() ); if ( meta.getDescription() != null ) data.setDescription( meta.getDescription() ); if ( meta.getImage() != null ) data.setImage( meta.getImage() ); } if ( entry.getParent() != null ) { PortletEntry parent = (PortletEntry)Registry .getEntry( Registry.PORTLET, entry.getParent() ); if (parent != null) { MetaData parentData = getMetaData( parent ); parentData.merge(data); return parentData; } } return data; } /** Create a MetaData object from a registry Metainfo object @param meta the Metainfo to copy @return the new MetaData object, empty if meta is null */ protected static MetaData getMetaData(PortletEntry entry) { MetaData data = new MetaData(); if ( entry.getTitle() != null ) data.setTitle( entry.getTitle() ); if ( entry.getDescription() != null ) data.setDescription( entry.getDescription() ); if ( entry.getMetaInfo() != null && entry.getMetaInfo().getImage() != null ) data.setImage( entry.getMetaInfo().getImage() ); return data; } /** * @param Entry entry Entry whose parent we want * @return PortletEntry Parent of Entry * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a> */ protected static PortletEntry getParentEntry(PortletEntry entry) { PortletEntry result = null; String parent = entry.getParent(); if (parent != null) { result = (PortletEntry) Registry.getEntry(Registry.PORTLET, parent); } return result; } /** * Retruns the classname defined for this PortletEntry. * If no classname was defined, the parent is queried * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a> */ protected String getClassname(PortletEntry entry) { String className = entry.getClassname(); if (className == null) { PortletEntry parent = getParentEntry(entry); if (parent != null) { // We must walk up the hierarchy just to be safe className = getClassname(parent); } } return className; } /** * Maps all parameters, not found within the <code>entry</code>, from * the <code>entry</code>'s parent into the entry * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a> */ protected void addParentInitParameters(PortletEntry entry, Map hash) { // Now map any parameters from the parent that the child does not have PortletEntry parent = getParentEntry(entry); if (parent != null) { Map parentMap = parent.getParameterMap(); Iterator names = parent.getParameterNames(); while (names.hasNext()) { String key = (String) names.next(); if (!hash.containsKey(key)) { hash.put(key, parentMap.get(key)); } } // Always make sure to get the entire inheritence chain addParentInitParameters(parent, hash); } } /** * Figures out how to produce a security reference for * this portlet. */ protected SecurityReference getSecurityReference(Entry entry, PortletEntry pEntry) { // If something happended during init() that prevented this if (runDataService == null) { this.runDataService = (JetspeedRunDataService) TurbineServices.getInstance().getService( RunDataService.SERVICE_NAME); } JetspeedRunData rundata = runDataService.getCurrentRunData(); return JetspeedSecurity.getSecurityReference(entry, rundata); } }