View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.decoration;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.Locale;
28  import java.util.Map;
29  import java.util.Properties;
30  import java.util.Set;
31  
32  import javax.servlet.ServletContext;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.jetspeed.cache.CacheElement;
37  import org.apache.jetspeed.cache.JetspeedCache;
38  import org.apache.jetspeed.components.portletregistry.PortletRegistry;
39  import org.apache.jetspeed.decoration.caches.SessionPathResolverCache;
40  import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
41  import org.apache.jetspeed.om.common.portlet.PortletDefinitionComposite;
42  import org.apache.jetspeed.om.page.Fragment;
43  import org.apache.jetspeed.om.page.Page;
44  import org.apache.jetspeed.request.RequestContext;
45  import org.apache.jetspeed.util.Path;
46  import org.apache.jetspeed.desktop.JetspeedDesktop;
47  import org.springframework.web.context.ServletContextAware;
48  
49  /***
50   *
51   * @author <href a="mailto:weaver@apache.org">Scott T. Weaver</a>
52   * @author <a href="mailto:smilek@apache.org">Steve Milek</a>
53   * @see org.apache.jetspeed.decoration.DecorationFactory
54   */
55  public class DecorationFactoryImpl implements DecorationFactory, ServletContextAware
56  {
57      private static final Log log = LogFactory.getLog(DecorationFactoryImpl.class);
58  
59      private final Path decorationsPath;
60      private final Path portletDecorationsPath;
61      private final Path layoutDecorationsPath;
62      private final String portletDecorationsPathStr;
63      private final String layoutDecorationsPathStr;
64      
65      private final ResourceValidator validator;
66      private final PortletRegistry registry;
67      
68      /*** cache to hold decoration Properties objects **/
69      private JetspeedCache decorationConfigurationCache;
70      
71      private ServletContext servletContext;
72  
73      private String defaultDesktopLayoutDecoration = null;
74      private String defaultDesktopPortletDecoration = null;
75      
76      private Set layoutDecorationsDir = Collections.EMPTY_SET;
77      private Set portletDecorationsDir = Collections.EMPTY_SET;
78      private Set desktopLayoutDecorationsDir = Collections.EMPTY_SET;
79      private Set desktopPortletDecorationsDir = Collections.EMPTY_SET;
80      
81      private Set layoutDecorationsList = Collections.EMPTY_SET;
82      private Set portletDecorationsList = Collections.EMPTY_SET;
83      private Set desktopLayoutDecorationsList = Collections.EMPTY_SET;
84      private Set desktopPortletDecorationsList = Collections.EMPTY_SET;
85      
86      private Map portletDecoratorProperties = new HashMap();
87      private Map layoutDecoratorProperties = new HashMap();
88  
89      public DecorationFactoryImpl( String decorationsPath, 
90                                    ResourceValidator validator )
91      {
92          this( null, decorationsPath, validator, null );
93      }
94      
95      public DecorationFactoryImpl( String decorationsPath, 
96                                    ResourceValidator validator,
97                                    JetspeedCache decorationConfigurationCache )
98      {
99          this( null, decorationsPath, validator, decorationConfigurationCache );
100     }
101 
102     public DecorationFactoryImpl( PortletRegistry registry,
103                                   String decorationsPath, 
104                                   ResourceValidator validator,
105                                   JetspeedCache decorationConfigurationCache )
106     {
107         this.registry =  registry;
108         this.decorationsPath = new Path( decorationsPath );
109         this.layoutDecorationsPath = getBasePath( Fragment.LAYOUT );
110         this.layoutDecorationsPathStr = this.layoutDecorationsPath.toString();
111         this.portletDecorationsPath = getBasePath( Fragment.PORTLET );
112         this.portletDecorationsPathStr = this.portletDecorationsPath.toString();
113         this.validator = validator;
114         this.decorationConfigurationCache = decorationConfigurationCache;
115     }
116         
117     public ResourceValidator getResourceValidator()
118     {
119         return validator;
120     }
121     
122     protected JetspeedCache getDecorationConfigurationCache()
123     {
124     	return decorationConfigurationCache;
125     }
126 
127     public Theme getTheme( Page page, RequestContext requestContext )
128     {
129         return new PageTheme(page, this, requestContext);
130     }
131     
132     public Decoration getDecoration( Page page, Fragment fragment, RequestContext requestContext )
133     {
134         String decorationName = getDefaultDecorationName( fragment, page );
135         Decoration decoration;
136 
137         // use layout decoration for top level layout root fragments
138         //    and use portlet decoration for all other fragments
139         boolean isLayout = fragment.getType().equals( Fragment.LAYOUT );
140         if ( isLayout )
141         {
142             decoration = getLayoutDecoration( decorationName, requestContext );
143         }
144         else
145         {
146             decoration = getPortletDecoration( decorationName, requestContext );
147         }
148 
149         if ( isDesktopEnabled( requestContext ) )
150         {   // assure that selected decoration supports /desktop
151             //    otherwise get default desktop decoration for fragment type
152             if ( decoration == null || ! decoration.supportsDesktop() )
153             {
154                 String defaultDecoration = null;
155                 if ( isLayout )
156                 {
157                 	if ( decoration == null || fragment.equals( page.getRootFragment() ) )
158                 	{
159                 		defaultDecoration = getDefaultDesktopLayoutDecoration();
160                 		decoration = getLayoutDecoration( defaultDecoration, requestContext );
161                 	}
162                 }
163                 else
164                 {
165                     defaultDecoration = getDefaultDesktopPortletDecoration();
166                     decoration = getPortletDecoration( defaultDecoration, requestContext );
167                 }
168                 if ( decoration == null )
169                 {
170                     String errMsg = "Cannot locate default desktop " + fragment.getType() + " decoration " + ( defaultDecoration == null ? "null" : ("\"" + defaultDecoration + "\"") ) + " (decoration " + ( defaultDecoration == null ? "null" : ("\"" + decorationName + "\"") ) + " specified for page could either not be located or does not support desktop). No desktop compatible " + fragment.getType() + " decoration is available.";
171                     log.error( errMsg );
172                 }
173             }
174         }
175         return decoration;
176     }
177 
178     public PortletDecoration getPortletDecoration( String name, RequestContext requestContext )
179     {
180         Path basePath = getPortletDecorationBasePath( name );
181         Path baseClientPath = createClientPath( name, basePath, requestContext, Fragment.PORTLET );
182         Properties configuration = getConfiguration( name, Fragment.PORTLET );
183         SessionPathResolverCache sessionPathResolver = new SessionPathResolverCache( requestContext.getRequest().getSession() );
184         return new PortletDecorationImpl( configuration, validator, basePath, baseClientPath, sessionPathResolver );
185     }
186 
187     public LayoutDecoration getLayoutDecoration( String name, RequestContext requestContext )
188     {
189         Path basePath = getLayoutDecorationBasePath( name );
190         Path baseClientPath = createClientPath( name, basePath, requestContext, Fragment.LAYOUT );
191         Properties configuration = getConfiguration( name, Fragment.LAYOUT );
192         SessionPathResolverCache sessionPathResolver = new SessionPathResolverCache( requestContext.getRequest().getSession() );
193         return new LayoutDecorationImpl( configuration, validator, basePath, baseClientPath, sessionPathResolver );
194     }    
195     
196     public boolean isDesktopEnabled( RequestContext requestContext )
197     {
198         Boolean desktopEnabled = (Boolean)requestContext.getAttribute( JetspeedDesktop.DESKTOP_ENABLED_REQUEST_ATTRIBUTE );
199         return ( desktopEnabled != null && desktopEnabled.booleanValue() ? true : false );
200     }
201 
202     public void setServletContext(ServletContext servletContext)
203     {
204         this.servletContext = servletContext;
205 
206     }
207 
208     protected Properties getCachedConfiguration( String name, String type )
209     {
210     	if ( decorationConfigurationCache == null )
211     	{
212     		if ( type.equals( Fragment.PORTLET ) )
213     		{
214     			return (Properties)this.portletDecoratorProperties.get( name );
215     		}
216     		else
217     		{
218     			return (Properties)this.layoutDecoratorProperties.get( name );
219     		}
220     	}
221     	CacheElement cachedElement = decorationConfigurationCache.get( getCachedConfigurationKey( type, name ) );
222         if (cachedElement != null)
223         	return (Properties)cachedElement.getContent();  
224         return null;
225     }
226     protected void setCachedConfiguration( String name, String type, Properties props )
227     {
228     	if ( decorationConfigurationCache == null )
229     	{
230     		if ( type.equals( Fragment.PORTLET ) )
231     		{
232     			this.portletDecoratorProperties.put( name, props );
233     		}
234     		else
235     		{
236     			this.layoutDecoratorProperties.put( name, props );
237     		}
238     	}
239     	else
240     	{
241     		CacheElement cachedElement = decorationConfigurationCache.createElement( getCachedConfigurationKey( type, name ), props );
242     		cachedElement.setTimeToIdleSeconds(decorationConfigurationCache.getTimeToIdleSeconds());
243     		cachedElement.setTimeToLiveSeconds(decorationConfigurationCache.getTimeToLiveSeconds());
244     		decorationConfigurationCache.put( cachedElement );
245     	}
246     }
247     protected String getCachedConfigurationKey( String type, String name )
248     {
249     	return type + "."  + name;
250     }
251     
252     /***
253      * Gets the configuration (decorator.properties) object for the decoration.
254      * @param name Name of the Decoration.
255      * @return <code>java.util.Properties</code> representing the configuration
256      * object.
257      */
258     public Properties getConfiguration( String name, String type )
259     {
260         Properties props = getCachedConfiguration( name, type );
261         if ( props != null )
262         {
263             return props;
264         }
265         
266         props = new Properties();
267         InputStream is = null;
268         
269         // load Decoration.CONFIG_FILE_NAME (decorator.properties)
270         try
271         {
272             is = servletContext.getResourceAsStream( decorationsPath + "/" + type + "/" + name + "/" + Decoration.CONFIG_FILE_NAME );
273             if (is != null)
274             {                
275                 props.load( is );
276             }
277             else
278             {
279                 log.warn( "Could not locate the " + Decoration.CONFIG_FILE_NAME + " configuration file for decoration \"" + name + "\".  This decoration may not exist." );
280                 props.setProperty( "id", name );
281                 props.setProperty( "name", name );
282             }
283         }
284         catch ( Exception e )
285         {
286             log.warn( "Failed to load the " + Decoration.CONFIG_FILE_NAME + " configuration file for decoration \"" + name + "\".", e );
287             props.setProperty( "id", name );
288             props.setProperty( "name", name );
289         }
290         finally
291         {
292             if ( is != null )
293             {
294                 try
295                 {
296                     is.close();
297                 }
298                 catch (IOException e)
299                 {
300                     log.warn("Failed to close decoration configuration.", e);
301                 }
302             }
303             String decorationIdPropVal = props.getProperty( "id" );
304             String decorationNamePropVal = props.getProperty( "name" );
305             if ( decorationIdPropVal == null )
306             {
307                 if ( decorationNamePropVal != null )
308                 {
309                     decorationIdPropVal = decorationNamePropVal;
310                 }
311                 else
312                 {
313                     decorationIdPropVal = name;
314                 }
315                 props.setProperty( "id", decorationIdPropVal );
316             }
317             
318             if ( decorationNamePropVal == null )
319             {
320                 props.setProperty( "name", decorationIdPropVal );
321             }
322         }
323         
324         // load Decoration.CONFIG_DESKTOP_FILE_NAME (decoratordesktop.properties)
325         try
326         {
327             is = servletContext.getResourceAsStream( decorationsPath + "/" + type + "/" + name + "/" + Decoration.CONFIG_DESKTOP_FILE_NAME );
328             if ( is != null )
329             {                
330                 props.load( is );
331                 if ( props.getProperty( Decoration.DESKTOP_SUPPORTED_PROPERTY ) == null )
332                 {
333                     props.setProperty( Decoration.DESKTOP_SUPPORTED_PROPERTY, "true" );
334                 }
335             }
336             else
337             {
338                 log.debug( "Could not locate the " + Decoration.CONFIG_DESKTOP_FILE_NAME + " configuration file for decoration \"" + name + "\".  This decoration may not exist." );
339             }
340         }
341         catch ( Exception e )
342         {
343             log.warn( "Failed to load the " + Decoration.CONFIG_DESKTOP_FILE_NAME + " configuration file for decoration \"" + name + "\".", e );
344         }
345         finally
346         {
347             if ( is != null )
348             {
349                 try
350                 {
351                     is.close();
352                 }
353                 catch ( IOException e )
354                 {
355                     log.warn( "Failed to close decoration desktop configuration.", e );
356                 }
357             }
358             if ( props.getProperty( Decoration.DESKTOP_SUPPORTED_PROPERTY ) == null )
359             {
360                 props.setProperty( Decoration.DESKTOP_SUPPORTED_PROPERTY, "false" );
361             }
362         }
363         
364         setCachedConfiguration( name, type, props );
365         
366         return props;
367     }
368     
369     /***
370      * Creates a <code>org.apache.jetspeed.util.Path</code> object based
371      * off of the user's client browser and locale.
372      *
373      * @param name Decroator's name
374      * @param requestContext  Current portal request.
375      * @param decorationType Type of decoration, either <code>layout</code>
376      * or <code>portlet</code>
377      * @return
378      *
379      * @see Path
380      * @see RequestContext
381      */
382     protected Path createClientPath( String name, RequestContext requestContext, String decorationType )
383     {
384         return createClientPath( name, null, requestContext, decorationType );
385     }   
386     
387     private Path createClientPath( String name, Path basePath, RequestContext requestContext, String decorationType )
388     {
389         if ( basePath == null )
390             basePath = getBasePath( name, decorationType );
391         String mediaType = requestContext.getMediaType();
392         Locale locale = requestContext.getLocale();
393         String language = locale.getLanguage();
394         String country = locale.getCountry();
395         String variant = locale.getVariant();
396 
397         basePath = basePath.addSegment( mediaType ).addSegment( language );
398 
399         if ( country != null )
400         {
401             basePath = basePath.addSegment( country );
402         }
403 
404         if (variant != null)
405         {
406             basePath = basePath.addSegment( variant );
407         }
408         return basePath;
409     }
410 
411     /***
412      * Returns a the default decoration name for the specific Fragment type.
413      *
414      * @param fragment Fragment whose default decroation has been requested
415      * @param page Page this fragment belongs to.
416      * @return Default decorator name.
417      *
418      * @see Page
419      * @see Fragment
420      */
421     protected String getDefaultDecorationName(Fragment fragment, Page page)
422     {
423         // get specified decorator
424         String decoration = fragment.getDecorator();
425         if (decoration == null)
426         {
427             if (fragment.getType().equals(Fragment.LAYOUT))
428             {
429                 if (fragment.equals(page.getRootFragment()))
430                 {
431                     // use page specified layout decorator name
432                     decoration = page.getEffectiveDefaultDecorator(Fragment.LAYOUT);
433                 }
434                 else
435                 {
436                     // use default nested layout portlet decorator name
437                     decoration = DEFAULT_NESTED_LAYOUT_PORTLET_DECORATOR;
438                 }
439             }
440             else
441             {
442                 // use page specified default portlet decorator name
443                 decoration = page.getEffectiveDefaultDecorator(Fragment.PORTLET);
444             }
445         }
446 
447         return decoration;
448     }
449 
450     public void clearCache(RequestContext requestContext)
451     {
452         new SessionPathResolverCache(requestContext.getRequest().getSession()).clear();
453     }
454 
455     protected Path getBasePath( String decorationType )
456     {
457         return decorationsPath.addSegment(decorationType);
458     }
459     
460     protected Path getBasePath( String name, String decorationType )
461     {
462         return decorationsPath.addSegment(decorationType).addSegment(name);
463     }
464     
465     protected Path getLayoutDecorationBasePath( String name )
466     {
467         return layoutDecorationsPath.addSegment(name);
468     }
469     protected Path getPortletDecorationBasePath( String name )
470     {
471         return portletDecorationsPath.addSegment(name);
472     }
473     
474     public String getLayoutDecorationsBasePath()
475     {
476         return this.layoutDecorationsPathStr;
477     }
478     
479     public String getPortletDecorationsBasePath()
480     {
481         return this.portletDecorationsPathStr;
482     }
483 
484     /***
485      * Get the portal-wide list of page decorations.
486      *
487      * @return A list of page decorations of type <code>Decoration</code>
488      */
489     public Set getPageDecorations( RequestContext request )
490     {
491         Set decorations = servletContext.getResourcePaths( decorationsPath.toString() + "/layout" );
492         if( ! layoutDecorationsDir.equals( decorations ) )
493         {
494             layoutDecorationsList = getListing( decorations, Decoration.CONFIG_FILE_NAME );
495             layoutDecorationsDir = decorations;
496             
497         }
498         return layoutDecorationsList;
499     }
500     
501     /***
502      * Get the portal-wide list of available desktop page decorations.
503      * 
504      * @return A list of desktop skins of type <code>String</code>
505      */        
506     public Set getDesktopPageDecorations( RequestContext request )
507     {
508         Set decorations = servletContext.getResourcePaths( decorationsPath.toString() + "/layout" );
509         if( ! desktopLayoutDecorationsDir.equals( decorations ) )
510         {
511             desktopLayoutDecorationsList = getListing( decorations, Decoration.CONFIG_DESKTOP_FILE_NAME );
512             desktopLayoutDecorationsDir = decorations;
513             
514         }
515         return desktopLayoutDecorationsList;
516     }
517 
518     /***
519      * Get the portal-wide list of portlet decorations.
520      *
521      * @return A list of portlet decorations of type <code>String</code>
522      */
523     public Set getPortletDecorations( RequestContext request )
524     {
525         Set decorations = servletContext.getResourcePaths( decorationsPath.toString() + "/portlet" );
526         if( ! portletDecorationsDir.equals( decorations ) )
527         {
528             portletDecorationsList = getListing( decorations, Decoration.CONFIG_FILE_NAME );
529             portletDecorationsDir = decorations;
530             
531         }
532         return portletDecorationsList;
533     }
534     
535     /***
536      * Get the portal-wide list of desktop portlet decorations.
537      *
538      * @return A list of portlet decorations of type <code>String</code>
539      */
540     public Set getDesktopPortletDecorations( RequestContext request )
541     {
542         Set decorations = servletContext.getResourcePaths( decorationsPath.toString() + "/portlet" );
543         if( ! desktopPortletDecorationsDir.equals( decorations ) )
544         {
545             desktopPortletDecorationsList = getListing( decorations, Decoration.CONFIG_DESKTOP_FILE_NAME );
546             desktopPortletDecorationsDir = decorations;
547             
548         }
549         return desktopPortletDecorationsList;
550     }
551     
552     /***
553      * Get the portal-wide list of available layouts.
554      *
555      * @return A list of layout portlets of type <code>PortletDefinitionComposite</code>
556      */
557     public List getLayouts( RequestContext request )
558     {
559         List list = new LinkedList();
560         Iterator portlets = registry.getAllPortletDefinitions().iterator();
561         while ( portlets.hasNext() )
562         {
563             PortletDefinitionComposite portlet = (PortletDefinitionComposite)portlets.next();
564             MutablePortletApplication muta = (MutablePortletApplication)portlet.getPortletApplicationDefinition();
565             String appName = muta.getName();
566             if ( appName == null )
567                 continue;
568             if ( ! appName.equals( "jetspeed-layouts" ) )
569                 continue;
570 
571             String uniqueName = appName + "::" + portlet.getName();
572             list.add( new LayoutInfoImpl( uniqueName,
573                       portlet.getDisplayNameText( request.getLocale() ),
574                       portlet.getDescriptionText( request.getLocale() ) ) );
575 
576         }
577         return list;
578     }
579     
580     protected Set getListing(Set rawList, String propsFile)
581     {
582         Iterator itr = rawList.iterator();
583         Set filteredList = new HashSet();
584         while(itr.hasNext())
585         {
586             Path path = new Path((String) itr.next());
587             if(path.getFileName() == null && validator.resourceExists(path.toString() + propsFile))
588             {
589                 int offset = path.length() - 1;
590                 filteredList.add(path.getSegment(offset));
591             }
592         }
593         return filteredList;
594     }
595 
596     public String getDefaultDesktopLayoutDecoration()
597     {
598         synchronized ( this )
599         {
600             return this.defaultDesktopLayoutDecoration;
601         }
602     }
603     public void setDefaultDesktopLayoutDecoration( String newOne )
604     {
605         synchronized ( this )
606         {
607             this.defaultDesktopLayoutDecoration = newOne;
608         }
609     }
610     public String getDefaultDesktopPortletDecoration()
611     {
612         synchronized ( this )
613         {
614             return this.defaultDesktopPortletDecoration;
615         }
616     }
617     public void setDefaultDesktopPortletDecoration( String newOne )
618     {
619         synchronized ( this )
620         {
621             this.defaultDesktopPortletDecoration = newOne;
622         }
623     }
624 }