View Javadoc

1   /*
2    * Copyright 2007 Hippo
3    *
4    * Licensed under the Apache License, Version 2.0 (the  "License"); 
5    * you may not use this file except in compliance with the License. 
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" 
12   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  package nl.hippo.portal.cms;
17  
18  import java.io.InputStream;
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Properties;
25  
26  import javax.xml.parsers.ParserConfigurationException;
27  import javax.xml.transform.TransformerException;
28  
29  import nl.hippo.client.api.content.Document;
30  import nl.hippo.client.api.content.DocumentPath;
31  import nl.hippo.client.api.event.EventValidity;
32  import nl.hippo.portal.binding.MarshalledObjectManager;
33  import nl.hippo.portal.cms.cache.EventCacheable;
34  import nl.hippo.portal.cms.cache.ObjectCache;
35  import nl.hippo.portal.cms.cache.PortalCacheKey;
36  import nl.hippo.portal.cms.repository.DocumentTreeBuilder;
37  import nl.hippo.portal.cms.scripting.xslt.XSLTTransformerService;
38  import nl.hippo.portal.cms.site.DocumentTransformer;
39  import nl.hippo.portal.cms.site.MetaDataLoader;
40  import nl.hippo.portal.cms.site.SiteMap;
41  import nl.hippo.portal.cms.site.SiteMapLoader;
42  import nl.hippo.portal.cms.source.Source;
43  import nl.hippo.portal.cms.source.SourceResolver;
44  import nl.hippo.portal.domain.service.UserService;
45  
46  import org.apache.commons.io.IOUtils;
47  import org.apache.commons.lang.ArrayUtils;
48  import org.apache.commons.lang.math.NumberUtils;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  import org.xml.sax.SAXException;
52  
53  /***
54   * @version $Id: CMSSiteImpl.java 13521 2008-09-17 14:50:38Z jhoffman $
55   *
56   */
57  public class CMSSiteImpl implements CMSSite
58  {
59      
60      private static final Log log = LogFactory.getLog(CMSSiteImpl.class);
61      private Object mutex = new Object();
62      
63      private boolean available;
64      private String name;
65      private String fullName;
66      private String pageContext;
67      private CMSApplicationImpl application;
68      private Map metaDataPaths = new HashMap();
69      private Map siteMapPaths = new HashMap();
70      private Map cmsServices = new HashMap();
71      private SiteMapLoader siteMapLoader;
72      private Properties siteProperties;
73      private String portalCMSResourcesPrefix;
74      private String requiredLoginPath;    
75      private String pageNotFoundPath;
76      private String pageAcceptTermsOfUse;
77      private String defaultPath;
78      private String[] publicPaths;   
79      private ObjectCache objectCache;
80      private SourceResolver sourceResolver;
81      private MarshalledObjectManager marshalledObjectManager;
82      private String sitemapPreprocessingXSLTSourceUrl;
83      private XSLTTransformerService xsltTransformerService;
84      
85      public static final int REPOSITORY_NAVIGATION_DEFAULT_MAX_DEPTH = 1;
86      
87      public static final String PROPERTY_REPOSITORY_NAVIGATION_MAX_DEPTH = "hippo.portal.repository.navigation.max-depth";
88      public static final String DATA_TYPE_METADATA = "MetaData";
89      public static final String DATA_TYPE_SITEMAP = "SiteMap";
90      
91      public void initialized()
92      {
93      }
94      
95      public CMSSiteImpl(String name)
96      {
97          this.name = name;
98          if ( name == null || name.length() == 0 || name.indexOf("/") != -1)
99          {
100             throw new IllegalArgumentException("Site name is empty or contains illegal '/' character(s): "+name);
101         }
102     }
103     
104     public synchronized void setAvailable(boolean available)
105     {
106         if ( this.available != available)
107         {
108             this.available = available;
109             CMSService cmsService = getCMSService(CMSService.DEFAULT);
110             int maxDepth = getRepositoryNavigationMaxDepth();
111             siteMapLoader = new SiteMapLoader(new DocumentTreeBuilder(cmsService), maxDepth);
112             checkRefreshData();
113         }
114     }
115     
116     public synchronized void checkRefreshData()
117     {
118         if ( available )
119         {
120             loadData(DATA_TYPE_SITEMAP, siteMapPaths, siteMapLoader,sitemapPreprocessingXSLTSourceUrl);
121             loadData(DATA_TYPE_METADATA, metaDataPaths, MetaDataLoader.getInstance(),null);
122         }
123     }
124     
125     public boolean isAvailable()
126     {
127         return available;
128     }
129     
130     /***
131      * @param application the application to set
132      */
133     public void setApplication(CMSApplicationImpl application)
134     {
135         this.application = application;
136         fullName = getApplication().getName() + "::" + getName();
137     }
138 
139     /***
140      * @param pageContext the pageContext to set
141      */
142     public void setPageContext(Object pageContext)
143     {
144         this.pageContext = (String)pageContext;
145     }
146 
147     /* (non-Javadoc)
148      * @see nl.hippo.portal.cms.CMSSite#getApplication()
149      */
150     public CMSApplication getApplication()
151     {
152         return application;
153     }
154 
155     /* (non-Javadoc)
156      * @see nl.hippo.portal.cms.CMSSite#getCache(java.lang.String)
157      */
158     public Object getCache(String name)
159     {
160         return null;
161     }
162 
163     /* (non-Javadoc)
164      * @see nl.hippo.portal.cms.CMSSite#getChangeNotifier()
165      */
166     public Object getChangeNotifier()
167     {
168         return null;
169     }
170 
171     private Object getDataObjectFromCache(String type, String name, String path){        
172         PortalCacheKey key = objectCache.getCacheKeyFactory().createKey(new Object[]{"type="+type,"name="+name,getCMSService(CMSService.DEFAULT).createRelativePath(path)});
173         synchronized (mutex)
174         {            
175             return available ? objectCache.get(key) : null;
176         }
177     }
178     
179     public XSLTTransformerService getXsltTransformerService() {
180 		return xsltTransformerService;
181 	}
182 
183 	public void setXsltTransformerService(
184 			XSLTTransformerService xsltTransformerService) {
185 		this.xsltTransformerService = xsltTransformerService;
186 	}
187 
188 	/* (non-Javadoc)
189      * @see nl.hippo.portal.cms.CMSSite#getMetaData(java.lang.String)
190      */
191     public CMSMetaData getMetaData(String name)
192     {
193         Object obj = getDataObjectFromCache(DATA_TYPE_METADATA,name,(String) metaDataPaths.get(name));
194         return (obj != null) ? (CMSMetaData)obj : null; 
195     }
196     
197     public SiteMap getSiteMap(String name)
198     {
199         Object obj = getDataObjectFromCache(DATA_TYPE_SITEMAP,name,(String) siteMapPaths.get(name));
200         return (obj != null) ? (SiteMap)obj : null; 
201     }
202 
203     public List getSiteMaps()
204     {
205         List maps = new ArrayList();
206         for (Iterator iter = siteMapPaths.keySet().iterator(); iter.hasNext();) {
207             String name = (String) iter.next();
208             String path = (String) siteMapPaths.get(name);
209             Object siteMapObj = getDataObjectFromCache(DATA_TYPE_SITEMAP,name,path);
210             if (siteMapObj != null){
211                 maps.add((SiteMap) siteMapObj);
212             }
213         }
214         return maps;
215     }
216 
217     /* (non-Javadoc)
218      * @see nl.hippo.portal.cms.CMSSite#getName()
219      */
220     public String getName()
221     {
222         return name;
223     }
224     
225     /* (non-Javadoc)
226      * @see nl.hippo.portal.cms.CMSSite#getFullName()
227      */
228     public String getFullName()
229     {
230         return fullName;
231     }
232 
233     /* (non-Javadoc)
234      * @see nl.hippo.portal.cms.CMSSite#getPageContext()
235      */
236     public Object getPageContext()
237     {
238         return pageContext;
239     }
240     
241     public String getPortalCMSResourcesPrefix()
242     {
243         return this.portalCMSResourcesPrefix;
244     }
245     
246     public void setPortalCMSResourcesPrefix(String portalCMSResourcesPrefix)
247     {
248         if ( portalCMSResourcesPrefix == null || !portalCMSResourcesPrefix.startsWith("/") )
249         {
250             portalCMSResourcesPrefix = "/" + (portalCMSResourcesPrefix==null?"":portalCMSResourcesPrefix);
251         }
252         if ( !portalCMSResourcesPrefix.endsWith("/") )
253         {
254             portalCMSResourcesPrefix += "/";
255         }
256         this.portalCMSResourcesPrefix = portalCMSResourcesPrefix + getName() + "/";
257     }
258     
259     public String getRequiredLoginPath()
260     {
261         return requiredLoginPath;
262     }
263     
264     public void setRequiredLoginPath(String requiredLoginPath)
265     {
266         this.requiredLoginPath = requiredLoginPath;
267     }
268     
269     /***
270      * @return the pageNotFoundPath
271      */
272     public String getPageNotFoundPath()
273     {
274         return pageNotFoundPath;
275     }
276 
277     /***
278      * @param pageNotFoundPath the pageNotFoundPath to set
279      */
280     public void setPageNotFoundPath(String pageNotFoundPath)
281     {
282         this.pageNotFoundPath = pageNotFoundPath;
283     }    
284 
285     /***
286      * @return the pageAcceptTermsOfUse
287      */
288     public String getPageAcceptTermsOfUse()
289     {
290         return pageAcceptTermsOfUse;
291     }
292 
293     /***
294      * @param pageAcceptTermsOfUse the pageAcceptTermsOfUse to set
295      */
296     public void setPageAcceptTermsOfUse(String pageAcceptTermsOfUse)
297     {
298         this.pageAcceptTermsOfUse = pageAcceptTermsOfUse;
299     }
300 
301     /***
302      * @return the defaultPath
303      */
304     public String getDefaultPath() {
305         return defaultPath;
306     }
307 
308     /***
309      * @return the publicPaths
310      */
311     public String[] getPublicPaths() {
312         return publicPaths;
313     }
314 
315     /***
316      * @param publicPaths the publicPaths to set
317      */
318     public void setPublicPaths(String[] publicPaths) {
319         this.publicPaths = publicPaths;
320     }
321 
322     /***
323      * @param defaultPath the defaultPath to set
324      */
325     public void setDefaultPath(String defaultPath) {
326         this.defaultPath = defaultPath;
327     }
328 
329     /* (non-Javadoc)
330      * @see nl.hippo.portal.cms.CMSSite#getService(java.lang.String)
331      */
332     public CMSService getCMSService(String name)
333     {
334         return (CMSService)cmsServices.get(name);
335     }
336     
337     public void setCMSServices(List services)
338     {
339         if ( services != null )
340         {
341             Iterator iter = services.iterator();
342             while (iter.hasNext())
343             {
344                 CMSService service = (CMSService)iter.next();
345                 cmsServices.put(service.getName(),service);
346             }
347         }
348         if (getCMSService(CMSService.DEFAULT) == null || getCMSService(CMSService.NOCACHE) == null || getCMSService(CMSService.RESOURCE) == null )
349         {
350             throw new IllegalArgumentException("Required CMSServices \""+CMSService.DEFAULT+"\", \""+CMSService.NOCACHE+"\" or \""+CMSService.RESOURCE+"\" undefined");
351         }
352     }
353     
354     /* (non-Javadoc)
355      * @see nl.hippo.portal.cms.CMSSite#getUserService()
356      */
357     public UserService getUserService()
358     {
359         return available ? application.getRegistration().getUserService() : null;
360     }
361     
362     public void setMetaDataPaths(Map metaDataPaths)
363     {
364         this.metaDataPaths=metaDataPaths;
365     }
366     
367     public void setSiteMapPaths(Map siteMapPaths)
368     {
369         this.siteMapPaths=siteMapPaths;
370     }
371     
372     private PortalCacheKey createMetadataCacheKey(CMSService service, String type, String name, String path){
373         DocumentPath cacheKeyPath = service.createRelativePath(path);
374         return objectCache.getCacheKeyFactory().createKey(new Object[]{"type="+type,"name="+name,cacheKeyPath});        
375     }
376     
377     private boolean loadData(String type, Map pathKeys, DocumentTransformer transformer, String preprocessingXsltURI)
378     {
379         boolean modified = false;
380         Iterator iter = pathKeys.keySet().iterator(); 
381         CMSService defaultService = getCMSService(CMSService.DEFAULT);
382         while (iter.hasNext())
383         {
384             String name = (String)iter.next();
385             String path = (String)pathKeys.get(name);
386             
387             DocumentPath cacheKeyPath = defaultService.createRelativePath(path);
388             PortalCacheKey key = createMetadataCacheKey(defaultService,type,name,path);
389             Object dataObject = objectCache.get(key);
390             if (dataObject == null){
391                 Document newDocument=null;
392                 try
393                 {
394                     newDocument = getCMSService(CMSService.DEFAULT).get(path);
395                     if ( newDocument == null)
396                     {
397                         log.error("Failed to retrieve "+type+" document: "+path+" with name: "+name+" for site: "+getName());
398                     }
399                     else 
400                     {
401                         modified = true;
402                         if (newDocument != null)
403                         {
404                             if (log.isDebugEnabled())
405                             {
406                                 log.debug("(Re)loading "+type+" document: "+path+" with name: "+name+" for site: "+getName());
407                             }
408                             try
409                             {
410                                 List dependentDatasources = new ArrayList();
411                                 InputStream newDocumentStream = newDocument.getContent().getResponseAsStream();
412                                 EventValidity[] xsltEventValidities = null;
413                                 
414                                 // transform the document's content with an XSLT, if an xslt source path has been set
415                                 if (preprocessingXsltURI != null && sourceResolver != null && xsltTransformerService != null) {
416                                     Source preprocessingXSLTSource = sourceResolver.resolve(preprocessingXsltURI);
417                                     if (preprocessingXSLTSource != null) {
418                                         String resultAsString = null;
419                                         try {
420                                             resultAsString = xsltTransformerService.transform(newDocumentStream, preprocessingXSLTSource
421                                                     .getInputStream());
422                                         } catch (TransformerException te) {
423 
424                                         }
425                                         newDocumentStream = IOUtils.toInputStream(resultAsString, "UTF-8");
426                                         if (preprocessingXSLTSource instanceof EventCacheable){
427                                             xsltEventValidities = ((EventCacheable)preprocessingXSLTSource).getEventValidities();
428                                         }
429                                     }
430                                 }
431                                 dataObject = transformer.transform(name, newDocumentStream, dependentDatasources);
432                                 int nrOfEventDatasources = dependentDatasources.size()+1; // the metadata file itself, plus related datasources
433                                 DocumentPath[] cacheEventPaths = new DocumentPath[nrOfEventDatasources];
434                                 for (int i=0; i<dependentDatasources.size(); i++) {
435                                     String dependentPath = (String) dependentDatasources.get(i);
436                                     cacheEventPaths[i]=defaultService.createRelativePath(dependentPath);                                    
437                                 }
438                                 cacheEventPaths[nrOfEventDatasources-1]=cacheKeyPath;
439                                 EventValidity[] sitemapValidities = objectCache.getEventValidityFactory().createValidities(cacheEventPaths);
440                                 
441                                 EventValidity[] validities;
442                                 if (xsltEventValidities != null){
443                                     // merge validities from XSLT transformer source with sitemap validities 
444                                 	validities = (EventValidity[]) ArrayUtils.addAll(sitemapValidities, xsltEventValidities);
445                                 } else {
446                                 	// no XSLT transform, or XSLT source is not event cacheable
447                                 	validities = sitemapValidities;
448                                 }
449                                 
450                                 objectCache.store(key, dataObject, validities );
451                             }
452                             catch (SAXException e)
453                             {
454                                 log.error("Failed to transform "+type+" document: "+path+" with name: "+name+" for site: "+getName(), e);
455                                 
456                             }
457                             catch (ParserConfigurationException e)
458                             {
459                                 log.error("Failed to transform "+type+" document: "+path+" with name: "+name+" for site: "+getName(), e);
460                                 
461                             }
462 
463                         }
464                     }
465                 }
466                 catch (Exception e)
467                 {
468                     log.error("Failed to retrieve "+type+" document: "+path+" with name: "+name+" for site: "+getName(), e);
469                 }
470             }
471         }
472         return modified;
473     }
474     
475     public void setProperties(Properties properties){
476         this.siteProperties=properties;
477     }
478     
479     public String getProperty(String name) {
480         String result = null;
481         if (this.siteProperties!=null){
482             result = this.siteProperties.getProperty(name);
483         }
484         return result != null ? result : (application != null ? application.getProperty(name) : null);
485     }
486     
487     public int getRepositoryNavigationMaxDepth() {       
488         return NumberUtils.toInt(getProperty(PROPERTY_REPOSITORY_NAVIGATION_MAX_DEPTH), REPOSITORY_NAVIGATION_DEFAULT_MAX_DEPTH);
489     }
490 
491     public ObjectCache getObjectCache() {
492         return objectCache;
493     }
494 
495     public void setObjectCache(ObjectCache objectCache) {
496         this.objectCache = objectCache;
497     }
498 
499     public SourceResolver getSourceResolver() {
500         return sourceResolver;
501     }
502 
503     public void setSourceResolver(SourceResolver sourceResolver) {
504         this.sourceResolver = sourceResolver;
505     }
506 
507     public MarshalledObjectManager getMarshalledObjectManager() {
508         return marshalledObjectManager;
509     }
510 
511     public void setMarshalledObjectManager(MarshalledObjectManager marshalledObjectManager) {
512         this.marshalledObjectManager = marshalledObjectManager;
513     }
514 
515 	public String getSitemapPreprocessingXSLTSourceUrl() {
516 		return sitemapPreprocessingXSLTSourceUrl;
517 	}
518 
519 	public void setSitemapPreprocessingXSLTSourceUrl(
520 			String sitemapPreprocessingXSLTSourceUrl) {
521 		this.sitemapPreprocessingXSLTSourceUrl = sitemapPreprocessingXSLTSourceUrl;
522 	}
523     
524 }