1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
148
149
150 public CMSApplication getApplication()
151 {
152 return application;
153 }
154
155
156
157
158 public Object getCache(String name)
159 {
160 return null;
161 }
162
163
164
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
189
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
218
219
220 public String getName()
221 {
222 return name;
223 }
224
225
226
227
228 public String getFullName()
229 {
230 return fullName;
231 }
232
233
234
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
330
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
355
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
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;
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
444 validities = (EventValidity[]) ArrayUtils.addAll(sitemapValidities, xsltEventValidities);
445 } else {
446
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 }