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.cms.usecases.webdav;
019:
020: import java.util.ArrayList;
021: import java.util.HashMap;
022: import java.util.List;
023:
024: import org.apache.avalon.framework.configuration.Configuration;
025: import org.apache.avalon.framework.configuration.ConfigurationException;
026: import org.apache.avalon.framework.service.ServiceException;
027: import org.apache.avalon.framework.service.ServiceSelector;
028: import org.apache.excalibur.source.SourceResolver;
029: import org.apache.lenya.cms.cocoon.source.SourceUtil;
030: import org.apache.lenya.cms.metadata.MetaData;
031: import org.apache.lenya.cms.metadata.MetaDataException;
032: import org.apache.lenya.cms.metadata.dublincore.DublinCore;
033: import org.apache.lenya.cms.publication.Document;
034: import org.apache.lenya.cms.publication.DocumentFactory;
035: import org.apache.lenya.cms.publication.DocumentManager;
036: import org.apache.lenya.cms.publication.Publication;
037: import org.apache.lenya.cms.publication.PublicationException;
038: import org.apache.lenya.cms.publication.PublicationUtil;
039: import org.apache.lenya.cms.publication.ResourceType;
040: import org.apache.lenya.cms.repository.Node;
041: import org.apache.lenya.cms.site.NodeSet;
042: import org.apache.lenya.cms.site.SiteStructure;
043: import org.apache.lenya.cms.site.SiteUtil;
044: import org.apache.lenya.cms.site.usecases.CreateDocument;
045: import org.apache.lenya.cms.usecase.UsecaseException;
046: import org.apache.lenya.cms.usecase.xml.UsecaseErrorHandler;
047: import org.apache.lenya.cms.workflow.WorkflowUtil;
048: import org.apache.lenya.cms.workflow.usecases.UsecaseWorkflowHelper;
049: import org.apache.lenya.xml.DocumentHelper;
050: import org.apache.lenya.xml.Schema;
051: import org.apache.lenya.xml.ValidationUtil;
052:
053: /**
054: * Supports WebDAV PUT.
055: * @version $Id: $
056: */
057: public class Put extends CreateDocument {
058:
059: // registeredExtensions contain all known extension matching to a certain resource-type.
060: private HashMap registeredExtensions = new HashMap();
061: // default is xhtml and xml but you can override it with the config
062: protected String TYPE = "xhtml";
063: protected String EXTENSION = "*";
064: protected static final String ATTRIBUTE_TYPE = "resource-type";
065: protected static final String ELEMENT_ROOT = "extensions";
066: protected static final String ELEMENT_EXTENSION = "extension";
067: protected static final String EVENT = "lenya.event";
068:
069: private boolean fallback = false;
070:
071: public void configure(Configuration config)
072: throws ConfigurationException {
073: super .configure(config);
074: Configuration extensionsConfig = config.getChild(ELEMENT_ROOT,
075: false);
076: if (extensionsConfig != null) {
077: Configuration[] extensions = extensionsConfig
078: .getChildren(ELEMENT_EXTENSION);
079: for (int i = 0; i < extensions.length; i++) {
080: Configuration extension = extensions[i];
081: // add extension to register (key: extension,value: resource-type)
082: if (extension != null)
083: registeredExtensions.put(extension.getValue(),
084: extension.getAttribute(ATTRIBUTE_TYPE));
085: }
086: } else {
087: registeredExtensions.put(this .EXTENSION, this .TYPE);
088: }
089: }
090:
091: protected void doCheckExecutionConditions() throws Exception {
092: String event = getParameterAsString(EVENT);
093: if (event != null) {
094: Document doc = getSourceDocument();
095: UsecaseWorkflowHelper.checkWorkflow(this .manager, this ,
096: event, doc, getLogger());
097: }
098: }
099:
100: /**
101: * @see org.apache.lenya.cms.usecase.AbstractUsecase#doExecute()
102: */
103: protected void doExecute() throws Exception {
104: SourceResolver resolver = null;
105:
106: try {
107: resolver = (SourceResolver) this .manager
108: .lookup(SourceResolver.ROLE);
109:
110: Document doc = getSourceDocument();
111: String extension = getSourceExtension();
112: // sanity check
113: if (doc == null)
114: throw new IllegalArgumentException(
115: "illegal usage, source document may not be null");
116:
117: // create new doc from PUT input
118: if (!doc.exists()) {
119: DocumentManager documentManager = null;
120: ServiceSelector selector = null;
121: ResourceType resourceType = null;
122: try {
123: selector = (ServiceSelector) this .manager
124: .lookup(ResourceType.ROLE + "Selector");
125:
126: documentManager = (DocumentManager) this .manager
127: .lookup(DocumentManager.ROLE);
128:
129: DocumentFactory map = getDocumentFactory();
130: String path = doc.getPath();
131: // lookupResourceType(extension)
132: resourceType = lookUpExtension(extension, selector);
133: ResourceType.Sample sample = resourceType
134: .getSample(resourceType.getSampleNames()[0]);
135: doc = documentManager.add(map, resourceType, sample
136: .getUri(), getPublication(), doc.getArea(),
137: path, doc.getLanguage(), extension, doc
138: .getName(), true);
139: doc.setMimeType(sample.getMimeType());
140: setMetaData(doc);
141: } finally {
142: if (documentManager != null) {
143: this .manager.release(documentManager);
144: }
145: if (selector != null) {
146: if (resourceType != null) {
147: selector.release(resourceType);
148: }
149: this .manager.release(selector);
150: }
151: }
152: }
153:
154: String sourceUri = "cocoon:/request/PUT/" + extension;
155: org.w3c.dom.Document xmlDoc = SourceUtil.readDOM(sourceUri,
156: manager);
157:
158: // validate if a schema is provided and we are not using any fallback
159: if (doc.getResourceType().getSchema() != null
160: & fallback == false) {
161: validateDoc(resolver, xmlDoc, doc);
162: }
163:
164: if (!hasErrors()) {
165: try {
166: DocumentHelper.writeDocument(xmlDoc, doc
167: .getOutputStream());
168: } catch (Exception e) {
169: addErrorMessage("invalid source xml. Full exception: "
170: + e);
171: }
172: }
173:
174: String event = getParameterAsString(EVENT);
175: if (event != null) {
176: WorkflowUtil.invoke(this .manager, getSession(),
177: getLogger(), doc, event);
178: }
179:
180: } finally {
181: if (resolver != null) {
182: this .manager.release(resolver);
183: }
184: }
185: }
186:
187: private ResourceType lookUpExtension(String extension,
188: ServiceSelector selector) throws ServiceException {
189: ResourceType resourceType;
190: String resourceTypeName = (String) registeredExtensions
191: .get(extension);
192: if (resourceTypeName == null || resourceTypeName.equals("")) {
193: resourceTypeName = (String) registeredExtensions
194: .get(this .EXTENSION);
195: this .fallback = true;
196: }
197: if (selector.isSelectable(resourceTypeName)) {
198: resourceType = (ResourceType) selector
199: .select(resourceTypeName);
200: } else {
201: // using a fallback resource type
202: // FIXME this resource tye should be a more generic one like "media-assets" or "bin"
203: resourceType = (ResourceType) selector.select(this .TYPE);
204: this .fallback = true;
205: }
206: return resourceType;
207: }
208:
209: private void validateDoc(SourceResolver resolver,
210: org.w3c.dom.Document xmlDoc, Document doc) throws Exception {
211: ResourceType resourceType = doc.getResourceType();
212: Schema schema = resourceType.getSchema();
213: ValidationUtil.validate(this .manager, xmlDoc, schema,
214: new UsecaseErrorHandler(this ));
215: }
216:
217: /**
218: * @see org.apache.lenya.cms.usecase.AbstractUsecase#getNodesToLock()
219: */
220: protected Node[] getNodesToLock() throws UsecaseException {
221: try {
222: Document doc = getSourceDocument();
223: List nodes = new ArrayList();
224:
225: NodeSet set = SiteUtil.getSubSite(this .manager, doc
226: .getLink().getNode());
227: Document[] documents = set.getDocuments();
228: for (int i = 0; i < documents.length; i++) {
229: nodes.add(documents[i].getRepositoryNode());
230: }
231:
232: SiteStructure structure = getSourceDocument().area()
233: .getSite();
234: nodes.add(structure.getRepositoryNode());
235: return (Node[]) nodes.toArray(new Node[nodes.size()]);
236:
237: } catch (Exception e) {
238: throw new UsecaseException(e);
239: }
240: }
241:
242: /**
243: * Sets the meta data of the created document.
244: *
245: * @param document The document.
246: * @throws MetaDataException if an error occurs.
247: */
248: protected void setMetaData(Document document)
249: throws MetaDataException {
250:
251: if (document == null)
252: throw new IllegalArgumentException(
253: "parameter document may not be null");
254:
255: MetaData dcMetaData = document
256: .getMetaData(DublinCore.DC_NAMESPACE);
257: dcMetaData.setValue(DublinCore.ELEMENT_TITLE, document
258: .getName());
259: dcMetaData.setValue(DublinCore.ELEMENT_CREATOR, "");
260: dcMetaData.setValue(DublinCore.ELEMENT_PUBLISHER, "");
261: dcMetaData.setValue(DublinCore.ELEMENT_SUBJECT, "");
262: dcMetaData.setValue(DublinCore.ELEMENT_DATE, "");
263: dcMetaData.setValue(DublinCore.ELEMENT_RIGHTS, "");
264: dcMetaData.setValue(DublinCore.ELEMENT_LANGUAGE, document
265: .getLanguage());
266: }
267:
268: private Publication publication;
269:
270: /**
271: * Access to the current publication. Use this when the publication is not yet known in the
272: * usecase: e.g. when creating a global asset. When adding a resource or a child to a document,
273: * access the publication via that document's interface instead.
274: *
275: * @return the publication in which the use-case is being executed
276: */
277: protected Publication getPublication() {
278: if (this .publication == null) {
279: try {
280: this .publication = PublicationUtil
281: .getPublicationFromUrl(this .manager,
282: getDocumentFactory(), getSourceURL());
283: } catch (PublicationException e) {
284: throw new RuntimeException(e);
285: }
286: }
287: return this .publication;
288: }
289:
290: protected String getSourceExtension() {
291: String destinationUri = getParameterAsString(SOURCE_URL);
292: String extension = null;
293: if (destinationUri.indexOf(".") > 0) {
294: extension = destinationUri.substring(destinationUri
295: .lastIndexOf(".") + 1, destinationUri.length());
296: } else {
297: extension = EXTENSION;
298: }
299: return extension;
300: }
301:
302: protected String getNewDocumentName() {
303: // TODO Auto-generated method stub
304: return null;
305: }
306:
307: protected String getNewDocumentPath() {
308: Document doc = getSourceDocument();
309: return doc.getUUID();
310: }
311:
312: protected boolean getVisibleInNav() {
313: return true;
314: }
315:
316: protected String getDocumentTypeName() {
317: ResourceType resourceType = null;
318: ServiceSelector selector = null;
319: String docType = "";
320: try {
321: selector = (ServiceSelector) this .manager
322: .lookup(ResourceType.ROLE + "Selector");
323: resourceType = lookUpExtension(getSourceExtension(),
324: selector);
325: docType = resourceType.getName();
326: } catch (ServiceException e) {
327: throw new RuntimeException(e);
328: } finally {
329: if (selector != null) {
330: this.manager.release(selector);
331: }
332: }
333: return docType;
334: }
335:
336: }
|