/*
* 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.portal.portlets;
//jetspeed
import org.apache.jetspeed.capability.CapabilityMap;
import org.apache.jetspeed.capability.CapabilityMapFactory;
import org.apache.jetspeed.om.registry.MediaTypeEntry;
import org.apache.jetspeed.om.registry.PortletEntry;
import org.apache.jetspeed.portal.BasePortletConfig;
import org.apache.jetspeed.portal.expire.Expire;
import org.apache.jetspeed.portal.expire.ExpireFactory;
import org.apache.jetspeed.portal.Portlet;
import org.apache.jetspeed.portal.PortletConfig;
import org.apache.jetspeed.portal.PortletException;
import org.apache.jetspeed.portal.PortletState;
import org.apache.jetspeed.services.persistence.PersistenceManager;
import org.apache.jetspeed.services.persistence.PortalPersistenceException;
import org.apache.jetspeed.portal.PortletInstance;
import org.apache.jetspeed.services.portletcache.Cacheable;
import org.apache.jetspeed.services.Registry;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.util.JetspeedException;
import org.apache.jetspeed.util.MetaData;
import org.apache.jetspeed.util.MimeType;
//ecs
import org.apache.jetspeed.util.JetspeedClearElement;
import org.apache.ecs.ConcreteElement;
//turbine stuff
import org.apache.turbine.services.cache.CachedObject;
import org.apache.turbine.services.cache.Refreshable;
import org.apache.turbine.util.RunData;
//java stuff
import java.util.Hashtable;
import java.util.Iterator;
/**
<p>
Should be used by most Portlets that wish to conform to default behavior
</p>
<p>
PERFORMANCE NOTE:
getContent returns a StringElement that was generated on setContent(). This is
used so that performance is increased since ECS does not have to work overtime
to generate output.
</p>
@author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>
@author <A HREF="mailto:raphael@apache.org">Rapha謖 Luta</A>
@author <A HREF="mailto:sgala@apache.org">Santiago Gala</A>
@author <A HREF="mailto:paulsp@apache.org">Paul Spencer</A>
@author <A HREF="mailto:morciuch@apache.org">Mark Orciuch</A>
@version $Id: AbstractPortlet.java,v 1.65 2004/03/29 21:38:42 taylor Exp $
*/
public abstract class AbstractPortlet implements Portlet, PortletState, Cacheable, Refreshable
{
/**
* Static initialization of the logger for this class
*/
private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(AbstractPortlet.class.getName());
private boolean cacheable = true;
private PortletConfig pc = null;
/**
Provide a required name for this Portlet
*/
private String name = null;
/**
Provide a Unique Portlet ID
*/
private String id = null;
/**
Cache handle for this object.
*/
private String handle = "";
/**
Expiration time of object in milliseconds since the standard base time
known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
*/
private Long expirationMillis = null;
/**
Holds instances of ConcreteElements (Portlet output/content)
based on its current CapabilityMap.
*/
protected Hashtable content = new Hashtable();
/**
The time this portlet was created.
*/
private long creationTime;
/**
* Handle to cached object
*/
private CachedObject cachedObject = null;
/**
*/
protected void clearContent() {
this.content.clear();
}
/**
*/
protected void setContent( ConcreteElement content ) {
this.setContent( content,
CapabilityMapFactory.getDefaultCapabilityMap() );
}
/**
*/
protected void setContent( String content ) {
this.setContent( new JetspeedClearElement( content ),
CapabilityMapFactory.getDefaultCapabilityMap() );
}
/**
*/
protected void setContent( ConcreteElement content,
CapabilityMap map )
throws IllegalArgumentException
{
CapabilityMap mymap = map;
if ( mymap == null ) {
mymap = CapabilityMapFactory.getDefaultCapabilityMap();
}
ConcreteElement buffer = new JetspeedClearElement( content.toString( ) );
this.content.put( mymap.toString(), buffer );
}
/*
* Implement methods required by Refreshable
*/
/**
* Usually called by caching system when portlet is marked as expired, but
* has not be idle longer then TimeToLive.
*
* This method should be implement in cachable portlets
*/
public void refresh() {
/*
* The following message is here to add in debugging. It is
* expected the any portlet type that is refreshable will
* implement this method.
*/
logger.debug( "AbstractPortlet - Refreshing " + this.getName() );
}
/*
* Implement methods required by Cacheable
*/
/**
* Is this portlet cacheable. It is the portlet's responsability to
* cache the content.
*
* @return <CODE>true</CODE> Cachable<BR>
* <CODE>false</CODE> Not cachable
*/
public boolean isCacheable() {
return this.cacheable;
}
/**
* Set cachable. This should only be called in the portlet's init().
*
* @param cacheable <CODE>true</CODE> Portlet is cachable<BR>
* <CODE>false</CODE> Portlet is NOT cachable
*/
public void setCacheable(boolean cacheable) {
this.cacheable = cacheable;
}
/**
* Used by a Cacheable object to determine when it should expire itself from the cache.
*
* @return Expire
*/
public Expire getExpire() {
try {
return ExpireFactory.getExpire( this, ExpireFactory.NO_EXPIRE );
} catch ( JetspeedException e ) {
logger.error("Exception", e);
return null;
}
}
/**
* <p>Used by the cache to get a unique reference on what you want to add
* and then retrieve in the future from the cache</p>
*
* <p>Most implementations should just call the CacheHandleManager with
* the given params within the implementation and just return this.</p>
*
* @return Cache handle (key)
*/
public final String getHandle() {
return this.handle;
}
/**
* Used by a Cacheable object to determine when it should
* expire itself from the cache.
*
* @param handle Cache Handle
*
* @deprecated cacheable classes should now implement a static getHandle(config) method
*/
public final void setHandle( String handle ) {
this.handle = handle;
}
/**
* Set the expiration time in milliseconds.
*
* @return Expiration time in milliseconds since epoch, or null if the expiration was not set.
*/
public Long getExpirationMillis() {
return this.expirationMillis;
}
/**
* Sets the cache expiration time. When the portlet is stale (expired),
* the refresh() will be called if the portlet has not been untouched
* longer then then it's TimeToLive.
*
* @param expirationMillis setExpirationMillis Expiration in milliseconds since epoch
*/
public void setExpirationMillis( long expirationMillis) {
this.expirationMillis = new Long(expirationMillis);
if (cachedObject != null) {
long expirationInterval = this.expirationMillis.longValue() - cachedObject.getCreated();
if (expirationInterval > 0) {
cachedObject.setExpires(expirationInterval);
} else {
cachedObject.setStale(true);
}
}
}
/**
* Builds a new cache handle for this cacheable class with the specified
* config object.
*
* @param config The configuration object to use for building the handle
*
* @return A cache handle
*/
public static Object getHandle(Object config)
{
//this implementation expects a PortletConfig object as its
// configuration
PortletConfig pc = null;
if (!(config instanceof PortletConfig))
{
return null;
}
// By default, only take into account the init parameters
pc = (PortletConfig)config;
StringBuffer handle = new StringBuffer(256);
if (pc.getURL()!=null && pc.isCachedOnURL())
{
handle.append(String.valueOf(pc.getURL().hashCode()));
}
Iterator i = pc.getInitParameterNames();
while(i.hasNext())
{
String name = (String)i.next();
String value = pc.getInitParameter(name);
if (value!=null)
{
handle.append("|").append(name).append("-").append(value);
}
}
return handle.toString();
}
/**
* Set this portlet's cached object.
*
* @param cachedObject Cached Object associated to this portlet
*/
public void setCachedObject(CachedObject cachedObject) {
this.cachedObject = cachedObject;
}
/*
* Implement methods required by Portlet
*/
/**
* Get the portlet's name
*
* @return Name of the portlet
*/
public String getName()
{
if ( name == null )
{
if (getPortletConfig()!=null)
{
if (getPortletConfig().getName()!=null)
{
return getPortletConfig().getName();
}
else
{
return this.getClass().getName();
}
}
}
return name;
}
/**
* Set the name of the portlet
*
* @param name Name of the portlet
*/
public void setName( String name ) {
this.name = name;
}
/**
* Get the config of this servlet
*
* @return PortletConfig Portlet
*/
public PortletConfig getPortletConfig() {
return this.pc;
}
/**
* Set's the configuration of this servlet.
*/
public void setPortletConfig( PortletConfig pc ) {
this.pc = pc;
}
/**
* @param rundata The RunData object for the current request
*/
public ConcreteElement getContent( RunData rundata ) {
return getContent( rundata, null , true );
}
public ConcreteElement getContent( RunData rundata, CapabilityMap map ) {
CapabilityMap mymap = map;
if ( mymap == null ) mymap = CapabilityMapFactory.getCapabilityMap( rundata );
return (ConcreteElement)content.get( mymap.toString() );
}
/**
* @param rundata The RunData object for the current request
*/
public ConcreteElement getContent( RunData rundata,
CapabilityMap map,
boolean allowRecurse ) {
CapabilityMap mymap = map;
if ( mymap == null ) mymap = CapabilityMapFactory.getCapabilityMap( rundata );
ConcreteElement element = (ConcreteElement)content.get( mymap.toString() );
if ( element == null ) {
if ( allowRecurse ) {
try {
// init will put content under default cmap
init( );
element = getContent( rundata, mymap, false );
if( element != null ) {
// now we put it under our cmap
this.setContent( element, mymap );
}
} catch (Exception e) {
element = new JetspeedClearElement("Error when retrieving Portlet contents");
if( logger.isDebugEnabled() ) {
logger.debug( "Error when retrieving Portlet contents", e );
}
}
} else {
if( element == null ) {
//FIXME: Let's asume that the contents under "default" map are good
mymap = CapabilityMapFactory.getDefaultCapabilityMap();
element = (ConcreteElement)content.get( mymap.toString() );
if( element == null ) {
element = new JetspeedClearElement("Unknown Problem getting Contents");
}
}
}
}
return element;
}
/**
* Provide a description within PML if the user has specified one.
*
* @return a null entry if the user hasn't defined anything
*/
public String getDescription() {
if (getPortletConfig()!=null)
if (getPortletConfig().getMetainfo()!=null)
return getPortletConfig().getMetainfo().getDescription();
return null;
}
/**
* Provide a Description within PML if the user has specified one.
*
* @return a null if entry AND portlet have not defined a description
*/
public String getDescription(String instanceDescription)
{
if (instanceDescription != null)
return instanceDescription;
return getDescription();
}
/**
*/
public void setDescription( String description ) {
PortletConfig pc = getPortletConfig();
if (pc==null) {
pc = new BasePortletConfig();
setPortletConfig(pc);
}
MetaData meta = pc.getMetainfo();
if (meta==null) {
meta = new MetaData();
pc.setMetainfo(meta);
}
meta.setDescription(description);
}
//provide default titles so that the user can define them in PML
/**
* Provide a title within PML if the user has specified one.
*
* @return a null entry if the user hasn't defined anything
*/
public String getTitle()
{
if (getPortletConfig()!=null)
if (getPortletConfig().getMetainfo()!=null)
return getPortletConfig().getMetainfo().getTitle();
return null;
}
/**
* Provide a title within PML if the user has specified one.
*
* @return a null if entry AND portlet have not defined a title
*/
public String getTitle(String instanceTitle)
{
if (instanceTitle != null)
return instanceTitle;
return getTitle();
}
/**
* Set the title for this Portlet.
* @param title Portlet title.
*/
public void setTitle( String title ) {
PortletConfig pc = getPortletConfig();
if (pc==null) {
pc = new BasePortletConfig();
setPortletConfig(pc);
}
MetaData meta = pc.getMetainfo();
if (meta==null) {
meta = new MetaData();
pc.setMetainfo(meta);
}
meta.setTitle(title);
}
/**
* Getter for property image.
* @return Name of portlet image, icon. The name is expected to be in the form of a URL.
*/
public String getImage()
{
if (getPortletConfig()!=null)
if (getPortletConfig().getMetainfo()!=null)
return getPortletConfig().getMetainfo().getImage();
return null;
}
/**
* Getter for property image.
* @return a null if entry AND portlet have not defined an icon.
*/
public String getImage(String instanceImage)
{
if (instanceImage != null)
return instanceImage;
return getImage();
}
public void setImage( String image )
{
PortletConfig pc = getPortletConfig();
if (pc==null) {
pc = new BasePortletConfig();
setPortletConfig(pc);
}
MetaData meta = pc.getMetainfo();
if (meta==null) {
meta = new MetaData();
pc.setMetainfo(meta);
}
meta.setImage(image);
}
/**
* Is the portled editable/customizeable.
* @param rundata The RunData object for the current request
* @return <CODE>true</CODE> Editing is allow
* <CODE>false</CODE> Editing is NOT alowed
*/
public boolean getAllowEdit( RunData rundata )
{
return allowCustomize(rundata);
}
/**
* Is the portled viewable.
* @param rundata The RunData object for the current request
* @return <CODE>true</CODE> Viewing is allow
* <CODE>false</CODE> Viewing is NOT alowed
*
* Override this method to control your own View behavior
*/
public boolean getAllowView( RunData rundata )
{
return allowView( rundata );
}
/**
* Can this portlet be maximized
* @param rundata The RunData object for the current request
* @return <CODE>true</CODE> Portlet can be maximized<br>
* <CODE>false</CODE> Portlet can NOT be maximized
*/
public boolean getAllowMaximize( RunData rundata )
{
return allowMaximize( rundata );
}
/**
* By default don't provide any initialization
*/
public void init( ) throws PortletException
{
// make sure to clean all content
clearContent();
}
/**
*/
public long getCreationTime() {
return this.creationTime;
}
/**
*/
public void setCreationTime( long creationTime ) {
this.creationTime = creationTime;
}
/**
*/
public boolean supportsType( MimeType mimeType )
{
PortletEntry entry = (PortletEntry)Registry.getEntry(Registry.PORTLET, getName() );
String baseType = mimeType.toString();
if (entry!=null)
{
Iterator i = entry.listMediaTypes();
while(i.hasNext())
{
String name = (String)i.next();
MediaTypeEntry media = (MediaTypeEntry)Registry.getEntry(Registry.MEDIA_TYPE, name);
if (media != null)
{
if (baseType.equals(media.getMimeType())) return true;
}
}
}
return MimeType.HTML.equals( mimeType );
}
/*
* Implement methods required by PortletState
*/
/**
* Implements the default close behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowClose( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return !isClosed( rundata );
}
/**
* Returns true if this portlet is currently closed
*
* @param rundata The RunData object for the current request
*/
public boolean isClosed(RunData rundata)
{
return this.getAttribute("_display", "normal", rundata ).equals("closed");
}
/**
* Toggles the portlet state between closed and normal
*
* @param minimized the new portlet state
* @param rundata The RunData object for the current request
*/
public void setClosed(boolean close, RunData rundata)
{
if( allowClose( rundata ) )
{
this.setAttribute("_display", close ? "closed" : "normal", rundata );
}
}
/**
* Implements the default info behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowInfo( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return true;
}
/**
* Implements the default customize behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowCustomize( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return true;
}
/**
* Implements the default maximize behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowMaximize( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return true;
}
/**
* Implements the default info behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowMinimize( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return true;
}
/**
* Implements the default view behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowView( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return true;
}
/**
* Implements the default print friendly format behavior:
* security permissions will be checked.
*
* @param rundata The RunData object for the current request
*/
public boolean allowPrintFriendly( RunData rundata )
{
//Security will not allow this call to succeed if there are
//not enough permissions
return true;
}
/**
* Returns true if this portlet is currently minimized
*
* @param rundata The RunData object for the current request
*/
public boolean isMinimized(RunData rundata)
{
return this.getAttribute("_display", "normal", rundata ).equals("minimized");
}
/**
* Change the portlet visibility state ( minimized <-> normal )
*
* @param minimize True if the portlet change to minimized
* @param rundata The RunData object for the current request
*/
public void setMinimized( boolean minimize, RunData rundata )
{
if( allowMinimize( rundata ) )
{
this.setAttribute("_display", minimize ? "minimized" : "normal", rundata );
}
}
/**
* Returns TRUE if the title bar in should be displayed. The title bar includes
* the portlet title and action buttons. This
*
* @param rundata The RunData object for the current request
*/
public boolean isShowTitleBar(RunData rundata)
{
if (getPortletConfig()!=null)
{
// Parameter can exist in PSML or <portlet-entry>
return Boolean.valueOf(getPortletConfig().getInitParameter("_showtitlebar","true")).booleanValue();
}
return this.getAttribute("_showtitlebar", "true", rundata ).equals("true");
}
// utility methods
/**
* Retrieve a portlet attribute from persistent storage
*
* @param attrName The attribute to retrieve
* @param attrDefValue The value if the attr doesn't exists
* @param rundata The RunData object for the current request
* @return The attribute value
*/
public String getAttribute( String attrName, String attrDefValue, RunData rundata )
{
String attrValue = null ;
PortletInstance instance = PersistenceManager.getInstance(this, rundata);
attrValue = instance.getAttribute(attrName, attrDefValue);
return attrValue;
}
/**
* Stores a portlet attribute in persistent storage
*
* @param attrName The attribute to retrieve
* @paarm attrValue The value to store
* @param rundata The RunData object for the current request
*/
public void setAttribute( String attrName, String attrValue, RunData rundata )
{
try
{
PortletInstance instance = PersistenceManager.getInstance(this, rundata);
instance.setAttribute(attrName, attrValue);
PersistenceManager.store(instance);
}
catch (PortalPersistenceException e)
{
logger.error("Exception while setting attribute "+attrName+" for portlet "+getName(), e);
}
}
/**
* Gets the portlet instance associated with this portlet.
*
* @param rundata The RunData object for the current request
* @return PortletInstance
*/
public PortletInstance getInstance(RunData rundata)
{
return PersistenceManager.getInstance(this, rundata);
}
//
// DST: Shouldn't getID and setID be deprecated and added to PortletInstance...
//
public String getID()
{
return id;
}
public void setID(String id)
{
this.id = id;
}
/**
* @return true if the portlet does its own customization
*/
public boolean providesCustomization()
{
return false;
}
}