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