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