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.portal.portlets;
1819//jetspeed20import org.apache.jetspeed.capability.CapabilityMap;
21import org.apache.jetspeed.capability.CapabilityMapFactory;
22import org.apache.jetspeed.om.registry.MediaTypeEntry;
23import org.apache.jetspeed.om.registry.PortletEntry;
24import org.apache.jetspeed.portal.BasePortletConfig;
25import org.apache.jetspeed.portal.expire.Expire;
26import org.apache.jetspeed.portal.expire.ExpireFactory;
27import org.apache.jetspeed.portal.Portlet;
28import org.apache.jetspeed.portal.PortletConfig;
29import org.apache.jetspeed.portal.PortletException;
30import org.apache.jetspeed.portal.PortletState;
31import org.apache.jetspeed.services.persistence.PersistenceManager;
32import org.apache.jetspeed.services.persistence.PortalPersistenceException;
33import org.apache.jetspeed.portal.PortletInstance;
34import org.apache.jetspeed.services.portletcache.Cacheable;
35import org.apache.jetspeed.services.Registry;
36import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
37import org.apache.jetspeed.services.logging.JetspeedLogger;
38import org.apache.jetspeed.util.JetspeedException;
39import org.apache.jetspeed.util.MetaData;
40import org.apache.jetspeed.util.MimeType;
4142//ecs43import org.apache.jetspeed.util.JetspeedClearElement;
44import org.apache.ecs.ConcreteElement;
4546//turbine stuff47import org.apache.turbine.services.cache.CachedObject;
48import org.apache.turbine.services.cache.Refreshable;
49import org.apache.turbine.util.RunData;
5051//java stuff52import java.util.Hashtable;
53import java.util.Iterator;
54/***55<p>56Should be used by most Portlets that wish to conform to default behavior57</p>5859<p>60PERFORMANCE NOTE:6162getContent returns a StringElement that was generated on setContent(). This is63used so that performance is increased since ECS does not have to work overtime64to generate output.65</p>6667@author <A HREF="mailto:burton@apache.org">Kevin A. Burton</A>68@author <A HREF="mailto:raphael@apache.org">Raphaël Luta</A>69@author <A HREF="mailto:sgala@apache.org">Santiago Gala</A>70@author <A HREF="mailto:paulsp@apache.org">Paul Spencer</A>71@author <A HREF="mailto:morciuch@apache.org">Mark Orciuch</A>72@version $Id: AbstractPortlet.java,v 1.65 2004/03/29 21:38:42 taylor Exp $73*/74publicabstractclassAbstractPortlet implements Portlet, PortletState, Cacheable, Refreshable
75 {
7677/***78 * Static initialization of the logger for this class79 */80privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(AbstractPortlet.class.getName());
8182privateboolean cacheable = true;
83privatePortletConfig pc = null;
8485/***86 Provide a required name for this Portlet87 */88private String name = null;
8990/***91 Provide a Unique Portlet ID92 */93private String id = null;
9495/***96 Cache handle for this object.97 */98private String handle = "";
99100/***101 Expiration time of object in milliseconds since the standard base time102 known as "the epoch", namely January 1, 1970, 00:00:00 GMT.103 */104private Long expirationMillis = null;
105106/***107 Holds instances of ConcreteElements (Portlet output/content)108 based on its current CapabilityMap.109 */110protected Hashtable content = new Hashtable();
111112/***113 The time this portlet was created.114 */115privatelong creationTime;
116117/***118 * Handle to cached object119 */120private CachedObject cachedObject = null;
121122/***123 */124protectedvoid clearContent() {
125this.content.clear();
126 }
127128/***129 */130protectedvoid setContent( ConcreteElement content ) {
131this.setContent( content,
132 CapabilityMapFactory.getDefaultCapabilityMap() );
133 }
134135/***136 */137protectedvoid setContent( String content ) {
138this.setContent( new JetspeedClearElement( content ),
139 CapabilityMapFactory.getDefaultCapabilityMap() );
140 }
141142/***143 */144protectedvoid setContent( ConcreteElement content,
145CapabilityMap map )
146 throws IllegalArgumentException
147 {
148CapabilityMap mymap = map;
149if ( mymap == null ) {
150 mymap = CapabilityMapFactory.getDefaultCapabilityMap();
151 }
152153 ConcreteElement buffer = newJetspeedClearElement( content.toString( ) );
154this.content.put( mymap.toString(), buffer );
155 }
156157158/*159 * Implement methods required by Refreshable160 */161162/***163 * Usually called by caching system when portlet is marked as expired, but164 * has not be idle longer then TimeToLive.165 *166 * This method should be implement in cachable portlets167 */168publicvoid refresh() {
169/*170 * The following message is here to add in debugging. It is171 * expected the any portlet type that is refreshable will172 * implement this method.173 */174 logger.debug( "AbstractPortlet - Refreshing " + this.getName() );
175 }
176177/*178 * Implement methods required by Cacheable179 */180181/***182 * Is this portlet cacheable. It is the portlet's responsability to183 * cache the content.184 *185 * @return <CODE>true</CODE> Cachable<BR>186 * <CODE>false</CODE> Not cachable187 */188publicboolean isCacheable() {
189returnthis.cacheable;
190 }
191192/***193 * Set cachable. This should only be called in the portlet's init().194 *195 * @param cacheable <CODE>true</CODE> Portlet is cachable<BR>196 * <CODE>false</CODE> Portlet is NOT cachable197 */198publicvoid setCacheable(boolean cacheable) {
199this.cacheable = cacheable;
200 }
201202203/***204 * Used by a Cacheable object to determine when it should expire itself from the cache.205 *206 * @return Expire207 */208publicExpire getExpire() {
209try {
210return ExpireFactory.getExpire( this, ExpireFactory.NO_EXPIRE );
211 } catch ( JetspeedException e ) {
212 logger.error("Exception", e);
213returnnull;
214 }
215 }
216217/***218 * <p>Used by the cache to get a unique reference on what you want to add219 * and then retrieve in the future from the cache</p>220 *221 * <p>Most implementations should just call the CacheHandleManager with222 * the given params within the implementation and just return this.</p>223 *224 * @return Cache handle (key)225 */226publicfinal String getHandle() {
227returnthis.handle;
228 }
229230/***231 * Used by a Cacheable object to determine when it should232 * expire itself from the cache.233 *234 * @param handle Cache Handle235 *236 * @deprecated cacheable classes should now implement a static getHandle(config) method237 */238publicfinalvoid setHandle( String handle ) {
239this.handle = handle;
240 }
241242/***243 * Set the expiration time in milliseconds.244 *245 * @return Expiration time in milliseconds since epoch, or null if the expiration was not set.246 */247public Long getExpirationMillis() {
248returnthis.expirationMillis;
249 }
250251/***252 * Sets the cache expiration time. When the portlet is stale (expired),253 * the refresh() will be called if the portlet has not been untouched254 * longer then then it's TimeToLive.255 *256 * @param expirationMillis setExpirationMillis Expiration in milliseconds since epoch257 */258publicvoid setExpirationMillis( long expirationMillis) {
259this.expirationMillis = new Long(expirationMillis);
260261if (cachedObject != null) {
262long expirationInterval = this.expirationMillis.longValue() - cachedObject.getCreated();
263if (expirationInterval > 0) {
264 cachedObject.setExpires(expirationInterval);
265 } else {
266 cachedObject.setStale(true);
267 }
268 }
269 }
270271/***272 * Builds a new cache handle for this cacheable class with the specified273 * config object.274 *275 * @param config The configuration object to use for building the handle276 *277 * @return A cache handle278 */279publicstatic Object getHandle(Object config)
280 {
281//this implementation expects a PortletConfig object as its282// configuration283PortletConfig pc = null;
284285if (!(config instanceof PortletConfig))
286 {
287returnnull;
288289 }
290291// By default, only take into account the init parameters292 pc = (PortletConfig)config;
293 StringBuffer handle = new StringBuffer(256);
294295if (pc.getURL()!=null && pc.isCachedOnURL())
296 {
297 handle.append(String.valueOf(pc.getURL().hashCode()));
298 }
299300 Iterator i = pc.getInitParameterNames();
301while(i.hasNext())
302 {
303 String name = (String)i.next();
304 String value = pc.getInitParameter(name);
305306if (value!=null)
307 {
308 handle.append("|").append(name).append("-").append(value);
309 }
310 }
311312return handle.toString();
313 }
314315/***316 * Set this portlet's cached object.317 *318 * @param cachedObject Cached Object associated to this portlet319 */320publicvoid setCachedObject(CachedObject cachedObject) {
321this.cachedObject = cachedObject;
322 }
323324/*325 * Implement methods required by Portlet326 */327328/***329 * Get the portlet's name330 *331 * @return Name of the portlet332 */333public String getName()
334 {
335336if ( name == null )
337 {
338if (getPortletConfig()!=null)
339 {
340if (getPortletConfig().getName()!=null)
341 {
342return getPortletConfig().getName();
343 }
344else345 {
346returnthis.getClass().getName();
347 }
348 }
349 }
350351return name;
352353 }
354355/***356 * Set the name of the portlet357 *358 * @param name Name of the portlet359 */360publicvoid setName( String name ) {
361this.name = name;
362 }
363364/***365 * Get the config of this servlet366 *367 * @return PortletConfig Portlet368 */369publicPortletConfig getPortletConfig() {
370returnthis.pc;
371 }
372373/***374 * Set's the configuration of this servlet.375 */376publicvoid setPortletConfig( PortletConfig pc ) {
377this.pc = pc;
378 }
379380/***381 * @param rundata The RunData object for the current request382 */383public ConcreteElement getContent( RunData rundata ) {
384385return getContent( rundata, null , true );
386 }
387388public ConcreteElement getContent( RunData rundata, CapabilityMap map ) {
389CapabilityMap mymap = map;
390if ( mymap == null ) mymap = CapabilityMapFactory.getCapabilityMap( rundata );
391392return (ConcreteElement)content.get( mymap.toString() );
393 }
394395/***396 * @param rundata The RunData object for the current request397 */398public ConcreteElement getContent( RunData rundata,
399CapabilityMap map,
400boolean allowRecurse ) {
401402CapabilityMap mymap = map;
403if ( mymap == null ) mymap = CapabilityMapFactory.getCapabilityMap( rundata );
404405 ConcreteElement element = (ConcreteElement)content.get( mymap.toString() );
406407if ( element == null ) {
408if ( allowRecurse ) {
409try {
410// init will put content under default cmap411 init( );
412 element = getContent( rundata, mymap, false );
413if( element != null ) {
414// now we put it under our cmap415this.setContent( element, mymap );
416 }
417 } catch (Exception e) {
418 element = newJetspeedClearElement("Error when retrieving Portlet contents");
419if( logger.isDebugEnabled() ) {
420 logger.debug( "Error when retrieving Portlet contents", e );
421 }
422 }
423 } else {
424if( element == null ) {
425//FIXME: Let's asume that the contents under "default" map are good426 mymap = CapabilityMapFactory.getDefaultCapabilityMap();
427 element = (ConcreteElement)content.get( mymap.toString() );
428if( element == null ) {
429 element = newJetspeedClearElement("Unknown Problem getting Contents");
430 }
431 }
432 }
433 }
434435return element;
436437 }
438439/***440 * Provide a description within PML if the user has specified one.441 *442 * @return a null entry if the user hasn't defined anything443 */444public String getDescription() {
445if (getPortletConfig()!=null)
446if (getPortletConfig().getMetainfo()!=null)
447return getPortletConfig().getMetainfo().getDescription();
448449returnnull;
450 }
451452/***453 * Provide a Description within PML if the user has specified one.454 *455 * @return a null if entry AND portlet have not defined a description456 */457public String getDescription(String instanceDescription)
458 {
459if (instanceDescription != null)
460return instanceDescription;
461return getDescription();
462 }
463464/***465 */466publicvoid setDescription( String description ) {
467PortletConfig pc = getPortletConfig();
468if (pc==null) {
469 pc = newBasePortletConfig();
470 setPortletConfig(pc);
471 }
472473MetaData meta = pc.getMetainfo();
474if (meta==null) {
475 meta = newMetaData();
476 pc.setMetainfo(meta);
477 }
478479 meta.setDescription(description);
480 }
481482//provide default titles so that the user can define them in PML483484/***485 * Provide a title within PML if the user has specified one.486 *487 * @return a null entry if the user hasn't defined anything488 */489public String getTitle()
490 {
491if (getPortletConfig()!=null)
492if (getPortletConfig().getMetainfo()!=null)
493return getPortletConfig().getMetainfo().getTitle();
494495returnnull;
496 }
497498/***499 * Provide a title within PML if the user has specified one.500 *501 * @return a null if entry AND portlet have not defined a title502 */503public String getTitle(String instanceTitle)
504 {
505if (instanceTitle != null)
506return instanceTitle;
507return getTitle();
508 }
509510/***511 * Set the title for this Portlet.512 * @param title Portlet title.513 */514publicvoid setTitle( String title ) {
515PortletConfig pc = getPortletConfig();
516if (pc==null) {
517 pc = newBasePortletConfig();
518 setPortletConfig(pc);
519 }
520521MetaData meta = pc.getMetainfo();
522if (meta==null) {
523 meta = newMetaData();
524 pc.setMetainfo(meta);
525 }
526527 meta.setTitle(title);
528 }
529530531/***532 * Getter for property image.533 * @return Name of portlet image, icon. The name is expected to be in the form of a URL.534 */535public String getImage()
536 {
537if (getPortletConfig()!=null)
538if (getPortletConfig().getMetainfo()!=null)
539return getPortletConfig().getMetainfo().getImage();
540541returnnull;
542 }
543544/***545 * Getter for property image.546 * @return a null if entry AND portlet have not defined an icon.547 */548public String getImage(String instanceImage)
549 {
550if (instanceImage != null)
551return instanceImage;
552return getImage();
553 }
554555publicvoid setImage( String image )
556 {
557PortletConfig pc = getPortletConfig();
558if (pc==null) {
559 pc = newBasePortletConfig();
560 setPortletConfig(pc);
561 }
562563MetaData meta = pc.getMetainfo();
564if (meta==null) {
565 meta = newMetaData();
566 pc.setMetainfo(meta);
567 }
568569 meta.setImage(image);
570 }
571572/***573 * Is the portled editable/customizeable.574 * @param rundata The RunData object for the current request575 * @return <CODE>true</CODE> Editing is allow576 * <CODE>false</CODE> Editing is NOT alowed577 */578publicboolean getAllowEdit( RunData rundata )
579 {
580return allowCustomize(rundata);
581 }
582583/***584 * Is the portled viewable.585 * @param rundata The RunData object for the current request586 * @return <CODE>true</CODE> Viewing is allow587 * <CODE>false</CODE> Viewing is NOT alowed588 * 589 * Override this method to control your own View behavior590 */591publicboolean getAllowView( RunData rundata )
592 {
593return allowView( rundata );
594 }
595596/***597 * Can this portlet be maximized598 * @param rundata The RunData object for the current request599 * @return <CODE>true</CODE> Portlet can be maximized<br>600 * <CODE>false</CODE> Portlet can NOT be maximized601 */602publicboolean getAllowMaximize( RunData rundata )
603 {
604return allowMaximize( rundata );
605 }
606607/***608 * By default don't provide any initialization609 */610publicvoid init( ) throws PortletException
611 {
612// make sure to clean all content613 clearContent();
614 }
615616/***617 */618publiclong getCreationTime() {
619returnthis.creationTime;
620 }
621622/***623 */624publicvoid setCreationTime( long creationTime ) {
625this.creationTime = creationTime;
626 }
627628/***629 */630publicboolean supportsType( MimeType mimeType )
631 {
632PortletEntry entry = (PortletEntry)Registry.getEntry(Registry.PORTLET, getName() );
633 String baseType = mimeType.toString();
634if (entry!=null)
635 {
636 Iterator i = entry.listMediaTypes();
637638while(i.hasNext())
639 {
640 String name = (String)i.next();
641MediaTypeEntry media = (MediaTypeEntry)Registry.getEntry(Registry.MEDIA_TYPE, name);
642if (media != null)
643 {
644if (baseType.equals(media.getMimeType())) returntrue;
645 }
646 }
647 }
648649return MimeType.HTML.equals( mimeType );
650 }
651652/*653 * Implement methods required by PortletState654 */655656/***657 * Implements the default close behavior:658 * security permissions will be checked.659 *660 * @param rundata The RunData object for the current request661 */662publicboolean allowClose( RunData rundata )
663 {
664//Security will not allow this call to succeed if there are665//not enough permissions666return !isClosed( rundata );
667 }
668669/***670 * Returns true if this portlet is currently closed671 *672 * @param rundata The RunData object for the current request673 */674publicboolean isClosed(RunData rundata)
675 {
676returnthis.getAttribute("_display", "normal", rundata ).equals("closed");
677 }
678679/***680 * Toggles the portlet state between closed and normal681 *682 * @param minimized the new portlet state683 * @param rundata The RunData object for the current request684 */685publicvoid setClosed(boolean close, RunData rundata)
686 {
687if( allowClose( rundata ) )
688 {
689this.setAttribute("_display", close ? "closed" : "normal", rundata );
690 }
691 }
692693/***694 * Implements the default info behavior:695 * security permissions will be checked.696 *697 * @param rundata The RunData object for the current request698 */699publicboolean allowInfo( RunData rundata )
700 {
701//Security will not allow this call to succeed if there are702//not enough permissions703returntrue;
704 }
705706/***707 * Implements the default customize behavior:708 * security permissions will be checked.709 *710 * @param rundata The RunData object for the current request711 */712publicboolean allowCustomize( RunData rundata )
713 {
714//Security will not allow this call to succeed if there are715//not enough permissions716returntrue;
717 }
718719/***720 * Implements the default maximize behavior:721 * security permissions will be checked.722 *723 * @param rundata The RunData object for the current request724 */725publicboolean allowMaximize( RunData rundata )
726 {
727//Security will not allow this call to succeed if there are728//not enough permissions729returntrue;
730 }
731732/***733 * Implements the default info behavior:734 * security permissions will be checked.735 *736 * @param rundata The RunData object for the current request737 */738publicboolean allowMinimize( RunData rundata )
739 {
740//Security will not allow this call to succeed if there are741//not enough permissions742returntrue;
743 }
744745/***746 * Implements the default view behavior:747 * security permissions will be checked.748 *749 * @param rundata The RunData object for the current request750 */751publicboolean allowView( RunData rundata )
752 {
753//Security will not allow this call to succeed if there are754//not enough permissions755returntrue;
756 }
757758/***759 * Implements the default print friendly format behavior:760 * security permissions will be checked.761 *762 * @param rundata The RunData object for the current request763 */764publicboolean allowPrintFriendly( RunData rundata )
765 {
766//Security will not allow this call to succeed if there are767//not enough permissions768returntrue;
769 }
770771/***772 * Returns true if this portlet is currently minimized773 *774 * @param rundata The RunData object for the current request775 */776publicboolean isMinimized(RunData rundata)
777 {
778returnthis.getAttribute("_display", "normal", rundata ).equals("minimized");
779 }
780781/***782 * Change the portlet visibility state ( minimized <-> normal )783 *784 * @param minimize True if the portlet change to minimized785 * @param rundata The RunData object for the current request786 */787publicvoid setMinimized( boolean minimize, RunData rundata )
788 {
789if( allowMinimize( rundata ) )
790 {
791this.setAttribute("_display", minimize ? "minimized" : "normal", rundata );
792 }
793 }
794795/***796 * Returns TRUE if the title bar in should be displayed. The title bar includes797 * the portlet title and action buttons. This798 *799 * @param rundata The RunData object for the current request800 */801publicboolean isShowTitleBar(RunData rundata)
802 {
803if (getPortletConfig()!=null)
804 {
805// Parameter can exist in PSML or <portlet-entry>806return Boolean.valueOf(getPortletConfig().getInitParameter("_showtitlebar","true")).booleanValue();
807 }
808returnthis.getAttribute("_showtitlebar", "true", rundata ).equals("true");
809 }
810// utility methods811812/***813 * Retrieve a portlet attribute from persistent storage814 *815 * @param attrName The attribute to retrieve816 * @param attrDefValue The value if the attr doesn't exists817 * @param rundata The RunData object for the current request818 * @return The attribute value819 */820public String getAttribute( String attrName, String attrDefValue, RunData rundata )
821 {
822 String attrValue = null ;
823824PortletInstance instance = PersistenceManager.getInstance(this, rundata);
825 attrValue = instance.getAttribute(attrName, attrDefValue);
826827return attrValue;
828 }
829830/***831 * Stores a portlet attribute in persistent storage832 *833 * @param attrName The attribute to retrieve834 * @paarm attrValue The value to store835 * @param rundata The RunData object for the current request836 */837publicvoid setAttribute( String attrName, String attrValue, RunData rundata )
838 {
839try840 {
841PortletInstance instance = PersistenceManager.getInstance(this, rundata);
842 instance.setAttribute(attrName, attrValue);
843 PersistenceManager.store(instance);
844 }
845catch (PortalPersistenceException e)
846 {
847 logger.error("Exception while setting attribute "+attrName+" for portlet "+getName(), e);
848 }
849 }
850851/***852 * Gets the portlet instance associated with this portlet.853 *854 * @param rundata The RunData object for the current request855 * @return PortletInstance856 */857publicPortletInstance getInstance(RunData rundata)
858 {
859return PersistenceManager.getInstance(this, rundata);
860 }
861862//863// DST: Shouldn't getID and setID be deprecated and added to PortletInstance...864//865public String getID()
866 {
867return id;
868 }
869870publicvoid setID(String id)
871 {
872this.id = id;
873 }
874875/***876 * @return true if the portlet does its own customization877 */878publicboolean providesCustomization()
879 {
880return false;
881 }
882883 }