KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > lenya > cms > publication > AbstractPublication


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
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" BASIS,
12  * 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  */

17
18 /* $Id: AbstractPublication.java 180203 2005-06-06 03:10:44Z gregor $ */
19
20 package org.apache.lenya.cms.publication;
21
22 import java.io.File JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28
29 import org.apache.avalon.framework.configuration.Configuration;
30 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
31 import org.apache.lenya.cms.publishing.PublishingEnvironment;
32 import org.apache.log4j.Category;
33
34 /**
35  * A publication.
36  */

37 public abstract class AbstractPublication implements Publication {
38     private static Category log = Category.getInstance(AbstractPublication.class);
39
40     private static final String JavaDoc[] areas = { AUTHORING_AREA, STAGING_AREA, LIVE_AREA, ADMIN_AREA,
41             ARCHIVE_AREA, TRASH_AREA, INFO_AREA_PREFIX + AUTHORING_AREA,
42             INFO_AREA_PREFIX + STAGING_AREA, INFO_AREA_PREFIX + LIVE_AREA,
43             INFO_AREA_PREFIX + ARCHIVE_AREA, INFO_AREA_PREFIX + TRASH_AREA };
44
45     private String JavaDoc id;
46     private PublishingEnvironment environment;
47     private File JavaDoc servletContext;
48     private DocumentIdToPathMapper mapper = null;
49     private ArrayList JavaDoc languages = new ArrayList JavaDoc();
50     private String JavaDoc defaultLanguage = null;
51     private String JavaDoc breadcrumbprefix = null;
52     private String JavaDoc sslprefix = null;
53     private String JavaDoc livemountpoint = null;
54     private HashMap JavaDoc siteTrees = new HashMap JavaDoc();
55     private String JavaDoc[] rewriteAttributeXPaths = { };
56     private boolean hasSitetree = true;
57
58     private static final String JavaDoc ELEMENT_PROXY = "proxy";
59     private static final String JavaDoc ATTRIBUTE_AREA = "area";
60     private static final String JavaDoc ATTRIBUTE_URL = "url";
61     private static final String JavaDoc ATTRIBUTE_SSL = "ssl";
62     private static final String JavaDoc ELEMENT_REWRITE_ATTRIBUTE = "link-attribute";
63     private static final String JavaDoc ATTRIBUTE_XPATH = "xpath";
64
65     /**
66      * Creates a new instance of Publication
67      *
68      * @param id the publication id
69      * @param servletContextPath the servlet context of this publication
70      *
71      * @throws PublicationException if there was a problem reading the config file
72      */

73     protected AbstractPublication(String JavaDoc id, String JavaDoc servletContextPath) throws PublicationException {
74         assert id != null;
75         this.id = id;
76
77         assert servletContextPath != null;
78
79         File JavaDoc servletContext = new File JavaDoc(servletContextPath);
80         assert servletContext.exists();
81         this.servletContext = servletContext;
82
83         // FIXME: remove PublishingEnvironment from publication
84
environment = new PublishingEnvironment(servletContextPath, id);
85
86         File JavaDoc configFile = new File JavaDoc(getDirectory(), CONFIGURATION_FILE);
87         DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
88
89         Configuration config;
90
91         String JavaDoc pathMapperClassName = null;
92         String JavaDoc documentBuilderClassName = null;
93
94         try {
95             config = builder.buildFromFile(configFile);
96
97             try {
98                 pathMapperClassName = config.getChild(ELEMENT_PATH_MAPPER).getValue();
99                 Class JavaDoc pathMapperClass = Class.forName(pathMapperClassName);
100                 this.mapper = (DocumentIdToPathMapper) pathMapperClass.newInstance();
101             } catch (ClassNotFoundException JavaDoc e) {
102                 throw new PublicationException("Cannot instantiate documentToPathMapper: ["
103                         + pathMapperClassName + "]", e);
104             }
105
106             try {
107                 Configuration documentBuilderConfiguration = config.getChild(
108                         ELEMENT_DOCUMENT_BUILDER, false);
109                 if (documentBuilderConfiguration != null) {
110                     documentBuilderClassName = documentBuilderConfiguration.getValue();
111                     Class JavaDoc documentBuilderClass = Class.forName(documentBuilderClassName);
112                     this.documentBuilder = (DocumentBuilder) documentBuilderClass.newInstance();
113                 }
114             } catch (ClassNotFoundException JavaDoc e) {
115                 throw new PublicationException("Cannot instantiate document builder: ["
116                         + pathMapperClassName + "]", e);
117             }
118
119             Configuration[] languages = config.getChild(LANGUAGES).getChildren();
120             for (int i = 0; i < languages.length; i++) {
121                 Configuration languageConfig = languages[i];
122                 String JavaDoc language = languageConfig.getValue();
123                 this.languages.add(language);
124                 if (languageConfig.getAttribute(DEFAULT_LANGUAGE_ATTR, null) != null) {
125                     defaultLanguage = language;
126                 }
127             }
128
129             Configuration siteStructureConfiguration = config.getChild(ELEMENT_SITE_STRUCTURE,
130                     false);
131             if (siteStructureConfiguration != null) {
132                 String JavaDoc siteStructureType = siteStructureConfiguration.getAttribute(ATTRIBUTE_TYPE);
133                 if (!siteStructureType.equals("sitetree")) {
134                     hasSitetree = false;
135                 }
136             }
137
138             Configuration[] proxyConfigs = config.getChildren(ELEMENT_PROXY);
139             for (int i = 0; i < proxyConfigs.length; i++) {
140                 String JavaDoc url = proxyConfigs[i].getAttribute(ATTRIBUTE_URL);
141                 String JavaDoc ssl = proxyConfigs[i].getAttribute(ATTRIBUTE_SSL);
142                 String JavaDoc area = proxyConfigs[i].getAttribute(ATTRIBUTE_AREA);
143
144                 Proxy proxy = new Proxy();
145                 proxy.setUrl(url);
146
147                 Object JavaDoc key = getProxyKey(area, Boolean.valueOf(ssl).booleanValue());
148                 this.areaSsl2proxy.put(key, proxy);
149                 if (log.isDebugEnabled()) {
150                     log.debug("Adding proxy: [" + proxy + "] for area=[" + area + "] SSL=[" + ssl
151                             + "]");
152                 }
153             }
154             
155             Configuration[] rewriteAttributeConfigs = config.getChildren(ELEMENT_REWRITE_ATTRIBUTE);
156             List JavaDoc xPaths = new ArrayList JavaDoc();
157             for (int i = 0; i < rewriteAttributeConfigs.length; i++) {
158                 String JavaDoc xPath = rewriteAttributeConfigs[i].getAttribute(ATTRIBUTE_XPATH);
159                 xPaths.add(xPath);
160             }
161             this.rewriteAttributeXPaths = (String JavaDoc[]) xPaths.toArray(new String JavaDoc[xPaths.size()]);
162
163         } catch (PublicationException e) {
164             throw e;
165         } catch (Exception JavaDoc e) {
166             log.error(e);
167             throw new PublicationException("Problem with config file: "
168                     + configFile.getAbsolutePath(), e);
169         }
170
171         breadcrumbprefix = config.getChild(BREADCRUMB_PREFIX).getValue("");
172
173         sslprefix = config.getChild(SSL_PREFIX).getValue("");
174
175         livemountpoint = config.getChild(LIVE_MOUNT_POINT).getValue("");
176
177     }
178
179     /**
180      * Returns the publication ID.
181      * @return A string value.
182      */

183     public String JavaDoc getId() {
184         return id;
185     }
186
187     /**
188      * Returns the publishing environment of this publication.
189      * @return A {@link PublishingEnvironment}object.
190      * @deprecated It is planned to decouple the environments from the publication.
191      */

192     public PublishingEnvironment getEnvironment() {
193         return environment;
194     }
195
196     /**
197      * Returns the servlet context this publication belongs to (usually, the
198      * <code>webapps/lenya</code> directory).
199      * @return A <code>File</code> object.
200      */

201     public File JavaDoc getServletContext() {
202         return servletContext;
203     }
204
205     /**
206      * Returns the publication directory.
207      * @return A <code>File</code> object.
208      */

209     public File JavaDoc getDirectory() {
210         return new File JavaDoc(getServletContext(), PUBLICATION_PREFIX + File.separator + getId());
211     }
212
213     /**
214      * Return the directory of a specific area.
215      *
216      * @param area a <code>File</code> representing the root of the area content directory.
217      *
218      * @return the directory of the given content area.
219      */

220     public File JavaDoc getContentDirectory(String JavaDoc area) {
221         return new File JavaDoc(getDirectory(), CONTENT_PATH + File.separator + area);
222     }
223
224     /**
225      * DOCUMENT ME!
226      *
227      * @param mapper DOCUMENT ME!
228      */

229     public void setPathMapper(DefaultDocumentIdToPathMapper mapper) {
230         assert mapper != null;
231         this.mapper = mapper;
232     }
233
234     /**
235      * Returns the path mapper.
236      *
237      * @return a <code>DocumentIdToPathMapper</code>
238      */

239     public DocumentIdToPathMapper getPathMapper() {
240         return mapper;
241     }
242
243     /**
244      * Returns if a given string is a valid area name.
245      * @param area The area string to test.
246      * @return A boolean value.
247      */

248     public static boolean isValidArea(String JavaDoc area) {
249         return area != null && Arrays.asList(areas).contains(area);
250     }
251
252     /**
253      * Get the default language
254      *
255      * @return the default language
256      */

257     public String JavaDoc getDefaultLanguage() {
258         return defaultLanguage;
259     }
260
261     /**
262      * Set the default language
263      *
264      * @param language the default language
265      */

266     public void setDefaultLanguage(String JavaDoc language) {
267         defaultLanguage = language;
268     }
269
270     /**
271      * Get all available languages for this publication
272      *
273      * @return an <code>Array</code> of languages
274      */

275     public String JavaDoc[] getLanguages() {
276         return (String JavaDoc[]) languages.toArray(new String JavaDoc[languages.size()]);
277     }
278
279     /**
280      * Get the breadcrumb prefix. It can be used as a prefix if a publication is part of a larger
281      * site
282      *
283      * @return the breadcrumb prefix
284      */

285     public String JavaDoc getBreadcrumbPrefix() {
286         return breadcrumbprefix;
287     }
288
289     /**
290      * Get the SSL prefix. If you want to serve SSL-protected pages through a special site, use this
291      * prefix. This can come in handy if you have multiple sites that need SSL protection and you
292      * want to share one SSL certificate.
293      *
294      * @return the SSL prefix
295      */

296     public String JavaDoc getSSLPrefix() {
297         return sslprefix;
298     }
299
300     /**
301      * Get the Live mount point. The live mount point is used to rewrite links that are of the form
302      * /contextprefix/publication/area/documentid to /livemountpoint/documentid
303      *
304      * This is useful if you serve your live area through mod_proxy. to enable this functionality,
305      * set the Live mount point to / or something else. An empty mount point disables the feature.
306      *
307      * @return the Live mount point
308      */

309     public String JavaDoc getLiveMountPoint() {
310         return livemountpoint;
311     }
312
313     /**
314      * Get the sitetree for a specific area of this publication. Sitetrees are created on demand and
315      * are cached.
316      *
317      * @param area the area
318      * @return the sitetree for the specified area
319      *
320      * @throws SiteTreeException if an error occurs
321      */

322     public SiteTree getTree(String JavaDoc area) throws SiteTreeException {
323
324         SiteTree sitetree = null;
325
326         if (hasSitetree) {
327             if (siteTrees.containsKey(area)) {
328                 sitetree = (SiteTree) siteTrees.get(area);
329             } else {
330                 sitetree = new DefaultSiteTree(getDirectory(), area);
331                 siteTrees.put(area, sitetree);
332             }
333         }
334         return sitetree;
335     }
336
337     /**
338      * Get the sitetree for a specific area of this publication. Sitetrees are created on demand and
339      * are cached.
340      *
341      * @deprecated Please use getTree() because this method returns the interface and not a specific implementation
342      * @see getTree()
343      *
344      * @param area the area
345      * @return the sitetree for the specified area
346      *
347      * @throws SiteTreeException if an error occurs
348      */

349     public DefaultSiteTree getSiteTree(String JavaDoc area) throws SiteTreeException {
350
351         DefaultSiteTree sitetree = null;
352
353         if (hasSitetree) {
354             if (siteTrees.containsKey(area)) {
355                 sitetree = (DefaultSiteTree) siteTrees.get(area);
356             } else {
357                 sitetree = new DefaultSiteTree(getDirectory(), area);
358                 siteTrees.put(area, sitetree);
359             }
360         }
361         return sitetree;
362     }
363
364     private DocumentBuilder documentBuilder;
365
366     /**
367      * Returns the document builder of this instance.
368      * @return A document builder.
369      */

370     public DocumentBuilder getDocumentBuilder() {
371
372         if (documentBuilder == null) {
373             throw new IllegalStateException JavaDoc(
374                     "The document builder was not defined in publication.xconf!");
375         }
376
377         return documentBuilder;
378     }
379
380     /**
381      * Creates a version of the document object in another area.
382      * @param document The document to clone.
383      * @param area The destination area.
384      * @return A document.
385      * @throws PublicationException when an error occurs.
386      */

387     public Document getAreaVersion(Document document, String JavaDoc area) throws PublicationException {
388         DocumentBuilder builder = getDocumentBuilder();
389         String JavaDoc url = builder
390                 .buildCanonicalUrl(this, area, document.getId(), document.getLanguage());
391         Document destinationDocument = builder.buildDocument(this, url);
392         return destinationDocument;
393     }
394
395     /**
396      * @see java.lang.Object#equals(java.lang.Object)
397      */

398     public boolean equals(Object JavaDoc object) {
399         boolean equals = false;
400
401         if (getClass().isInstance(object)) {
402             Publication publication = (Publication) object;
403             equals = getId().equals(publication.getId())
404                     && getServletContext().equals(publication.getServletContext());
405         }
406
407         return equals;
408     }
409
410     /**
411      * @see java.lang.Object#hashCode()
412      */

413     public int hashCode() {
414         String JavaDoc key = getServletContext() + ":" + getId();
415         return key.hashCode();
416     }
417
418     /**
419      * Template method to copy a document. Override {@link #copyDocumentSource(Document, Document)}
420      * to implement access to a custom repository.
421      * @see org.apache.lenya.cms.publication.Publication#copyDocument(org.apache.lenya.cms.publication.Document,
422      * org.apache.lenya.cms.publication.Document)
423      */

424     public void copyDocument(Document sourceDocument, Document destinationDocument)
425             throws PublicationException {
426
427         copyDocumentSource(sourceDocument, destinationDocument);
428
429         copySiteStructure(sourceDocument, destinationDocument);
430     }
431
432     /**
433      * Copies a document in the site structure.
434      * @param sourceDocument The source document.
435      * @param destinationDocument The destination document.
436      * @throws PublicationException when something went wrong.
437      */

438     protected void copySiteStructure(Document sourceDocument, Document destinationDocument)
439             throws PublicationException {
440         if (hasSitetree) {
441             try {
442                 SiteTree sourceTree = getSiteTree(sourceDocument.getArea());
443                 SiteTree destinationTree = getSiteTree(destinationDocument.getArea());
444
445                 SiteTreeNode sourceNode = sourceTree.getNode(sourceDocument.getId());
446                 if (sourceNode == null) {
447                     throw new PublicationException("The node for source document ["
448                             + sourceDocument.getId() + "] doesn't exist!");
449                 }
450                 SiteTreeNode[] siblings = sourceNode.getNextSiblings();
451                 String JavaDoc parentId = sourceNode.getAbsoluteParentId();
452                 SiteTreeNode sibling = null;
453                 String JavaDoc siblingDocId = null;
454
455                 // same document ID -> insert at the same position
456
if (sourceDocument.getId().equals(destinationDocument.getId())) {
457                     for (int i = 0; i < siblings.length; i++) {
458                         String JavaDoc docId = parentId + "/" + siblings[i].getId();
459                         sibling = destinationTree.getNode(docId);
460                         if (sibling != null) {
461                             siblingDocId = docId;
462                             break;
463                         }
464                     }
465                 }
466                 
467          
468                 Label label = sourceNode.getLabel(sourceDocument.getLanguage());
469                 if (label == null) {
470                     // the node that we're trying to publish
471
// doesn't have this language
472
throw new PublicationException("The node " + sourceDocument.getId()
473                             + " doesn't contain a label for language "
474                             + sourceDocument.getLanguage());
475                 }
476                 SiteTreeNode destinationNode = destinationTree.getNode(destinationDocument
477                         .getId());
478                 if (destinationNode == null) {
479                     Label[] labels = { label };
480
481                     if (siblingDocId == null) {
482                         destinationTree.addNode(destinationDocument.getId(), labels,
483                                 sourceNode.visibleInNav(), sourceNode.getHref(), sourceNode.getSuffix(), sourceNode
484                                         .hasLink());
485                     } else {
486                         destinationTree.addNode(destinationDocument.getId(), labels, sourceNode.visibleInNav(),
487                                 sourceNode.getHref(), sourceNode.getSuffix(), sourceNode
488                                         .hasLink(), siblingDocId);
489                     }
490
491                 } else {
492                     // if the node already exists in the live
493
// tree simply insert the label in the
494
// live tree
495
destinationTree.setLabel(destinationDocument.getId(), label);
496                     //and synchronize visibilityinnav attribute with the one in the source area
497
String JavaDoc visibility ="true";
498                     if (!sourceNode.visibleInNav()) visibility = "false";
499                     destinationNode.setNodeAttribute(SiteTreeNodeImpl.VISIBLEINNAV_ATTRIBUTE_NAME,
500                             visibility);
501
502                     // also update the link attribute if necessary
503
if (sourceNode.hasLink() != destinationNode.hasLink()) {
504                         String JavaDoc link = (sourceNode.hasLink() ? "true" : "false");
505                         destinationNode.setNodeAttribute(SiteTreeNodeImpl.LINK_ATTRIBUTE_NAME, link);
506                     }
507
508                 }
509
510                 destinationTree.save();
511             } catch (SiteTreeException e) {
512                 throw new PublicationException(e);
513             }
514         }
515     }
516
517     /**
518      * Copies a document source.
519      * @param sourceDocument The source document.
520      * @param destinationDocument The destination document.
521      * @throws PublicationException when something went wrong.
522      */

523     protected abstract void copyDocumentSource(Document sourceDocument, Document destinationDocument)
524             throws PublicationException;
525
526     /**
527      * @see org.apache.lenya.cms.publication.Publication#deleteDocument(org.apache.lenya.cms.publication.Document)
528      */

529     public void deleteDocument(Document document) throws PublicationException {
530         if (!document.exists()) {
531             throw new PublicationException("Document [" + document + "] does not exist!");
532         }
533         deleteFromSiteStructure(document);
534         deleteDocumentSource(document);
535     }
536
537     /**
538      * Deletes a document from the site structure.
539      * @param document The document to remove.
540      * @throws PublicationException when something went wrong.
541      */

542     protected void deleteFromSiteStructure(Document document) throws PublicationException {
543         if (hasSitetree) {
544             SiteTree tree;
545             try {
546                 tree = getSiteTree(document.getArea());
547             } catch (SiteTreeException e) {
548                 throw new PublicationException(e);
549             }
550
551             SiteTreeNode node = tree.getNode(document.getId());
552
553             if (node == null) {
554                 throw new PublicationException("Sitetree node for document [" + document
555                         + "] does not exist!");
556             }
557
558             Label label = node.getLabel(document.getLanguage());
559
560             if (label == null) {
561                 throw new PublicationException("Sitetree label for document [" + document
562                         + "] in language [" + document.getLanguage() + "]does not exist!");
563             }
564
565             if (node.getLabels().length == 1 && node.getChildren().length > 0) {
566                 throw new PublicationException("Cannot delete last language version of document ["
567                         + document + "] because this node has children.");
568             }
569
570             node.removeLabel(label);
571
572             try {
573                 if (node.getLabels().length == 0) {
574                     tree.deleteNode(document.getId());
575                 }
576
577                 tree.save();
578             } catch (SiteTreeException e) {
579                 throw new PublicationException(e);
580             }
581         }
582     }
583
584     /**
585      * Deletes the source of a document.
586      * @param document The document to delete.
587      * @throws PublicationException when something went wrong.
588      */

589     protected abstract void deleteDocumentSource(Document document) throws PublicationException;
590
591     /**
592      * @see org.apache.lenya.cms.publication.Publication#moveDocument(org.apache.lenya.cms.publication.Document,
593      * org.apache.lenya.cms.publication.Document)
594      */

595     public void moveDocument(Document sourceDocument, Document destinationDocument)
596             throws PublicationException {
597         copyDocument(sourceDocument, destinationDocument);
598         deleteDocument(sourceDocument);
599     }
600
601     private Map JavaDoc areaSsl2proxy = new HashMap JavaDoc();
602
603     /**
604      * Generates a hash key for a area-SSL combination.
605      * @param area The area.
606      * @param isSslProtected If the proxy is assigned for SSL-protected pages.
607      * @return An object.
608      */

609     protected Object JavaDoc getProxyKey(String JavaDoc area, boolean isSslProtected) {
610         return area + ":" + isSslProtected;
611     }
612
613     /**
614      * @see org.apache.lenya.cms.publication.Publication#getProxy(org.apache.lenya.cms.publication.Document,
615      * boolean)
616      */

617     public Proxy getProxy(Document document, boolean isSslProtected) {
618
619         Object JavaDoc key = getProxyKey(document.getArea(), isSslProtected);
620         Proxy proxy = (Proxy) this.areaSsl2proxy.get(key);
621
622         if (log.isDebugEnabled()) {
623             log.debug("Resolving proxy for [" + document + "] SSL=[" + isSslProtected + "]");
624             log.debug("Resolved proxy: [" + proxy + "]");
625         }
626
627         return proxy;
628     }
629
630     /**
631      * @see org.apache.lenya.cms.publication.Publication#getRewriteAttributeXPaths()
632      */

633     public String JavaDoc[] getRewriteAttributeXPaths() {
634         return this.rewriteAttributeXPaths;
635     }
636 }
637
Popular Tags