001: /*
002: * Copyright 2005 Hippo Webworks.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package nl.hippo.slide.webdav.method;
017:
018: import java.io.BufferedInputStream;
019: import java.io.ByteArrayInputStream;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.InputStreamReader;
023: import java.io.Reader;
024: import java.util.Date;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Vector;
028: import java.util.regex.Matcher;
029: import java.util.regex.Pattern;
030:
031: import org.apache.slide.common.NamespaceAccessToken;
032: import org.apache.slide.common.Uri;
033: import org.apache.slide.content.NodeRevisionContent;
034: import org.apache.slide.content.NodeRevisionDescriptor;
035: import org.apache.slide.content.NodeRevisionDescriptors;
036: import org.apache.slide.content.RevisionContentNotFoundException;
037: import org.apache.slide.content.RevisionDescriptorNotFoundException;
038: import org.apache.slide.content.RevisionNotFoundException;
039: import org.apache.slide.store.ExtendedStore;
040: import org.apache.slide.store.Store;
041: import org.apache.slide.structure.LinkedObjectNotFoundException;
042: import org.apache.slide.webdav.WebdavException;
043: import org.apache.slide.webdav.WebdavServletConfig;
044: import org.apache.slide.webdav.method.AbstractWebdavMethod;
045: import org.apache.slide.webdav.method.WriteMethod;
046: import org.apache.slide.webdav.util.PropertyHelper;
047: import org.apache.slide.webdav.util.WebdavStatus;
048: import org.jdom.Document;
049: import org.jdom.Element;
050: import org.jdom.JDOMException;
051: import org.jdom.Namespace;
052: import org.jdom.input.SAXBuilder;
053: import org.jdom.output.Format;
054: import org.jdom.output.XMLOutputter;
055:
056: /**
057: * @author mpfingsthorn
058: *
059: */
060: public class ReplaceMethod extends AbstractWebdavMethod implements
061: WriteMethod {
062:
063: public static final Namespace HIPPO_NS = Namespace.getNamespace(
064: "hippo", "http://hippo.nl/slide");
065: public static final String REQUEST_EL = "replace-request";
066: public static final String RESPONSE_EL = "replace-response";
067: public static final String REPLACE_EL = "replace";
068: public static final String WITH_EL = "with";
069: public static final String IN_EL = "in";
070: public static final String RESOURCE_EL = "resource";
071:
072: public static final String DEFAULT_ENCODING = "UTF-8";
073:
074: protected ReplaceRequest request = null;
075:
076: public ReplaceMethod(NamespaceAccessToken token,
077: WebdavServletConfig config) {
078: super (token, config);
079: }
080:
081: protected void parseRequest() throws WebdavException {
082: try {
083: Element replaceRequest = getQueryElement();
084: if (replaceRequest.getName().equals(REQUEST_EL)
085: && replaceRequest.getNamespace().equals(HIPPO_NS)) {
086: request = new ReplaceRequest(replaceRequest);
087: } else {
088: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
089: resp.setContentType(TEXT_XML_UTF_8);
090: createErrorResult(WebdavStatus.SC_BAD_REQUEST,
091: "Top level element must be " + HIPPO_NS + ":"
092: + REQUEST_EL + "!");
093: throw new WebdavException(WebdavStatus.SC_BAD_REQUEST);
094: }
095:
096: } catch (JDOMException e) {
097: resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
098: resp.setContentType(TEXT_XML_UTF_8);
099: createErrorResult(WebdavStatus.SC_BAD_REQUEST, e
100: .getMessage());
101: throw new WebdavException(WebdavStatus.SC_BAD_REQUEST);
102: }
103: }
104:
105: protected void executeRequest() throws WebdavException, IOException {
106:
107: Element rootResponseElement = new Element(RESPONSE_EL, HIPPO_NS);
108: Document responseDoc = new Document(rootResponseElement);
109:
110: // Prevent dirty reads
111: slideToken.setForceStoreEnlistment(true);
112:
113: Pattern pattern = Pattern.compile(request.toBeReplaced);
114:
115: Iterator resources = request.resources.iterator();
116: while (resources.hasNext()) {
117: String resourcePath = (String) resources.next();
118:
119: ReplaceResponse resp = null;
120: try {
121: NodeRevisionDescriptors revisionDescriptors = content
122: .retrieve(slideToken, resourcePath);
123:
124: if (revisionDescriptors.hasRevisions()) {
125: NodeRevisionDescriptor revisionDescriptor = content
126: .retrieve(slideToken, revisionDescriptors);
127:
128: if (revisionDescriptor != null
129: && !revisionDescriptor
130: .propertyValueContains(
131: NodeRevisionDescriptor.RESOURCE_TYPE,
132: NodeRevisionDescriptor.COLLECTION_TYPE)) {
133:
134: if (revisionDescriptor.getContentType()
135: .startsWith("text/")) {
136:
137: String original = getString(content
138: .retrieve(slideToken,
139: revisionDescriptors,
140: revisionDescriptor)
141: .streamContent(), DEFAULT_ENCODING);
142: Matcher match = pattern.matcher(original);
143:
144: if (match.find()) {
145:
146: revisionDescriptor.setContentLength(-1);
147:
148: byte[] replaced = match.replaceAll(
149: request.replacedWith).getBytes(
150: DEFAULT_ENCODING);
151:
152: InputStream result = new ByteArrayInputStream(
153: replaced);
154:
155: NodeRevisionContent revisionContent = new NodeRevisionContent();
156:
157: revisionContent.setContent(result);
158:
159: revisionDescriptor
160: .setContentLength(replaced.length);
161:
162: // Last modification date
163: revisionDescriptor
164: .setLastModified(new Date());
165:
166: // Etag generation
167: revisionDescriptor
168: .setETag(PropertyHelper
169: .computeEtag(
170: resourcePath,
171: revisionDescriptor));
172: revisionDescriptor.setResourceType("");
173:
174: content.store(slideToken, resourcePath,
175: revisionDescriptor,
176: revisionContent);
177:
178: // add it to the indexer queue explicitly
179: Uri uri = token.getUri(slideToken,
180: resourcePath);
181: Store store = uri.getStore();
182: if (store instanceof ExtendedStore) {
183: if (((ExtendedStore) store)
184: .getContentIndexer() != null) {
185: ((ExtendedStore) store)
186: .getContentIndexer()
187: .updateIndex(uri, null,
188: null);
189: }
190: }
191:
192: resp = new ReplaceResponse(
193: resourcePath,
194: WebdavStatus.SC_OK, null);
195: } else {
196: resp = new ReplaceResponse(
197: resourcePath,
198: WebdavStatus.SC_OK,
199: "Nothing to replace.");
200: }
201:
202: } else {
203: resp = new ReplaceResponse(
204: resourcePath,
205: WebdavStatus.SC_UNSUPPORTED_MEDIA_TYPE,
206: "Content has to be of plain text type.");
207: }
208:
209: } else {
210: resp = new ReplaceResponse(resourcePath,
211: WebdavStatus.SC_METHOD_NOT_ALLOWED,
212: "Must not be a collection.");
213: }
214: } else {
215: resp = new ReplaceResponse(resourcePath,
216: WebdavStatus.SC_METHOD_NOT_ALLOWED,
217: "Must not be a collection.");
218: }
219:
220: } catch (Exception e) {
221: int statusCode = getErrorCode(e);
222: resp = new ReplaceResponse(resourcePath, statusCode, e
223: .getMessage());
224: }
225:
226: if (resp != null) {
227: rootResponseElement.addContent(resp.getElement());
228: }
229: }
230:
231: resp.setStatus(WebdavStatus.SC_OK);
232: resp.setContentType(TEXT_XML_UTF_8);
233:
234: try {
235: sendResult(responseDoc);
236: } catch (Exception e) {
237: System.err.println(e.getMessage());
238: e.printStackTrace();
239: resp.setStatus(getErrorCode(e)); // no special handling needed
240: throw new WebdavException(WebdavStatus.SC_ACCEPTED, false); // abort the TA
241: }
242: }
243:
244: private String getString(InputStream stream, String encoding)
245: throws Exception {
246: char[] buf = new char[1024];
247: int read = -1;
248:
249: Reader in = new InputStreamReader(new BufferedInputStream(
250: stream), encoding);
251: StringBuffer str = new StringBuffer();
252:
253: while ((read = in.read(buf)) > -1) {
254: str.append(buf, 0, read);
255: }
256:
257: return str.toString();
258: }
259:
260: private Element getQueryElement() throws WebdavException,
261: JDOMException {
262: try {
263: // parse request body with given char encoding! (for UTF-8)
264: Document document = new SAXBuilder()
265: .build(new InputStreamReader(req.getInputStream(),
266: req.getCharacterEncoding()));
267: return document.getRootElement();
268: } catch (IOException e) {
269: System.err.println(e.getMessage());
270: e.printStackTrace();
271: resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
272: throw new WebdavException(
273: WebdavStatus.SC_INTERNAL_SERVER_ERROR);
274: }
275: }
276:
277: private void sendResult(Document responseDoc) throws JDOMException,
278: IOException {
279: Format format = Format.getPrettyFormat();
280: format.setIndent(XML_RESPONSE_INDENT);
281: XMLOutputter xmlWriter = new XMLOutputter(format);
282: xmlWriter.output(responseDoc, resp.getWriter());
283: }
284:
285: private void createErrorResult(int status, String message)
286: throws WebdavException {
287: Element rootElement = new Element(E_ERROR, DNSP);
288:
289: Document errorDocument = new Document(rootElement);
290:
291: Element statusElement = new org.jdom.Element(E_STATUS, DNSP);
292: statusElement.addContent(getStatusText(status));
293: rootElement.addContent(statusElement);
294:
295: Element messageElement = new org.jdom.Element(
296: E_RESPONSEDESCRIPTION, DNSP);
297: messageElement.addContent(message);
298: rootElement.addContent(messageElement);
299:
300: try {
301: sendResult(errorDocument);
302: } catch (Exception e) {
303: System.err.println(e.getMessage());
304: e.printStackTrace();
305: resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
306: throw new WebdavException(
307: WebdavStatus.SC_INTERNAL_SERVER_ERROR);
308: }
309: }
310:
311: private static String getStatusText(int status) {
312: return "HTTP/1.1 " + status + " "
313: + WebdavStatus.getStatusText(status);
314: }
315:
316: // icky, but oh well.
317: protected int getErrorCode(Exception ex) {
318: try {
319: throw ex;
320: } catch (RevisionNotFoundException e) {
321: return WebdavStatus.SC_NOT_FOUND;
322: } catch (RevisionContentNotFoundException e) {
323: return WebdavStatus.SC_NOT_FOUND;
324: } catch (RevisionDescriptorNotFoundException e) {
325: return WebdavStatus.SC_NOT_FOUND;
326: } catch (LinkedObjectNotFoundException e) {
327: return WebdavStatus.SC_NOT_FOUND;
328: } catch (Exception e) {
329: return super .getErrorCode(e);
330: }
331: }
332:
333: // response for ONE replace operation on ONE resource
334: protected static class ReplaceResponse {
335: private int status = 200;
336: private String message = null;
337: private String href = null;
338:
339: public ReplaceResponse(String uri, int status) {
340: this (uri, status, null);
341: }
342:
343: public ReplaceResponse(String uri, int status, String message) {
344: href = uri;
345: this .status = status;
346: this .message = message;
347: }
348:
349: public Element getElement() {
350: Element el = new Element(RESOURCE_EL, HIPPO_NS);
351: Element status = new Element(E_STATUS, HIPPO_NS);
352: Element href = new Element(E_HREF, HIPPO_NS);
353:
354: status.addContent(getStatusText(this .status));
355: href.addContent(this .href);
356:
357: el.addContent(href);
358: el.addContent(status);
359:
360: if (message != null && !message.equals("")) {
361: Element message = new Element(E_ERROR, HIPPO_NS);
362: message.addContent(this .message);
363: el.addContent(message);
364: }
365:
366: return el;
367: }
368: }
369:
370: protected static class ReplaceRequest {
371: String toBeReplaced = null;
372: String replacedWith = null;
373:
374: Vector resources = new Vector();
375:
376: public ReplaceRequest(Element requestEl) {
377: toBeReplaced = requestEl.getChild(REPLACE_EL, HIPPO_NS)
378: .getText();
379: replacedWith = requestEl.getChild(WITH_EL, HIPPO_NS)
380: .getText();
381:
382: Element in = requestEl.getChild(IN_EL, HIPPO_NS);
383: if (in == null)
384: throw new RuntimeException(
385: "Couldn't find the <hippo:in> element in the request!");
386:
387: List resourceList = in.getChildren(RESOURCE_EL, HIPPO_NS);
388: Iterator it = resourceList.iterator();
389: while (it.hasNext()) {
390: Element res = (Element) it.next();
391: resources.add(res.getText());
392: }
393: }
394: }
395: }
|