1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.portal.portlets;
18
19
20 import org.apache.jetspeed.capability.CapabilityMap;
21 import org.apache.jetspeed.capability.CapabilityMapFactory;
22 import org.apache.jetspeed.om.registry.MediaTypeEntry;
23 import org.apache.jetspeed.om.registry.PortletEntry;
24 import org.apache.jetspeed.portal.BasePortletConfig;
25 import org.apache.jetspeed.portal.expire.Expire;
26 import org.apache.jetspeed.portal.expire.ExpireFactory;
27 import org.apache.jetspeed.portal.Portlet;
28 import org.apache.jetspeed.portal.PortletConfig;
29 import org.apache.jetspeed.portal.PortletException;
30 import org.apache.jetspeed.portal.PortletState;
31 import org.apache.jetspeed.services.persistence.PersistenceManager;
32 import org.apache.jetspeed.services.persistence.PortalPersistenceException;
33 import org.apache.jetspeed.portal.PortletInstance;
34 import org.apache.jetspeed.services.portletcache.Cacheable;
35 import org.apache.jetspeed.services.Registry;
36 import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
37 import org.apache.jetspeed.services.logging.JetspeedLogger;
38 import org.apache.jetspeed.util.JetspeedException;
39 import org.apache.jetspeed.util.MetaData;
40 import org.apache.jetspeed.util.MimeType;
41
42
43 import org.apache.jetspeed.util.JetspeedClearElement;
44 import org.apache.ecs.ConcreteElement;
45
46
47 import org.apache.turbine.services.cache.CachedObject;
48 import org.apache.turbine.services.cache.Refreshable;
49 import org.apache.turbine.util.RunData;
50
51
52 import java.util.Hashtable;
53 import java.util.Iterator;
54 /***
55 <p>
56 Should be used by most Portlets that wish to conform to default behavior
57 </p>
58
59 <p>
60 PERFORMANCE NOTE:
61
62 getContent returns a StringElement that was generated on setContent(). This is
63 used so that performance is increased since ECS does not have to work overtime
64 to generate output.
65 </p>
66
67 @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 */
74 public abstract class AbstractPortlet implements Portlet, PortletState, Cacheable, Refreshable
75 {
76
77 /***
78 * Static initialization of the logger for this class
79 */
80 private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(AbstractPortlet.class.getName());
81
82 private boolean cacheable = true;
83 private PortletConfig pc = null;
84
85 /***
86 Provide a required name for this Portlet
87 */
88 private String name = null;
89
90 /***
91 Provide a Unique Portlet ID
92 */
93 private String id = null;
94
95 /***
96 Cache handle for this object.
97 */
98 private String handle = "";
99
100 /***
101 Expiration time of object in milliseconds since the standard base time
102 known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
103 */
104 private Long expirationMillis = null;
105
106 /***
107 Holds instances of ConcreteElements (Portlet output/content)
108 based on its current CapabilityMap.
109 */
110 protected Hashtable content = new Hashtable();
111
112 /***
113 The time this portlet was created.
114 */
115 private long creationTime;
116
117 /***
118 * Handle to cached object
119 */
120 private CachedObject cachedObject = null;
121
122 /***
123 */
124 protected void clearContent() {
125 this.content.clear();
126 }
127
128 /***
129 */
130 protected void setContent( ConcreteElement content ) {
131 this.setContent( content,
132 CapabilityMapFactory.getDefaultCapabilityMap() );
133 }
134
135 /***
136 */
137 protected void setContent( String content ) {
138 this.setContent( new JetspeedClearElement( content ),
139 CapabilityMapFactory.getDefaultCapabilityMap() );
140 }
141
142 /***
143 */
144 protected void setContent( ConcreteElement content,
145 CapabilityMap map )
146 throws IllegalArgumentException
147 {
148 CapabilityMap mymap = map;
149 if ( mymap == null ) {
150 mymap = CapabilityMapFactory.getDefaultCapabilityMap();
151 }
152
153 ConcreteElement buffer = new JetspeedClearElement( content.toString( ) );
154 this.content.put( mymap.toString(), buffer );
155 }
156
157
158
159
160
161
162 /***
163 * Usually called by caching system when portlet is marked as expired, but
164 * has not be idle longer then TimeToLive.
165 *
166 * This method should be implement in cachable portlets
167 */
168 public void refresh() {
169
170
171
172
173
174 logger.debug( "AbstractPortlet - Refreshing " + this.getName() );
175 }
176
177
178
179
180
181 /***
182 * Is this portlet cacheable. It is the portlet's responsability to
183 * cache the content.
184 *
185 * @return <CODE>true</CODE> Cachable<BR>
186 * <CODE>false</CODE> Not cachable
187 */
188 public boolean isCacheable() {
189 return this.cacheable;
190 }
191
192 /***
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 cachable
197 */
198 public void setCacheable(boolean cacheable) {
199 this.cacheable = cacheable;
200 }
201
202
203 /***
204 * Used by a Cacheable object to determine when it should expire itself from the cache.
205 *
206 * @return Expire
207 */
208 public Expire getExpire() {
209 try {
210 return ExpireFactory.getExpire( this, ExpireFactory.NO_EXPIRE );
211 } catch ( JetspeedException e ) {
212 logger.error("Exception", e);
213 return null;
214 }
215 }
216
217 /***
218 * <p>Used by the cache to get a unique reference on what you want to add
219 * and then retrieve in the future from the cache</p>
220 *
221 * <p>Most implementations should just call the CacheHandleManager with
222 * the given params within the implementation and just return this.</p>
223 *
224 * @return Cache handle (key)
225 */
226 public final String getHandle() {
227 return this.handle;
228 }
229
230 /***
231 * Used by a Cacheable object to determine when it should
232 * expire itself from the cache.
233 *
234 * @param handle Cache Handle
235 *
236 * @deprecated cacheable classes should now implement a static getHandle(config) method
237 */
238 public final void setHandle( String handle ) {
239 this.handle = handle;
240 }
241
242 /***
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 */
247 public Long getExpirationMillis() {
248 return this.expirationMillis;
249 }
250
251 /***
252 * Sets the cache expiration time. When the portlet is stale (expired),
253 * the refresh() will be called if the portlet has not been untouched
254 * longer then then it's TimeToLive.
255 *
256 * @param expirationMillis setExpirationMillis Expiration in milliseconds since epoch
257 */
258 public void setExpirationMillis( long expirationMillis) {
259 this.expirationMillis = new Long(expirationMillis);
260
261 if (cachedObject != null) {
262 long expirationInterval = this.expirationMillis.longValue() - cachedObject.getCreated();
263 if (expirationInterval > 0) {
264 cachedObject.setExpires(expirationInterval);
265 } else {
266 cachedObject.setStale(true);
267 }
268 }
269 }
270
271 /***
272 * Builds a new cache handle for this cacheable class with the specified
273 * config object.
274 *
275 * @param config The configuration object to use for building the handle
276 *
277 * @return A cache handle
278 */
279 public static Object getHandle(Object config)
280 {
281
282
283 PortletConfig pc = null;
284
285 if (!(config instanceof PortletConfig))
286 {
287 return null;
288
289 }
290
291
292 pc = (PortletConfig)config;
293 StringBuffer handle = new StringBuffer(256);
294
295 if (pc.getURL()!=null && pc.isCachedOnURL())
296 {
297 handle.append(String.valueOf(pc.getURL().hashCode()));
298 }
299
300 Iterator i = pc.getInitParameterNames();
301 while(i.hasNext())
302 {
303 String name = (String)i.next();
304 String value = pc.getInitParameter(name);
305
306 if (value!=null)
307 {
308 handle.append("|").append(name).append("-").append(value);
309 }
310 }
311
312 return handle.toString();
313 }
314
315 /***
316 * Set this portlet's cached object.
317 *
318 * @param cachedObject Cached Object associated to this portlet
319 */
320 public void setCachedObject(CachedObject cachedObject) {
321 this.cachedObject = cachedObject;
322 }
323
324
325
326
327
328 /***
329 * Get the portlet's name
330 *
331 * @return Name of the portlet
332 */
333 public String getName()
334 {
335
336 if ( name == null )
337 {
338 if (getPortletConfig()!=null)
339 {
340 if (getPortletConfig().getName()!=null)
341 {
342 return getPortletConfig().getName();
343 }
344 else
345 {
346 return this.getClass().getName();
347 }
348 }
349 }
350
351 return name;
352
353 }
354
355 /***
356 * Set the name of the portlet
357 *
358 * @param name Name of the portlet
359 */
360 public void setName( String name ) {
361 this.name = name;
362 }
363
364 /***
365 * Get the config of this servlet
366 *
367 * @return PortletConfig Portlet
368 */
369 public PortletConfig getPortletConfig() {
370 return this.pc;
371 }
372
373 /***
374 * Set's the configuration of this servlet.
375 */
376 public void setPortletConfig( PortletConfig pc ) {
377 this.pc = pc;
378 }
379
380 /***
381 * @param rundata The RunData object for the current request
382 */
383 public ConcreteElement getContent( RunData rundata ) {
384
385 return getContent( rundata, null , true );
386 }
387
388 public ConcreteElement getContent( RunData rundata, CapabilityMap map ) {
389 CapabilityMap mymap = map;
390 if ( mymap == null ) mymap = CapabilityMapFactory.getCapabilityMap( rundata );
391
392 return (ConcreteElement)content.get( mymap.toString() );
393 }
394
395 /***
396 * @param rundata The RunData object for the current request
397 */
398 public ConcreteElement getContent( RunData rundata,
399 CapabilityMap map,
400 boolean allowRecurse ) {
401
402 CapabilityMap mymap = map;
403 if ( mymap == null ) mymap = CapabilityMapFactory.getCapabilityMap( rundata );
404
405 ConcreteElement element = (ConcreteElement)content.get( mymap.toString() );
406
407 if ( element == null ) {
408 if ( allowRecurse ) {
409 try {
410
411 init( );
412 element = getContent( rundata, mymap, false );
413 if( element != null ) {
414
415 this.setContent( element, mymap );
416 }
417 } catch (Exception e) {
418 element = new JetspeedClearElement("Error when retrieving Portlet contents");
419 if( logger.isDebugEnabled() ) {
420 logger.debug( "Error when retrieving Portlet contents", e );
421 }
422 }
423 } else {
424 if( element == null ) {
425
426 mymap = CapabilityMapFactory.getDefaultCapabilityMap();
427 element = (ConcreteElement)content.get( mymap.toString() );
428 if( element == null ) {
429 element = new JetspeedClearElement("Unknown Problem getting Contents");
430 }
431 }
432 }
433 }
434
435 return element;
436
437 }
438
439 /***
440 * Provide a description within PML if the user has specified one.
441 *
442 * @return a null entry if the user hasn't defined anything
443 */
444 public String getDescription() {
445 if (getPortletConfig()!=null)
446 if (getPortletConfig().getMetainfo()!=null)
447 return getPortletConfig().getMetainfo().getDescription();
448
449 return null;
450 }
451
452 /***
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 description
456 */
457 public String getDescription(String instanceDescription)
458 {
459 if (instanceDescription != null)
460 return instanceDescription;
461 return getDescription();
462 }
463
464 /***
465 */
466 public void setDescription( String description ) {
467 PortletConfig pc = getPortletConfig();
468 if (pc==null) {
469 pc = new BasePortletConfig();
470 setPortletConfig(pc);
471 }
472
473 MetaData meta = pc.getMetainfo();
474 if (meta==null) {
475 meta = new MetaData();
476 pc.setMetainfo(meta);
477 }
478
479 meta.setDescription(description);
480 }
481
482
483
484 /***
485 * Provide a title within PML if the user has specified one.
486 *
487 * @return a null entry if the user hasn't defined anything
488 */
489 public String getTitle()
490 {
491 if (getPortletConfig()!=null)
492 if (getPortletConfig().getMetainfo()!=null)
493 return getPortletConfig().getMetainfo().getTitle();
494
495 return null;
496 }
497
498 /***
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 title
502 */
503 public String getTitle(String instanceTitle)
504 {
505 if (instanceTitle != null)
506 return instanceTitle;
507 return getTitle();
508 }
509
510 /***
511 * Set the title for this Portlet.
512 * @param title Portlet title.
513 */
514 public void setTitle( String title ) {
515 PortletConfig pc = getPortletConfig();
516 if (pc==null) {
517 pc = new BasePortletConfig();
518 setPortletConfig(pc);
519 }
520
521 MetaData meta = pc.getMetainfo();
522 if (meta==null) {
523 meta = new MetaData();
524 pc.setMetainfo(meta);
525 }
526
527 meta.setTitle(title);
528 }
529
530
531 /***
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 */
535 public String getImage()
536 {
537 if (getPortletConfig()!=null)
538 if (getPortletConfig().getMetainfo()!=null)
539 return getPortletConfig().getMetainfo().getImage();
540
541 return null;
542 }
543
544 /***
545 * Getter for property image.
546 * @return a null if entry AND portlet have not defined an icon.
547 */
548 public String getImage(String instanceImage)
549 {
550 if (instanceImage != null)
551 return instanceImage;
552 return getImage();
553 }
554
555 public void setImage( String image )
556 {
557 PortletConfig pc = getPortletConfig();
558 if (pc==null) {
559 pc = new BasePortletConfig();
560 setPortletConfig(pc);
561 }
562
563 MetaData meta = pc.getMetainfo();
564 if (meta==null) {
565 meta = new MetaData();
566 pc.setMetainfo(meta);
567 }
568
569 meta.setImage(image);
570 }
571
572 /***
573 * Is the portled editable/customizeable.
574 * @param rundata The RunData object for the current request
575 * @return <CODE>true</CODE> Editing is allow
576 * <CODE>false</CODE> Editing is NOT alowed
577 */
578 public boolean getAllowEdit( RunData rundata )
579 {
580 return allowCustomize(rundata);
581 }
582
583 /***
584 * Is the portled viewable.
585 * @param rundata The RunData object for the current request
586 * @return <CODE>true</CODE> Viewing is allow
587 * <CODE>false</CODE> Viewing is NOT alowed
588 *
589 * Override this method to control your own View behavior
590 */
591 public boolean getAllowView( RunData rundata )
592 {
593 return allowView( rundata );
594 }
595
596 /***
597 * Can this portlet be maximized
598 * @param rundata The RunData object for the current request
599 * @return <CODE>true</CODE> Portlet can be maximized<br>
600 * <CODE>false</CODE> Portlet can NOT be maximized
601 */
602 public boolean getAllowMaximize( RunData rundata )
603 {
604 return allowMaximize( rundata );
605 }
606
607 /***
608 * By default don't provide any initialization
609 */
610 public void init( ) throws PortletException
611 {
612
613 clearContent();
614 }
615
616 /***
617 */
618 public long getCreationTime() {
619 return this.creationTime;
620 }
621
622 /***
623 */
624 public void setCreationTime( long creationTime ) {
625 this.creationTime = creationTime;
626 }
627
628 /***
629 */
630 public boolean supportsType( MimeType mimeType )
631 {
632 PortletEntry entry = (PortletEntry)Registry.getEntry(Registry.PORTLET, getName() );
633 String baseType = mimeType.toString();
634 if (entry!=null)
635 {
636 Iterator i = entry.listMediaTypes();
637
638 while(i.hasNext())
639 {
640 String name = (String)i.next();
641 MediaTypeEntry media = (MediaTypeEntry)Registry.getEntry(Registry.MEDIA_TYPE, name);
642 if (media != null)
643 {
644 if (baseType.equals(media.getMimeType())) return true;
645 }
646 }
647 }
648
649 return MimeType.HTML.equals( mimeType );
650 }
651
652
653
654
655
656 /***
657 * Implements the default close behavior:
658 * security permissions will be checked.
659 *
660 * @param rundata The RunData object for the current request
661 */
662 public boolean allowClose( RunData rundata )
663 {
664
665
666 return !isClosed( rundata );
667 }
668
669 /***
670 * Returns true if this portlet is currently closed
671 *
672 * @param rundata The RunData object for the current request
673 */
674 public boolean isClosed(RunData rundata)
675 {
676 return this.getAttribute("_display", "normal", rundata ).equals("closed");
677 }
678
679 /***
680 * Toggles the portlet state between closed and normal
681 *
682 * @param minimized the new portlet state
683 * @param rundata The RunData object for the current request
684 */
685 public void setClosed(boolean close, RunData rundata)
686 {
687 if( allowClose( rundata ) )
688 {
689 this.setAttribute("_display", close ? "closed" : "normal", rundata );
690 }
691 }
692
693 /***
694 * Implements the default info behavior:
695 * security permissions will be checked.
696 *
697 * @param rundata The RunData object for the current request
698 */
699 public boolean allowInfo( RunData rundata )
700 {
701
702
703 return true;
704 }
705
706 /***
707 * Implements the default customize behavior:
708 * security permissions will be checked.
709 *
710 * @param rundata The RunData object for the current request
711 */
712 public boolean allowCustomize( RunData rundata )
713 {
714
715
716 return true;
717 }
718
719 /***
720 * Implements the default maximize behavior:
721 * security permissions will be checked.
722 *
723 * @param rundata The RunData object for the current request
724 */
725 public boolean allowMaximize( RunData rundata )
726 {
727
728
729 return true;
730 }
731
732 /***
733 * Implements the default info behavior:
734 * security permissions will be checked.
735 *
736 * @param rundata The RunData object for the current request
737 */
738 public boolean allowMinimize( RunData rundata )
739 {
740
741
742 return true;
743 }
744
745 /***
746 * Implements the default view behavior:
747 * security permissions will be checked.
748 *
749 * @param rundata The RunData object for the current request
750 */
751 public boolean allowView( RunData rundata )
752 {
753
754
755 return true;
756 }
757
758 /***
759 * Implements the default print friendly format behavior:
760 * security permissions will be checked.
761 *
762 * @param rundata The RunData object for the current request
763 */
764 public boolean allowPrintFriendly( RunData rundata )
765 {
766
767
768 return true;
769 }
770
771 /***
772 * Returns true if this portlet is currently minimized
773 *
774 * @param rundata The RunData object for the current request
775 */
776 public boolean isMinimized(RunData rundata)
777 {
778 return this.getAttribute("_display", "normal", rundata ).equals("minimized");
779 }
780
781 /***
782 * Change the portlet visibility state ( minimized <-> normal )
783 *
784 * @param minimize True if the portlet change to minimized
785 * @param rundata The RunData object for the current request
786 */
787 public void setMinimized( boolean minimize, RunData rundata )
788 {
789 if( allowMinimize( rundata ) )
790 {
791 this.setAttribute("_display", minimize ? "minimized" : "normal", rundata );
792 }
793 }
794
795 /***
796 * Returns TRUE if the title bar in should be displayed. The title bar includes
797 * the portlet title and action buttons. This
798 *
799 * @param rundata The RunData object for the current request
800 */
801 public boolean isShowTitleBar(RunData rundata)
802 {
803 if (getPortletConfig()!=null)
804 {
805
806 return Boolean.valueOf(getPortletConfig().getInitParameter("_showtitlebar","true")).booleanValue();
807 }
808 return this.getAttribute("_showtitlebar", "true", rundata ).equals("true");
809 }
810
811
812 /***
813 * Retrieve a portlet attribute from persistent storage
814 *
815 * @param attrName The attribute to retrieve
816 * @param attrDefValue The value if the attr doesn't exists
817 * @param rundata The RunData object for the current request
818 * @return The attribute value
819 */
820 public String getAttribute( String attrName, String attrDefValue, RunData rundata )
821 {
822 String attrValue = null ;
823
824 PortletInstance instance = PersistenceManager.getInstance(this, rundata);
825 attrValue = instance.getAttribute(attrName, attrDefValue);
826
827 return attrValue;
828 }
829
830 /***
831 * Stores a portlet attribute in persistent storage
832 *
833 * @param attrName The attribute to retrieve
834 * @paarm attrValue The value to store
835 * @param rundata The RunData object for the current request
836 */
837 public void setAttribute( String attrName, String attrValue, RunData rundata )
838 {
839 try
840 {
841 PortletInstance instance = PersistenceManager.getInstance(this, rundata);
842 instance.setAttribute(attrName, attrValue);
843 PersistenceManager.store(instance);
844 }
845 catch (PortalPersistenceException e)
846 {
847 logger.error("Exception while setting attribute "+attrName+" for portlet "+getName(), e);
848 }
849 }
850
851 /***
852 * Gets the portlet instance associated with this portlet.
853 *
854 * @param rundata The RunData object for the current request
855 * @return PortletInstance
856 */
857 public PortletInstance getInstance(RunData rundata)
858 {
859 return PersistenceManager.getInstance(this, rundata);
860 }
861
862
863
864
865 public String getID()
866 {
867 return id;
868 }
869
870 public void setID(String id)
871 {
872 this.id = id;
873 }
874
875 /***
876 * @return true if the portlet does its own customization
877 */
878 public boolean providesCustomization()
879 {
880 return false;
881 }
882
883 }