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: */
018: package org.apache.lenya.defaultpub.cms.publication.templating;
019:
020: import java.io.IOException;
021: import java.io.OutputStream;
022: import java.io.OutputStreamWriter;
023: import java.net.MalformedURLException;
024: import java.util.Collection;
025: import java.util.Iterator;
026:
027: import javax.xml.parsers.ParserConfigurationException;
028: import javax.xml.transform.TransformerConfigurationException;
029: import javax.xml.transform.TransformerException;
030:
031: import org.apache.avalon.framework.configuration.Configuration;
032: import org.apache.avalon.framework.configuration.MutableConfiguration;
033: import org.apache.avalon.framework.configuration.ConfigurationException;
034: import org.apache.avalon.framework.configuration.DefaultConfiguration;
035: import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
036: import org.apache.avalon.framework.configuration.DefaultConfigurationSerializer;
037: import org.apache.avalon.framework.logger.AbstractLogEnabled;
038: import org.apache.avalon.framework.service.ServiceException;
039: import org.apache.avalon.framework.service.ServiceManager;
040: import org.apache.avalon.framework.service.Serviceable;
041: import org.apache.cocoon.components.search.components.IndexManager;
042: import org.apache.excalibur.source.ModifiableSource;
043: import org.apache.excalibur.source.Source;
044: import org.apache.excalibur.source.SourceNotFoundException;
045: import org.apache.excalibur.source.SourceResolver;
046: import org.apache.excalibur.source.impl.FileSource;
047: import org.apache.lenya.cms.publication.Publication;
048: import org.apache.lenya.cms.publication.PublicationManager; // import org.apache.lenya.cms.publication.PublicationConfiguration;
049: import org.apache.lenya.xml.DocumentHelper;
050: import org.apache.lenya.xml.NamespaceHelper;
051: import org.w3c.dom.Document;
052: import org.w3c.dom.Element;
053: import org.xml.sax.SAXException;
054:
055: /**
056: * Instantiate the publication.
057: *
058: * @version $Id: Instantiator.java 567473 2007-08-19 21:12:37Z andreas $
059: */
060: public class Instantiator extends AbstractLogEnabled implements
061: org.apache.lenya.cms.publication.templating.Instantiator,
062: Serviceable {
063:
064: protected static final String[] sourcesToCopy = {
065: "config/publication.xml",
066: "config/access-control/access-control.xml",
067: "config/access-control/policies/",
068: "config/access-control/usecase-policies.xml",
069: "config/workflow/workflow.xml",
070: "config/"
071: + org.apache.cocoon.components.search.components.impl.IndexManagerImpl.INDEX_CONF_FILE };
072:
073: // the following stuff should actually come from PublicationConfiguration,
074: // but there's currently no way to get at it.
075: // the correct solution suggested by andreas is not to meddle with config
076: // files here at all, but instead implement
077: // appropriate setter functions in the Publication class. postponed to after
078: // 2.0.
079: // thus, don't waste too much effort on this file, it's all an intermediate
080: // hack.
081: private static final String CONFIGURATION_FILE = "config/publication.xml";
082: private static final String CONFIGURATION_NAMESPACE = "http://apache.org/cocoon/lenya/publication/1.1";
083: private static final String ELEMENT_NAME = "name";
084: private static final String ELEMENT_TEMPLATE = "template";
085: private static final String ATTRIBUTE_ID = "id";
086: private static final String ELEMENT_RESOURCE_TYPES = "resource-types";// *
087: private static final String ELEMENT_RESOURCE_TYPE = "resource-type";// *
088: private static final String ELEMENT_MODULES = "modules";// *
089: private static final String ELEMENT_MODULE = "module";// *
090:
091: /**
092: * @see org.apache.lenya.cms.publication.templating.Instantiator#instantiate(org.apache.lenya.cms.publication.Publication,
093: * java.lang.String, java.lang.String)
094: */
095: public void instantiate(Publication template,
096: String newPublicationId, String name) throws Exception {
097:
098: SourceResolver resolver = null;
099: Source publicationsSource = null;
100: PublicationManager pubManager = null;
101: try {
102: resolver = (SourceResolver) this .manager
103: .lookup(SourceResolver.ROLE);
104:
105: publicationsSource = resolver.resolveURI("context://"
106: + Publication.PUBLICATION_PREFIX_URI);
107: String publicationsUri = publicationsSource.getURI();
108:
109: for (int i = 0; i < sourcesToCopy.length; i++) {
110:
111: String source = sourcesToCopy[i];
112: if (source.endsWith("/")) {
113: copyDirSource(template, newPublicationId, resolver,
114: publicationsUri, source);
115: } else {
116: copySource(template, newPublicationId, resolver,
117: publicationsUri, source);
118: }
119: }
120:
121: updateMetaData(resolver, newPublicationId, name,
122: publicationsUri);
123:
124: configureSearchIndex(resolver, template, newPublicationId,
125: publicationsUri);
126:
127: updateConfiguration(resolver, template, newPublicationId,
128: publicationsUri);
129:
130: pubManager = (PublicationManager) this .manager
131: .lookup(PublicationManager.ROLE);
132: pubManager.addPublication(newPublicationId);
133:
134: } finally {
135: if (resolver != null) {
136: this .manager.release(resolver);
137: if (publicationsSource != null) {
138: resolver.release(publicationsSource);
139: }
140: }
141: if (pubManager != null) {
142: this .manager.release(pubManager);
143: }
144: }
145:
146: }
147:
148: protected void updateMetaData(SourceResolver resolver,
149: String newPublicationId, String name, String publicationsUri)
150: throws MalformedURLException, IOException,
151: ParserConfigurationException, SAXException,
152: SourceNotFoundException, TransformerConfigurationException,
153: TransformerException {
154: ModifiableSource metaSource = null;
155: try {
156: metaSource = (ModifiableSource) resolver
157: .resolveURI(publicationsUri + "/"
158: + newPublicationId + "/"
159: + CONFIGURATION_FILE);
160: Document metaDoc = DocumentHelper.readDocument(metaSource
161: .getInputStream());
162: NamespaceHelper helper = new NamespaceHelper(
163: CONFIGURATION_NAMESPACE, "", metaDoc);
164: Element nameElement = helper.getFirstChild(metaDoc
165: .getDocumentElement(), ELEMENT_NAME);
166: DocumentHelper.setSimpleElementText(nameElement, name);
167:
168: save(metaDoc, metaSource);
169: } finally {
170: if (resolver != null) {
171: if (metaSource != null) {
172: resolver.release(metaSource);
173: }
174: }
175: }
176: }
177:
178: protected void updateConfiguration(SourceResolver resolver,
179: Publication template, String newPublicationId,
180: String publicationsUri) throws MalformedURLException,
181: IOException, SAXException, ConfigurationException,
182: SourceNotFoundException {
183: ModifiableSource configSource = null;
184: try {
185:
186: configSource = (ModifiableSource) resolver
187: .resolveURI(publicationsUri + "/"
188: + newPublicationId + "/"
189: + CONFIGURATION_FILE);
190:
191: final boolean ENABLE_XML_NAMESPACES = true;
192: DefaultConfiguration config = (DefaultConfiguration) new DefaultConfigurationBuilder(
193: ENABLE_XML_NAMESPACES).build(configSource
194: .getInputStream());
195: addTemplateConfiguration(template, config);
196:
197: removeChildren(config.getMutableChild(ELEMENT_MODULES),
198: ELEMENT_MODULE);
199: removeChildren(config
200: .getMutableChild(ELEMENT_RESOURCE_TYPES),
201: ELEMENT_RESOURCE_TYPE);
202:
203: OutputStream oStream = configSource.getOutputStream();
204: new DefaultConfigurationSerializer().serialize(oStream,
205: config);
206: if (oStream != null) {
207: oStream.flush();
208: try {
209: oStream.close();
210: } catch (Throwable t) {
211: if (getLogger().isDebugEnabled()) {
212: getLogger().debug(
213: "Exception closing output stream: ", t);
214: }
215: throw new RuntimeException(
216: "Could not write document: ", t);
217: }
218: }
219: } finally {
220: if (resolver != null) {
221: if (configSource != null) {
222: resolver.release(configSource);
223: }
224: }
225: }
226: }
227:
228: protected void addTemplateConfiguration(Publication template,
229: DefaultConfiguration config) throws ConfigurationException {
230: Configuration[] templateConfigs = config
231: .getChildren(ELEMENT_TEMPLATE);
232: for (int i = 0; i < templateConfigs.length; i++) {
233: config.removeChild(templateConfigs[i]);
234: }
235: DefaultConfiguration templateConfig = new DefaultConfiguration(
236: ELEMENT_TEMPLATE, null, CONFIGURATION_NAMESPACE, "");
237: templateConfig.setAttribute(ATTRIBUTE_ID, template.getId());
238: config.addChild(templateConfig);
239: }
240:
241: protected void removeChildren(MutableConfiguration config,
242: String name) throws ConfigurationException {
243: MutableConfiguration[] moduleConfigs = config
244: .getMutableChildren(name);
245: for (int i = 0; i < moduleConfigs.length; i++) {
246: config.removeChild(moduleConfigs[i]);
247: }
248: }
249:
250: protected void configureSearchIndex(SourceResolver resolver,
251: Publication template, String newPublicationId,
252: String publicationsUri) throws MalformedURLException,
253: IOException, ParserConfigurationException, SAXException,
254: SourceNotFoundException, TransformerConfigurationException,
255: TransformerException, ServiceException,
256: ConfigurationException {
257: ModifiableSource indexSource = null;
258: IndexManager indexManager = null;
259: try {
260:
261: // RGE: Soc addition
262: // First, patch the xconf patchfile with the new publication name
263:
264: String indexDir = "lenya/pubs/" + newPublicationId
265: + "/work/lucene/index";
266:
267: indexSource = (ModifiableSource) resolver
268: .resolveURI(publicationsUri
269: + "/"
270: + newPublicationId
271: + "/config/"
272: + org.apache.cocoon.components.search.components.impl.IndexManagerImpl.INDEX_CONF_FILE);
273: Document indexDoc = DocumentHelper.readDocument(indexSource
274: .getInputStream());
275: Element[] indexElement = DocumentHelper.getChildren(
276: indexDoc.getDocumentElement(), null, "index");
277:
278: for (int i = 0; i < indexElement.length; i++) {
279: String id = indexElement[i].getAttribute("id");
280: String area = id.split("-")[1];
281: indexElement[i].setAttribute("id", newPublicationId
282: + "-" + area);
283: indexElement[i].setAttribute("directory", indexDir
284: + "/" + area + "/index");
285: }
286:
287: save(indexDoc, indexSource);
288:
289: // Second, configure the index and add it to the IndexManager
290:
291: indexManager = (IndexManager) manager
292: .lookup(IndexManager.ROLE);
293:
294: indexManager.addIndexes(indexSource);
295:
296: // TODO: release all objects!
297:
298: // RGE: End Soc addition
299:
300: } finally {
301: if (indexManager != null) {
302: this .manager.release(indexManager);
303: }
304: if (resolver != null) {
305: this .manager.release(resolver);
306: if (indexSource != null) {
307: resolver.release(indexSource);
308: }
309: }
310: }
311: }
312:
313: protected void copySource(Publication template,
314: String publicationId, SourceResolver resolver,
315: String publicationsUri, String source)
316: throws MalformedURLException, IOException {
317: Source templateSource = null;
318: ModifiableSource targetSource = null;
319: try {
320: templateSource = resolver.resolveURI(publicationsUri + "/"
321: + template.getId() + "/" + source);
322: targetSource = (ModifiableSource) resolver
323: .resolveURI(publicationsUri + "/" + publicationId
324: + "/" + source);
325:
326: org.apache.lenya.cms.cocoon.source.SourceUtil.copy(
327: templateSource, targetSource, false);
328: } finally {
329: if (templateSource != null) {
330: resolver.release(templateSource);
331: }
332: if (targetSource != null) {
333: resolver.release(targetSource);
334: }
335: }
336: }
337:
338: protected void copyDirSource(Publication template,
339: String publicationId, SourceResolver resolver,
340: String publicationsUri, String source)
341: throws MalformedURLException, IOException {
342: FileSource directory = new FileSource(publicationsUri + "/"
343: + template.getId() + "/" + source);
344: Collection files = directory.getChildren();
345: for (Iterator iter = files.iterator(); iter.hasNext();) {
346: FileSource filesource = (FileSource) iter.next();
347: if (filesource.isCollection()) {
348: copyDirSource(template, publicationId, resolver,
349: publicationsUri, source + "/"
350: + filesource.getName());
351: } else {
352: copySource(template, publicationId, resolver,
353: publicationsUri, source + "/"
354: + filesource.getName());
355: }
356: }
357: }
358:
359: protected void save(Document metaDoc, ModifiableSource metaSource)
360: throws IOException, TransformerConfigurationException,
361: TransformerException {
362: OutputStream oStream = metaSource.getOutputStream();
363: DocumentHelper.writeDocument(metaDoc, new OutputStreamWriter(
364: oStream));
365: if (oStream != null) {
366: oStream.flush();
367: try {
368: oStream.close();
369: } catch (Throwable t) {
370: if (getLogger().isDebugEnabled()) {
371: getLogger().debug(
372: "Exception closing output stream: ", t);
373: }
374: throw new RuntimeException(
375: "Could not write document: ", t);
376: }
377: }
378: }
379:
380: private ServiceManager manager;
381:
382: /**
383: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
384: */
385: public void service(ServiceManager manager) throws ServiceException {
386: this.manager = manager;
387: }
388:
389: }
|