1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
138
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 {
151
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
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
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
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
432 decoration = page.getEffectiveDefaultDecorator(Fragment.LAYOUT);
433 }
434 else
435 {
436
437 decoration = DEFAULT_NESTED_LAYOUT_PORTLET_DECORATOR;
438 }
439 }
440 else
441 {
442
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 }