Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 27896 invoked from network); 12 Jul 2006 09:38:53 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 12 Jul 2006 09:38:53 -0000 Received: (qmail 93654 invoked by uid 500); 12 Jul 2006 09:38:53 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 93628 invoked by uid 500); 12 Jul 2006 09:38:53 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 93607 invoked by uid 99); 12 Jul 2006 09:38:53 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2006 02:38:53 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2006 02:38:50 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 11C471A981D; Wed, 12 Jul 2006 02:38:30 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r421206 - in /jackrabbit/trunk/jcr-server: client/src/java/org/apache/jackrabbit/webdav/client/methods/ server/src/java/org/apache/jackrabbit/webdav/jcr/observation/ webdav/src/java/org/apache/jackrabbit/webdav/ webdav/src/java/org/apache/j... Date: Wed, 12 Jul 2006 09:38:26 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060712093830.11C471A981D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: angela Date: Wed Jul 12 02:38:25 2006 New Revision: 421206 URL: http://svn.apache.org/viewvc?rev=421206&view=rev Log: - add methods for observation to client - minor improvements to observation impl in jcr-server - fix client LockMethod and adjust LockDiscovery accordingly - fix client MergeMethod adjust corresponding Info object accordingly - improve UpdateInfo - use CodedURLHeader instaed of building locktoken header manually Added: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java (with props) jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java (with props) jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java (with props) jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java (with props) Modified: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/DavMethodBase.java jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/LockMethod.java jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/MergeMethod.java jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavResponseImpl.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/TimeoutHeader.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/lock/LockDiscovery.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/EventDiscovery.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionDiscovery.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionInfo.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/MergeInfo.java jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/UpdateInfo.java Modified: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/DavMethodBase.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/DavMethodBase.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/DavMethodBase.java (original) +++ jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/DavMethodBase.java Wed Jul 12 02:38:25 2006 @@ -250,9 +250,7 @@ protected void processStatusLine(HttpState httpState, HttpConnection httpConnection) { super.processStatusLine(httpState, httpConnection); int code = getStatusCode(); - // default - success = code < DavServletResponse.SC_BAD_REQUEST; - // sub classes overwrites + // sub classes define if status code indicates success. success = isSuccess(code); } @@ -265,7 +263,7 @@ * @param httpConnection * @see HttpMethodBase#processResponseBody(HttpState, HttpConnection) */ - protected final void processResponseBody(HttpState httpState, HttpConnection httpConnection) { + protected void processResponseBody(HttpState httpState, HttpConnection httpConnection) { // in case of multi-status response if (getStatusCode() == DavServletResponse.SC_MULTI_STATUS) { try { Modified: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/LockMethod.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/LockMethod.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/LockMethod.java (original) +++ jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/LockMethod.java Wed Jul 12 02:38:25 2006 @@ -18,17 +18,24 @@ import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; -import org.apache.jackrabbit.webdav.header.CodedUrlHeader; import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.xml.DomUtil; import org.apache.jackrabbit.webdav.header.DepthHeader; import org.apache.jackrabbit.webdav.header.IfHeader; import org.apache.jackrabbit.webdav.header.TimeoutHeader; -import org.apache.jackrabbit.webdav.lock.ActiveLock; +import org.apache.jackrabbit.webdav.header.CodedUrlHeader; import org.apache.jackrabbit.webdav.lock.LockInfo; import org.apache.jackrabbit.webdav.lock.Scope; import org.apache.jackrabbit.webdav.lock.Type; +import org.apache.jackrabbit.webdav.lock.LockDiscovery; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.HttpState; +import org.apache.commons.httpclient.HttpConnection; +import org.apache.commons.httpclient.Header; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; import java.io.IOException; @@ -39,6 +46,9 @@ private static Logger log = LoggerFactory.getLogger(LockMethod.class); + private final boolean isRefresh; + private LockDiscovery lockDiscovery; + /** * Creates a new LockMethod. * @@ -62,20 +72,21 @@ */ public LockMethod(String uri, LockInfo lockInfo) throws IOException { super(uri); - if (lockInfo != null) { + if (lockInfo != null && !lockInfo.isRefreshLock()) { TimeoutHeader th = new TimeoutHeader(lockInfo.getTimeout()); setRequestHeader(th); - if (!lockInfo.isRefreshLock()) { - DepthHeader dh = new DepthHeader(lockInfo.isDeep()); - setRequestHeader(dh); - setRequestHeader(DavConstants.HEADER_CONTENT_TYPE, "text/xml; charset=UTF-8"); - setRequestBody(lockInfo); - } + DepthHeader dh = new DepthHeader(lockInfo.isDeep()); + setRequestHeader(dh); + setRequestHeader(DavConstants.HEADER_CONTENT_TYPE, "text/xml; charset=UTF-8"); + setRequestBody(lockInfo); + isRefresh = false; + } else { + throw new IllegalArgumentException("Cannot create a LOCK request without lock info. Use the constructor taking lock tokens in order to build a LOCK request for refresh."); } } /** - * Create a new 'Refresh' lock method. + * Create a new Lock method used to 'REFRESH' an existing lock. * * @param uri * @param timeout @@ -88,18 +99,48 @@ setRequestHeader(th); IfHeader ifh = new IfHeader(lockTokens); setRequestHeader(ifh); + isRefresh = true; } - public ActiveLock getResponseAsLock() throws IOException { + /** + * + * @return + * @throws IOException + * @throws DavException + */ + public LockDiscovery getResponseAsLockDiscovery() throws IOException, DavException { checkUsed(); - // todo -> build lockdiscovery-prop -> retrieve activelock - return null; + // lockDiscovery has been build while processing the response body. + // if its still null, this indicates that either the method failed + // or that the response body could not be parsed. + // in either case this is an error and will be reported to the caller. + if (lockDiscovery != null) { + return lockDiscovery; + } else { + DavException dx = getResponseException(); + if (dx != null) { + throw dx; + } else { + throw new DavException(getStatusCode(), getName() + " resulted with unexpected status: " + getStatusLine()); + } + } } + /** + * + * @return + */ public String getLockToken() { checkUsed(); - CodedUrlHeader cuh = new CodedUrlHeader(DavConstants.HEADER_LOCK_TOKEN, getResponseHeader(DavConstants.HEADER_LOCK_TOKEN).getValue()); - return cuh.getCodedUrl(); + Header ltHeader = getResponseHeader(DavConstants.HEADER_LOCK_TOKEN); + if (ltHeader != null) { + CodedUrlHeader cuh = new CodedUrlHeader(DavConstants.HEADER_LOCK_TOKEN, ltHeader.getValue()); + return cuh.getCodedUrl(); + } else { + // not Lock-Token header must be sent in response to a 'refresh'. + // see the validation performed while processing the response. + return null; + } } //---------------------------------------------------------< HttpMethod >--- @@ -110,6 +151,17 @@ return DavMethods.METHOD_LOCK; } + //----------------------------------------------------------< DavMethod >--- + /** + * @return true, if the status code indicates success and if the response + * contains a Lock-Token header for a request used to create a new lock. + */ + public boolean succeeded() { + checkUsed(); + String lt = getLockToken(); + boolean containsRequiredHeader = (isRefresh) ? lt == null : lt != null; + return getSuccess() && containsRequiredHeader; + } //------------------------------------------------------< DavMethodBase >--- /** * @@ -118,5 +170,55 @@ */ protected boolean isSuccess(int statusCode) { return statusCode == DavServletResponse.SC_OK; + } + + //-----------------------------------------------------< HttpMethodBase >--- + /** + * Retrieves the DAV:lockdiscovery property present in the response body + * and builds 'ActiveLock' objects from the corresponding DAV:activelock + * child elements inside the lock discovery. If parsing the response body + * fails for whatever reason or if the DAV:lockdiscovery did not contain + * at least a single DAV:activelock entry (the one created by the LOCK + * call) this methods in addition resets the 'success' flag to false. + * + * @param httpState + * @param httpConnection + * @see HttpMethodBase#processResponseBody(HttpState, HttpConnection) + */ + protected void processResponseBody(HttpState httpState, HttpConnection httpConnection) { + // in case of successful response code -> parse xml body into lockDiscovery. + if (getSuccess()) { + try { + setSuccess(buildDiscoveryFromRoot(getRootElement())); + } catch (IOException e) { + log.error("Error while parsing multistatus response: " + e); + setSuccess(false); + } + } + } + + //------------------------------------------------------------< private >--- + /** + * Builds a new LockDiscovery object from the root XML + * present in the response body and returns true if the object could be + * created successfully. + * + * @param root + * @return if a LockDiscovery object could be created from the + * given XML, false otherwise. + */ + private boolean buildDiscoveryFromRoot(Element root) { + if (DomUtil.matches(root, XML_PROP, NAMESPACE) && DomUtil.hasChildElement(root, PROPERTY_LOCKDISCOVERY, NAMESPACE)) { + Element lde = DomUtil.getChildElement(root, PROPERTY_LOCKDISCOVERY, NAMESPACE); + if (DomUtil.hasChildElement(lde, XML_ACTIVELOCK, NAMESPACE)) { + lockDiscovery = LockDiscovery.createFromXml(lde); + return true; + } else { + log.debug("The DAV:lockdiscovery must contain a least a single DAV:activelock in response to a successful LOCK request."); + } + } else { + log.debug("Missing DAV:prop response body in LOCK method."); + } + return false; } } Modified: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/MergeMethod.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/MergeMethod.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/MergeMethod.java (original) +++ jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/MergeMethod.java Wed Jul 12 02:38:25 2006 @@ -51,6 +51,7 @@ * @return true if status code is {@link DavServletResponse#SC_MULTI_STATUS 207 (Multi-Status)}. */ protected boolean isSuccess(int statusCode) { + // TODO: is this correct? return statusCode == DavServletResponse.SC_MULTI_STATUS; } } Added: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java?rev=421206&view=auto ============================================================================== --- jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java (added) +++ jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java Wed Jul 12 02:38:25 2006 @@ -0,0 +1,127 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. The ASF licenses this file to You +* under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.jackrabbit.webdav.client.methods; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.jackrabbit.webdav.DavMethods; +import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.DavConstants; +import org.apache.jackrabbit.webdav.xml.DomUtil; +import org.apache.jackrabbit.webdav.xml.ElementIterator; +import org.apache.jackrabbit.webdav.observation.ObservationConstants; +import org.apache.jackrabbit.webdav.observation.EventDiscovery; +import org.apache.jackrabbit.webdav.observation.EventBundle; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.HttpState; +import org.apache.commons.httpclient.HttpConnection; +import org.w3c.dom.Element; +import org.w3c.dom.Document; + +import java.io.IOException; + +/** + * PollMethod imple + */ +public class PollMethod extends DavMethodBase implements ObservationConstants { + + private static Logger log = LoggerFactory.getLogger(PollMethod.class); + + private EventDiscovery eventDiscovery; + + public PollMethod(String uri, String subscriptionId) { + super(uri); + setRequestHeader(ObservationConstants.HEADER_SUBSCRIPTIONID, subscriptionId); + } + + public EventDiscovery getResponseAsEventDiscovery() throws IOException, DavException { + checkUsed(); + if (eventDiscovery != null) { + return eventDiscovery; + } else { + DavException dx = getResponseException(); + if (dx != null) { + throw dx; + } else { + throw new DavException(getStatusCode(), getName() + " resulted with unexpected status: " + getStatusLine()); + } + } + } + //---------------------------------------------------------< HttpMethod >--- + public String getName() { + return DavMethods.METHOD_POLL; + } + + //------------------------------------------------------< DavMethodBase >--- + protected boolean isSuccess(int statusCode) { + return DavServletResponse.SC_OK == statusCode; + } + + //------------------------------------------------------< HttpMethodBase >--- + /** + * Retrieves the DAV:subscriptiondiscovery property present in the response body + * and builds 'Subscription' objects from the corresponding DAV:subscription + * child elements inside the discovery. If parsing the response body + * fails for whatever reason or if the DAV:subscriptiondiscovery did not contain + * at least a single DAV:subscription entry (the one created by the SUBSCRIBE + * call) this methods in addition resets the 'success' flag to false. + * + * @param httpState + * @param httpConnection + * @see HttpMethodBase#processResponseBody(HttpState, HttpConnection) + */ + protected void processResponseBody(HttpState httpState, HttpConnection httpConnection) { + // in case of successful response code -> parse xml body discovery object + if (getSuccess()) { + try { + setSuccess(buildDiscoveryFromRoot(getRootElement())); + } catch (IOException e) { + log.error("Error while parsing multistatus response: " + e); + setSuccess(false); + } + } + } + + //------------------------------------------------------------< private >--- + /** + * + * @param root + * @return + */ + private boolean buildDiscoveryFromRoot(Element root) { + if (DomUtil.matches(root, XML_PROP, DavConstants.NAMESPACE) && + DomUtil.hasChildElement(root, XML_EVENTDISCOVERY, ObservationConstants.NAMESPACE)) { + Element ld = DomUtil.getChildElement(root, XML_EVENTDISCOVERY, ObservationConstants.NAMESPACE); + eventDiscovery = new EventDiscovery(); + + ElementIterator it = DomUtil.getChildren(ld, XML_SUBSCRIPTION, ObservationConstants.NAMESPACE); + while (it.hasNext()) { + final Element ebElement = it.nextElement(); + EventBundle eb = new EventBundle() { + public Element toXml(Document document) { + return (Element) document.importNode(ebElement, true); + } + }; + eventDiscovery.addEventBundle(eb); + } + return true; + } else { + log.debug("Missing DAV:prop response body in SUBSCRIBE method."); + } + return false; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/PollMethod.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java?rev=421206&view=auto ============================================================================== --- jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java (added) +++ jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java Wed Jul 12 02:38:25 2006 @@ -0,0 +1,180 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. The ASF licenses this file to You +* under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.jackrabbit.webdav.client.methods; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.jackrabbit.webdav.DavMethods; +import org.apache.jackrabbit.webdav.DavConstants; +import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.xml.DomUtil; +import org.apache.jackrabbit.webdav.header.CodedUrlHeader; +import org.apache.jackrabbit.webdav.header.DepthHeader; +import org.apache.jackrabbit.webdav.header.TimeoutHeader; +import org.apache.jackrabbit.webdav.observation.SubscriptionInfo; +import org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery; +import org.apache.jackrabbit.webdav.observation.ObservationConstants; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.HttpState; +import org.apache.commons.httpclient.HttpConnection; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.util.List; + +/** + * SubscribeMethod... + */ +public class SubscribeMethod extends DavMethodBase implements ObservationConstants { + + private static Logger log = LoggerFactory.getLogger(SubscribeMethod.class); + + private SubscriptionDiscovery subscriptionDiscovery; + + /** + * Create a new SubscribeMethod used to register to the + * observation events specified within the given SubscriptionInfo. + * See {@link #SubscribeMethod(String, SubscriptionInfo, String) for a + * constructor that allows to redefined an existing subscription. + * + * @param uri + * @param subscriptionInfo + * @throws IOException + */ + public SubscribeMethod(String uri, SubscriptionInfo subscriptionInfo) throws IOException { + this(uri, subscriptionInfo, null); + } + + /** + * Create a new SubscribeMethod used to register to the + * observation events specified within the given SubscriptionInfo. + * Note that in contrast to {@link #SubscribeMethod(String, SubscriptionInfo)} + * this constructor optionally takes a subscription id identifying a + * subscription made before. In this case the subscription will be modified + * according to the definitions present in the SubscriptionInfo. + * If the id is null this constructor is identical to + * {@link #SubscribeMethod(String, SubscriptionInfo)}. + * + * @param uri + * @param subscriptionInfo + * @throws IOException + */ + public SubscribeMethod(String uri, SubscriptionInfo subscriptionInfo, String subscriptionId) throws IOException { + super(uri); + if (subscriptionInfo == null) { + throw new IllegalArgumentException("SubscriptionInfo must not be null."); + } + // optional subscriptionId (only required to modify an existing subscription) + if (subscriptionId != null) { + setRequestHeader(new CodedUrlHeader(HEADER_SUBSCRIPTIONID, subscriptionId)); + } + // optional timeout header + long to = subscriptionInfo.getTimeOut(); + if (to != DavConstants.UNDEFINED_TIMEOUT) { + setRequestHeader(new TimeoutHeader(subscriptionInfo.getTimeOut())); + } + // always set depth header since value is boolean flag + setRequestHeader(new DepthHeader(subscriptionInfo.isDeep())); + + setRequestHeader(DavConstants.HEADER_CONTENT_TYPE, "text/xml; charset=UTF-8"); + setRequestBody(subscriptionInfo); + } + + public SubscriptionDiscovery getResponseAsSubscriptionDiscovery() throws IOException, DavException { + checkUsed(); + if (subscriptionDiscovery != null) { + return subscriptionDiscovery; + } else { + DavException dx = getResponseException(); + if (dx != null) { + throw dx; + } else { + throw new DavException(getStatusCode(), getName() + " resulted with unexpected status: " + getStatusLine()); + } + } + } + + public String getSubscriptionId() { + checkUsed(); + Header sbHeader = getResponseHeader(HEADER_SUBSCRIPTIONID); + if (sbHeader != null) { + CodedUrlHeader cuh = new CodedUrlHeader(HEADER_SUBSCRIPTIONID, sbHeader.getValue()); + return cuh.getCodedUrl(); + } + return null; + } + + //---------------------------------------------------------< HttpMethod >--- + public String getName() { + return DavMethods.METHOD_SUBSCRIBE; + } + + //------------------------------------------------------< DavMethodBase >--- + protected boolean isSuccess(int statusCode) { + return DavServletResponse.SC_OK == statusCode; + } + + //------------------------------------------------------< HttpMethodBase >--- + /** + * Retrieves the DAV:subscriptiondiscovery property present in the response body + * and builds 'Subscription' objects from the corresponding DAV:subscription + * child elements inside the discovery. If parsing the response body + * fails for whatever reason or if the DAV:subscriptiondiscovery did not contain + * at least a single DAV:subscription entry (the one created by the SUBSCRIBE + * call) this methods in addition resets the 'success' flag to false. + * + * @param httpState + * @param httpConnection + * @see HttpMethodBase#processResponseBody(HttpState, HttpConnection) + */ + protected void processResponseBody(HttpState httpState, HttpConnection httpConnection) { + // in case of successful response code -> parse xml body discovery object + if (getSuccess()) { + try { + setSuccess(buildDiscoveryFromRoot(getRootElement())); + } catch (IOException e) { + log.error("Error while parsing multistatus response: " + e); + setSuccess(false); + } + } + } + + //------------------------------------------------------------< private >--- + /** + * + * @param root + * @return + */ + private boolean buildDiscoveryFromRoot(Element root) { + if (DomUtil.matches(root, XML_PROP, DavConstants.NAMESPACE) && + DomUtil.hasChildElement(root, SUBSCRIPTIONDISCOVERY.getName(), SUBSCRIPTIONDISCOVERY.getNamespace())) { + Element sdElem = DomUtil.getChildElement(root, SUBSCRIPTIONDISCOVERY.getName(), SUBSCRIPTIONDISCOVERY.getNamespace()); + + SubscriptionDiscovery sd = SubscriptionDiscovery.createFromXml(sdElem); + if (!((List)sd.getValue()).isEmpty()) { + subscriptionDiscovery = sd; + return true; + } else { + log.debug("Missing 'subscription' elements in SUBSCRIBE response body. At least a single subscription must be present if SUBSCRIBE was successful."); + } + } else { + log.debug("Missing DAV:prop response body in SUBSCRIBE method."); + } + return false; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/SubscribeMethod.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java?rev=421206&view=auto ============================================================================== --- jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java (added) +++ jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java Wed Jul 12 02:38:25 2006 @@ -0,0 +1,45 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. The ASF licenses this file to You +* under the Apache License, Version 2.0 (the "License"); you may not +* use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.jackrabbit.webdav.client.methods; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.jackrabbit.webdav.DavMethods; +import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.observation.ObservationConstants; + +/** + * UnSubscribeMethod... + */ +public class UnSubscribeMethod extends DavMethodBase { + + private static Logger log = LoggerFactory.getLogger(UnSubscribeMethod.class); + + public UnSubscribeMethod(String uri, String subscriptionId) { + super(uri); + setRequestHeader(ObservationConstants.HEADER_SUBSCRIPTIONID, subscriptionId); + } + + //---------------------------------------------------------< HttpMethod >--- + public String getName() { + return DavMethods.METHOD_UNSUBSCRIBE; + } + + //------------------------------------------------------< DavMethodBase >--- + protected boolean isSuccess(int statusCode) { + return DavServletResponse.SC_NO_CONTENT == statusCode; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jcr-server/client/src/java/org/apache/jackrabbit/webdav/client/methods/UnSubscribeMethod.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Modified: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java (original) +++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java Wed Jul 12 02:38:25 2006 @@ -28,6 +28,7 @@ import org.apache.jackrabbit.webdav.observation.ObservationResource; import org.apache.jackrabbit.webdav.observation.Subscription; import org.apache.jackrabbit.webdav.observation.SubscriptionInfo; +import org.apache.jackrabbit.webdav.observation.DefaultEventType; import org.apache.jackrabbit.webdav.xml.DomUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,6 +105,7 @@ locator = resource.getLocator(); } + //-------------------------------------------------------< Subscription >--- /** * Returns the id of this subscription. * @@ -113,6 +115,7 @@ return subscriptionId; } + //----------------------------------------------------< XmlSerializable >--- /** * Return the Xml representation of this Subscription as required * for the {@link org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery} @@ -130,8 +133,10 @@ subscr.appendChild(DomUtil.depthToXml(info.isDeep(), document)); subscr.appendChild(DomUtil.timeoutToXml(info.getTimeOut(), document)); - Element id = DomUtil.addChildElement(subscr, XML_SUBSCRIPTIONID, NAMESPACE); - id.appendChild(DomUtil.hrefToXml(subscriptionId, document)); + if (getSubscriptionId() != null) { + Element id = DomUtil.addChildElement(subscr, XML_SUBSCRIPTIONID, NAMESPACE); + id.appendChild(DomUtil.hrefToXml(getSubscriptionId(), document)); + } return subscr; } @@ -155,11 +160,11 @@ * @return JCR compliant integer representation of the event types defined * for this {@link SubscriptionInfo}. */ - int getEventTypes() throws DavException { + int getJcrEventTypes() throws DavException { EventType[] eventTypes = info.getEventTypes(); int events = 0; for (int i = 0; i < eventTypes.length; i++) { - events |= getEventType(eventTypes[i].getName()); + events |= getJcrEventType(eventTypes[i]); } return events; } @@ -223,7 +228,7 @@ * resource. */ boolean isSubscribedToResource(ObservationResource resource) { - return locator.equals(resource.getLocator()); + return locator.getResourcePath().equals(resource.getResourcePath()); } /** @@ -274,47 +279,71 @@ //-------------------------------------------------------------------------- /** - * Static utility method in order to convert the types defined by - * {@link javax.jcr.observation.Event} into their Xml representation. - * - * @param eventType The jcr event type - * @return Xml representation of the event type. - */ - private static String getEventName(int eventType) { - String eventName; - switch (eventType) { + * Static utility method to convert the type defined by a + * {@link javax.jcr.observation.Event JCR event} into an EventType + * object. + * + * @param jcrEventType + * @return EventType representation of the given JCR event type. + * @throws IllegalArgumentException if the given int does not represent a + * valid type constants as defined by {@link Event}.
+ * Valid values are + *
    + *
  • {@link Event#NODE_ADDED}
  • + *
  • {@link Event#NODE_REMOVED}
  • + *
  • {@link Event#PROPERTY_ADDED}
  • + *
  • {@link Event#PROPERTY_REMOVED}
  • + *
  • {@link Event#PROPERTY_CHANGED}
  • + *
+ */ + public static EventType getEventType(int jcrEventType) { + String localName; + switch (jcrEventType) { case Event.NODE_ADDED: - eventName = EVENT_NODEADDED; + localName = EVENT_NODEADDED; break; case Event.NODE_REMOVED: - eventName = EVENT_NODEREMOVED; + localName = EVENT_NODEREMOVED; break; case Event.PROPERTY_ADDED: - eventName = EVENT_PROPERTYADDED; + localName = EVENT_PROPERTYADDED; break; case Event.PROPERTY_CHANGED: - eventName = EVENT_PROPERTYCHANGED; + localName = EVENT_PROPERTYCHANGED; break; - default: - //Event.PROPERTY_REMOVED: - eventName = EVENT_PROPERTYREMOVED; + case Event.PROPERTY_REMOVED: + localName = EVENT_PROPERTYREMOVED; break; + default: // no default + throw new IllegalArgumentException("Invalid JCR event type: " + jcrEventType); } - return eventName; + return DefaultEventType.create(localName, NAMESPACE); } /** - * Static utility method to convert an event type name as present in the - * Xml request body into the corresponding constant defined by + * Static utility method to convert an EventType as present in + * the Xml body into the corresponding JCR event constant defined by * {@link javax.jcr.observation.Event}. * - * @param eventName - * @return event type as defined by {@link javax.jcr.observation.Event}. - * @throws DavException if the given element cannot be translated - * to any of the events defined by {@link javax.jcr.observation.Event}. - */ - private static int getEventType(String eventName) throws DavException { + * @param eventType + * @return Any of the event types defined by {@link Event}.
+ * Possible values are + *
    + *
  • {@link Event#NODE_ADDED}
  • + *
  • {@link Event#NODE_REMOVED}
  • + *
  • {@link Event#PROPERTY_ADDED}
  • + *
  • {@link Event#PROPERTY_REMOVED}
  • + *
  • {@link Event#PROPERTY_CHANGED}
  • + *
+ * @throws DavException if the given event type does not define a valid + * JCR event type, such as returned by {@link #getEventType(int)}. + */ + public static int getJcrEventType(EventType eventType) throws DavException { + if (eventType == null || !NAMESPACE.equals(eventType.getNamespace())) { + throw new DavException(DavServletResponse.SC_UNPROCESSABLE_ENTITY, "Invalid JCR event type: "+ eventType + ": Namespace mismatch."); + } int eType; + String eventName = eventType.getName(); if (EVENT_NODEADDED.equals(eventName)) { eType = Event.NODE_ADDED; } else if (EVENT_NODEREMOVED.equals(eventName)) { @@ -364,12 +393,11 @@ eventElem.appendChild(DomUtil.hrefToXml(eHref, document)); // eventtype Element eType = DomUtil.addChildElement(eventElem, XML_EVENTTYPE, NAMESPACE); - DomUtil.addChildElement(eType, getEventName(event.getType()), NAMESPACE); + eType.appendChild(getEventType(event.getType()).toXml(document)); // user id DomUtil.addChildElement(eventElem, XML_EVENTUSERID, NAMESPACE, event.getUserID()); } return bundle; } - } -} \ No newline at end of file +} Modified: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java (original) +++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java Wed Jul 12 02:38:25 2006 @@ -29,6 +29,8 @@ import org.apache.jackrabbit.webdav.observation.SubscriptionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.w3c.dom.Document; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -54,14 +56,16 @@ private final SubscriptionMap subscriptions = new SubscriptionMap(); /** - * Retrieve the {@link org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery} object for the given - * resource. Note, that the discovery object will be empty if there are - * no subscriptions present. + * Retrieve the {@link org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery} + * object for the given resource. Note, that the discovery object will be empty + * if there are no subscriptions present.
+ * Note that all subscriptions present on the given resource are returned. + * However, the subscription id will not be visible in order to avoid abuse + * by clients not having registered the subscription originally. * * @param resource */ public SubscriptionDiscovery getSubscriptionDiscovery(ObservationResource resource) { - // todo: is it correct to return subscriptions made by another session? Subscription[] subsForResource = subscriptions.getByPath(resource.getLocator()); return new SubscriptionDiscovery(subsForResource); } @@ -80,20 +84,23 @@ ObservationResource resource) throws DavException { - SubscriptionImpl subscription; + Subscription subscription; if (subscriptionId == null) { // new subscription - subscription = new SubscriptionImpl(info, resource); - registerSubscription(subscription, resource); + SubscriptionImpl newSubs = new SubscriptionImpl(info, resource); + registerSubscription(newSubs, resource); // ajust references to this subscription - subscriptions.put(subscription.getSubscriptionId(), subscription); - resource.getSession().addReference(subscription.getSubscriptionId()); + subscriptions.put(newSubs.getSubscriptionId(), newSubs); + resource.getSession().addReference(newSubs.getSubscriptionId()); + subscription = newSubs; } else { // refresh/modify existing one - subscription = validate(subscriptionId, resource); - subscription.setInfo(info); - registerSubscription(subscription, resource); + SubscriptionImpl existing = validate(subscriptionId, resource); + existing.setInfo(info); + registerSubscription(existing, resource); + + subscription = new WrappedSubscription(existing); } return subscription; } @@ -112,7 +119,7 @@ Session session = getRepositorySession(resource); ObservationManager oMgr = session.getWorkspace().getObservationManager(); String itemPath = subscription.getLocator().getRepositoryPath(); - oMgr.addEventListener(subscription, subscription.getEventTypes(), + oMgr.addEventListener(subscription, subscription.getJcrEventTypes(), itemPath, subscription.isDeep(), subscription.getUuidFilters(), subscription.getNodetypeNameFilters(), @@ -220,6 +227,31 @@ return JcrDavSession.getRepositorySession(resource.getSession()); } + //----------------------------------------------< private inner classes >--- + /** + * Private inner class wrapping around an Subscription as + * present in the internal map. This allows to hide the subscription Id + * from other sessions, that did create the subscription. + */ + private class WrappedSubscription implements Subscription { + + private final Subscription delegatee; + + private WrappedSubscription(Subscription subsc) { + this.delegatee = subsc; + } + + public String getSubscriptionId() { + // always return null, since the subscription id must not be exposed + // but to the client, that created the subscription. + return null; + } + + public Element toXml(Document document) { + return delegatee.toXml(document); + } + } + /** * Private inner class SubscriptionMap that allows for quick * access by resource path as well as by subscription id. @@ -264,7 +296,8 @@ Subscription[] subsForResource = new Subscription[idSet.size()]; int i = 0; while (idIterator.hasNext()) { - subsForResource[i] = (Subscription) subscriptions.get(idIterator.next()); + SubscriptionImpl s = (SubscriptionImpl) subscriptions.get(idIterator.next()); + subsForResource[i] = new WrappedSubscription(s); } return subsForResource; } else { Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavResponseImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavResponseImpl.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavResponseImpl.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/WebdavResponseImpl.java Wed Jul 12 02:38:25 2006 @@ -21,9 +21,12 @@ import org.apache.jackrabbit.webdav.observation.EventDiscovery; import org.apache.jackrabbit.webdav.observation.Subscription; import org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery; +import org.apache.jackrabbit.webdav.observation.ObservationConstants; import org.apache.jackrabbit.webdav.property.DavPropertySet; import org.apache.jackrabbit.webdav.xml.XmlSerializable; import org.apache.jackrabbit.webdav.xml.DomUtil; +import org.apache.jackrabbit.webdav.header.CodedUrlHeader; +import org.apache.jackrabbit.webdav.header.Header; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.slf4j.Logger; @@ -111,8 +114,10 @@ * @see DavServletResponse#sendLockResponse(org.apache.jackrabbit.webdav.lock.ActiveLock) */ public void sendLockResponse(ActiveLock lock) throws IOException { - httpResponse.setHeader(DavConstants.HEADER_LOCK_TOKEN, "<" + lock.getToken() + ">"); - DavPropertySet propSet = new DavPropertySet(); + CodedUrlHeader ltHeader = new CodedUrlHeader(DavConstants.HEADER_LOCK_TOKEN, lock.getToken()); + httpResponse.setHeader(ltHeader.getHeaderName(), ltHeader.getHeaderValue()); + + DavPropertySet propSet = new DavPropertySet(); propSet.add(new LockDiscovery(lock)); sendXmlResponse(propSet, SC_OK); } @@ -172,7 +177,12 @@ * @see org.apache.jackrabbit.webdav.observation.ObservationDavServletResponse#sendSubscriptionResponse(org.apache.jackrabbit.webdav.observation.Subscription) */ public void sendSubscriptionResponse(Subscription subscription) throws IOException { - DavPropertySet propSet = new DavPropertySet(); + String id = subscription.getSubscriptionId(); + if (id != null) { + Header h = new CodedUrlHeader(ObservationConstants.HEADER_SUBSCRIPTIONID, id); + httpResponse.setHeader(h.getHeaderName(), h.getHeaderValue()); + } + DavPropertySet propSet = new DavPropertySet(); propSet.add(new SubscriptionDiscovery(subscription)); sendXmlResponse(propSet, SC_OK); } Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/TimeoutHeader.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/TimeoutHeader.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/TimeoutHeader.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/header/TimeoutHeader.java Wed Jul 12 02:38:25 2006 @@ -48,20 +48,34 @@ } /** - * Parse the request timeout header and convert the timeout value + * Parses the request timeout header and converts it into a new + * TimeoutHeader object.
The default value is used as + * fallback if the String is not parseable. + * + * @param request + * @param defaultValue + * @return a new TimeoutHeader object. + */ + public static TimeoutHeader parse(HttpServletRequest request, long defaultValue) { + String timeoutStr = request.getHeader(HEADER_TIMEOUT); + long timeout = parse(timeoutStr, defaultValue); + return new TimeoutHeader(timeout); + } + + /** + * Parses the given timeout String and converts the timeout value * into a long indicating the number of milliseconds until expiration time * is reached.
- * NOTE: If the requested timeout is 'infinite' {@link Long.MAX_VALUE} - * is returned. If the header is missing or is in an invalid format that - * cannot be parsed, the default value is returned. + * NOTE: If the timeout String equals to {@link #TIMEOUT_INFINITE 'infinite'} + * {@link Long.MAX_VALUE} is returned. If the Sting is invalid or is in an + * invalid format that cannot be parsed, the default value is returned. * - * @param request + * @param timeoutStr * @param defaultValue * @return long representing the timeout present in the header or the default * value if the header is missing or could not be parsed. */ - public static TimeoutHeader parse(HttpServletRequest request, long defaultValue) { - String timeoutStr = request.getHeader(HEADER_TIMEOUT); + public static long parse(String timeoutStr, long defaultValue) { long timeout = defaultValue; if (timeoutStr != null && timeoutStr.length() > 0) { int secondsInd = timeoutStr.indexOf("Second-"); @@ -81,6 +95,6 @@ timeout = INFINITE_TIMEOUT; } } - return new TimeoutHeader(timeout); + return timeout; } } Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/lock/LockDiscovery.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/lock/LockDiscovery.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/lock/LockDiscovery.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/lock/LockDiscovery.java Wed Jul 12 02:38:25 2006 @@ -18,6 +18,9 @@ import org.apache.jackrabbit.webdav.property.AbstractDavProperty; import org.apache.jackrabbit.webdav.property.DavPropertyName; +import org.apache.jackrabbit.webdav.xml.DomUtil; +import org.apache.jackrabbit.webdav.xml.ElementIterator; +import org.apache.jackrabbit.webdav.header.TimeoutHeader; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -104,4 +107,116 @@ return lockdiscovery; } + //---------------------------------------------------< factory from xml >--- + /** + * Builds a new LockDiscovery object from the given xml element. + * + * @param lockDiscoveryElement + * @return + * @throws IllegalArgumentException if the given xml element is not a + * DAV:lockdiscovery element. + */ + public static LockDiscovery createFromXml(Element lockDiscoveryElement) { + if (!DomUtil.matches(lockDiscoveryElement, PROPERTY_LOCKDISCOVERY, NAMESPACE)) { + throw new IllegalArgumentException("DAV:lockdiscovery element expected."); + } + + List activeLocks = new ArrayList(); + ElementIterator it = DomUtil.getChildren(lockDiscoveryElement, XML_ACTIVELOCK, NAMESPACE); + while (it.hasNext()) { + Element al = it.nextElement(); + activeLocks.add(new ALockImpl(al)); + } + + return new LockDiscovery((ActiveLock[]) activeLocks.toArray(new ActiveLock[activeLocks.size()])); + } + + //------< inner class >----------------------------------------------------- + /** + * Simple implementation of ActiveLock interface, that retrieves + * the values from the DAV:activelock XML element.
+ * Note, that all set-methods as well as {@link #isExpired()} are not + * implemented. + */ + private static class ALockImpl implements ActiveLock { + + private final Element alElement; + + private ALockImpl(Element alElement) { + if (!DomUtil.matches(alElement, XML_ACTIVELOCK, NAMESPACE)) { + throw new IllegalArgumentException("DAV:activelock element expected."); + } + this.alElement = alElement; + } + + public boolean isLockedByToken(String lockToken) { + String lt = getToken(); + if (lt == null) { + return false; + } else { + return lt.equals(lockToken); + } + } + + public boolean isExpired() { + throw new UnsupportedOperationException("Not implemented"); + } + + public String getToken() { + Element ltEl = DomUtil.getChildElement(alElement, XML_LOCKTOKEN, NAMESPACE); + if (ltEl != null) { + return DomUtil.getChildText(alElement, XML_HREF, NAMESPACE); + } + return null; + } + + public String getOwner() { + String owner = null; + Element ow = DomUtil.getChildElement(alElement, XML_OWNER, NAMESPACE); + if (ow != null) { + if (DomUtil.hasChildElement(ow, XML_HREF, NAMESPACE)) { + owner = DomUtil.getChildTextTrim(ow, XML_HREF, NAMESPACE); + } else { + owner = DomUtil.getTextTrim(ow); + } + } + return owner; + } + + public void setOwner(String owner) { + throw new UnsupportedOperationException("Not implemented"); + } + + public long getTimeout() { + // get timeout string. if no DAV:timeout element is present, + // 't' will be 'null' and the undefined timeout will be returned. + String t = DomUtil.getChildTextTrim(alElement, XML_TIMEOUT, NAMESPACE); + return TimeoutHeader.parse(t, UNDEFINED_TIMEOUT); + } + + public void setTimeout(long timeout) { + throw new UnsupportedOperationException("Not implemented"); + } + + public boolean isDeep() { + String depth = DomUtil.getChildTextTrim(alElement, XML_DEPTH, NAMESPACE); + return DEPTH_INFINITY_S.equalsIgnoreCase(depth); + } + + public void setIsDeep(boolean isDeep) { + throw new UnsupportedOperationException("Not implemented"); + } + + public Type getType() { + return Type.createFromXml(DomUtil.getChildElement(alElement, XML_LOCKTYPE, NAMESPACE)); + } + + public Scope getScope() { + return Scope.createFromXml(DomUtil.getChildElement(alElement, XML_LOCKSCOPE, NAMESPACE)); + } + + public Element toXml(Document document) { + return (Element) document.importNode(alElement, true); + } + } } Added: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java?rev=421206&view=auto ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java (added) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java Wed Jul 12 02:38:25 2006 @@ -0,0 +1,130 @@ +/* + * $URL$ + * $Id$ + * + * Copyright 1997-2005 Day Management AG + * Barfuesserplatz 6, 4001 Basel, Switzerland + * All Rights Reserved. + * + * This software is the confidential and proprietary information of + * Day Management AG, ("Confidential Information"). You shall not + * disclose such Confidential Information and shall use it only in + * accordance with the terms of the license agreement you entered into + * with Day. + */ +package org.apache.jackrabbit.webdav.observation; + +import org.apache.jackrabbit.webdav.xml.Namespace; +import org.apache.jackrabbit.webdav.xml.DomUtil; +import org.apache.jackrabbit.webdav.xml.XmlSerializable; +import org.apache.jackrabbit.webdav.xml.ElementIterator; +import org.w3c.dom.Element; +import org.w3c.dom.Document; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +/** + * DefaultEventType defines a simple EventType implementation that + * only consists of a qualified event name consisting of namespace plus local + * name. + */ +public class DefaultEventType implements EventType { + + private static final Map eventTypes = new HashMap(); + + private final String localName; + private final Namespace namespace; + + /** + * Avoid instanciation of DefaultEventType. Since the amount + * of available (and registered) events is considered to be limited, the + * static {@link #create(String, Namespace) method is defined. + * + * @param localName + * @param namespace + */ + private DefaultEventType(String localName, Namespace namespace) { + this.localName = localName; + this.namespace = namespace; + } + + /** + * Factory method to create a new EventType. + * + * @param localName + * @param namespace + * @return + */ + public static EventType create(String localName, Namespace namespace) { + if (localName == null || "".equals(localName)) { + throw new IllegalArgumentException("null and '' are not valid local names of an event type."); + } + String key = DomUtil.getQualifiedName(localName, namespace); + if (eventTypes.containsKey(key)) { + return (EventType) eventTypes.get(key); + } else { + EventType type = new DefaultEventType(localName, namespace); + eventTypes.put(key, type); + return type; + } + } + + /** + * Retrieves one or multiple EventTypes from the 'eventtype' + * Xml element. While a subscription may register multiple types (thus + * the 'eventtype' contains multiple child elements), a single event may only + * refer to one single type. + * + * @param eventType + * @return + */ + public static EventType[] createFromXml(Element eventType) { + if (!DomUtil.matches(eventType, ObservationConstants.XML_EVENTTYPE, ObservationConstants.NAMESPACE)) { + throw new IllegalArgumentException("'eventtype' element expected which contains a least a single child element."); + } + + List etypes = new ArrayList(); + ElementIterator it = DomUtil.getChildren(eventType); + while (it.hasNext()) { + Element el = it.nextElement(); + etypes.add(create(el.getLocalName(), DomUtil.getNamespace(el))); + } + return (EventType[]) etypes.toArray(new EventType[etypes.size()]); + } + + //----------------------------------------------------------< EventType >--- + /** + * @see EventType#getName() + */ + public String getName() { + return localName; + } + + /** + * @see EventType#getNamespace() + */ + public Namespace getNamespace() { + return namespace; + } + + //----------------------------------------------------< XmlSerializable >--- + /** + * Returns a single empty Xml element where namespace and local name of this + * event type define the elements name. + *
+     * EventType.create("someevent", Namespace.getNamespace("F", "http://www.foo.bar/eventtypes"));
+     *
+     * returns the following element upon call of toXml:
+     *
+     * <F:someevent xmlns:F="http://www.foo.bar/eventtypes" />
+     * 
+ * + * @see XmlSerializable#toXml(Document) + */ + public Element toXml(Document document) { + return DomUtil.createElement(document, localName, namespace); + } +} Propchange: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/DefaultEventType.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/EventDiscovery.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/EventDiscovery.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/EventDiscovery.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/EventDiscovery.java Wed Jul 12 02:38:25 2006 @@ -37,7 +37,7 @@ private static Logger log = LoggerFactory.getLogger(EventDiscovery.class); - private List bundles = new ArrayList(); + private final List bundles = new ArrayList(); /** * Add the Xml representation of an single 'eventBundle' listing the @@ -54,6 +54,27 @@ } /** + * Returns an iterator over the {@link EventBundle event bundles} currently + * present on this discovery. + * + * @return iterator over event bundles present. + */ + public Iterator getEventBundles() { + return bundles.iterator(); + } + + /** + * Returns true, if this event discovery does not report any events (thus + * {@link #getEventBundles()} would return an empty iterator. + * + * @return true if {@link #getEventBundles()} would return an empty iterator, + * false otherwise. + */ + public boolean isEmpty() { + return bundles.isEmpty(); + } + + /** * Returns the Xml representation of this EventDiscovery as * being present in the POLL response body. * @@ -63,12 +84,11 @@ */ public Element toXml(Document document) { Element ed = DomUtil.createElement(document, XML_EVENTDISCOVERY, NAMESPACE); - Iterator it = bundles.iterator(); + Iterator it = getEventBundles(); while (it.hasNext()) { EventBundle bundle = (EventBundle)it.next(); ed.appendChild(bundle.toXml(document)); } return ed; } - } Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionDiscovery.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionDiscovery.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionDiscovery.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionDiscovery.java Wed Jul 12 02:38:25 2006 @@ -17,9 +17,16 @@ package org.apache.jackrabbit.webdav.observation; import org.apache.jackrabbit.webdav.property.AbstractDavProperty; +import org.apache.jackrabbit.webdav.xml.DomUtil; +import org.apache.jackrabbit.webdav.xml.ElementIterator; +import org.apache.jackrabbit.webdav.xml.XmlSerializable; +import org.apache.jackrabbit.webdav.DavConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; +import java.util.List; +import java.util.ArrayList; + /** * SubscriptionDiscovery encapsulates the 'subscriptiondiscovery' * property of a webdav resource. @@ -79,4 +86,38 @@ return elem; } + //-----------------------------------------------------< static Factory >--- + public static SubscriptionDiscovery createFromXml(Element sDiscoveryElement) { + if (!DomUtil.matches(sDiscoveryElement, ObservationConstants.SUBSCRIPTIONDISCOVERY.getName(), ObservationConstants.SUBSCRIPTIONDISCOVERY.getNamespace())) { + throw new IllegalArgumentException("'subscriptiondiscovery' element expected."); + } + + List subscriptions = new ArrayList(); + ElementIterator it = DomUtil.getChildren(sDiscoveryElement, ObservationConstants.XML_SUBSCRIPTION, ObservationConstants.NAMESPACE); + while (it.hasNext()) { + final Element sb = it.nextElement(); + // anonymous inner class: Subscription interface + Subscription s = new Subscription() { + /** + * @see Subscription#getSubscriptionId() + */ + public String getSubscriptionId() { + Element ltEl = DomUtil.getChildElement(sb, ObservationConstants.XML_SUBSCRIPTIONID, ObservationConstants.NAMESPACE); + if (ltEl != null) { + return DomUtil.getChildText(sb, DavConstants.XML_HREF, DavConstants.NAMESPACE); + } + return null; + } + /** + * @see XmlSerializable#toXml(Document) + */ + public Element toXml(Document document) { + return (Element) document.importNode(sb, true); + } + }; + subscriptions.add(s); + } + + return new SubscriptionDiscovery((Subscription[]) subscriptions.toArray(new Subscription[subscriptions.size()])); + } } Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionInfo.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionInfo.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionInfo.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/observation/SubscriptionInfo.java Wed Jul 12 02:38:25 2006 @@ -18,6 +18,8 @@ import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.DavServletResponse; +import org.apache.jackrabbit.webdav.header.TimeoutHeader; +import org.apache.jackrabbit.webdav.header.DepthHeader; import org.apache.jackrabbit.webdav.xml.DomUtil; import org.apache.jackrabbit.webdav.xml.ElementIterator; import org.apache.jackrabbit.webdav.xml.Namespace; @@ -98,11 +100,16 @@ } /** - * Create a new SubscriptionInfo - * + * Create a new SubscriptionInfo from the given Xml element + * and from additional information that is transported within the request + * header: + *
    + *
  • {@link TimeoutHeader timeout},
  • + *
  • {@link DepthHeader isDeep}
  • + *
* @param reqInfo Xml element present in the request body. - * @param timeout as defined by the {@link org.apache.jackrabbit.webdav.DavConstants#HEADER_TIMEOUT timeout header}. - * @param isDeep as defined by the {@link org.apache.jackrabbit.webdav.DavConstants#HEADER_DEPTH depth header}. + * @param timeout as defined in the {@link org.apache.jackrabbit.webdav.DavConstants#HEADER_TIMEOUT timeout header}. + * @param isDeep as defined in the {@link org.apache.jackrabbit.webdav.DavConstants#HEADER_DEPTH depth header}. * @throws IllegalArgumentException if the reqInfo element does not contain the mandatory elements. */ public SubscriptionInfo(Element reqInfo, long timeout, boolean isDeep) throws DavException { @@ -110,26 +117,18 @@ log.warn("Element with name 'subscriptioninfo' expected"); throw new DavException(DavServletResponse.SC_BAD_REQUEST); } - List typeList = new ArrayList(); Element el = DomUtil.getChildElement(reqInfo, XML_EVENTTYPE, NAMESPACE); if (el != null) { - ElementIterator it = DomUtil.getChildren(el); - while (it.hasNext()) { - Element typeElem = it.nextElement(); - EventType et = new SimpleEventType(typeElem.getLocalName(), DomUtil.getNamespace(typeElem)); - typeList.add(et); + eventTypes = DefaultEventType.createFromXml(el); + if (eventTypes.length == 0) { + log.warn("'subscriptioninfo' must at least indicate a single, valid event type."); + throw new DavException(DavServletResponse.SC_BAD_REQUEST); } } else { log.warn("'subscriptioninfo' must contain an 'eventtype' child element."); throw new DavException(DavServletResponse.SC_BAD_REQUEST); } - if (typeList.isEmpty()) { - log.warn("'subscriptioninfo' must at least indicate a single event type."); - throw new DavException(DavServletResponse.SC_BAD_REQUEST); - } - eventTypes = (EventType[]) typeList.toArray(new EventType[typeList.size()]); - List filters = new ArrayList(); el = DomUtil.getChildElement(reqInfo, XML_FILTER, NAMESPACE); if (el != null) { @@ -239,33 +238,5 @@ DomUtil.addChildElement(subscrInfo, XML_NOLOCAL, NAMESPACE); } return subscrInfo; - } - - //--------------------------------------------------------< inner class >--- - /** - * Simple EventType implementation that only consists of a qualified event - * name. - */ - private class SimpleEventType implements EventType { - - private String localName; - private Namespace namespace; - - SimpleEventType(String localName, Namespace namespace) { - this.localName = localName; - this.namespace = namespace; - } - - public Element toXml(Document document) { - return DomUtil.createElement(document, localName, namespace); - } - - public String getName() { - return localName; - } - - public Namespace getNamespace() { - return namespace; - } } } Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/MergeInfo.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/MergeInfo.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/MergeInfo.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/MergeInfo.java Wed Jul 12 02:38:25 2006 @@ -154,4 +154,29 @@ return elem; } + + /** + * Factory method to create a minimal DAV:merge element to create a new + * MergeInfo object. + * + * @param mergeSource + * @param isNoAutoMerge + * @param isNoCheckout + * @param factory + * @return + */ + public static Element createMergeElement(String[] mergeSource, boolean isNoAutoMerge, boolean isNoCheckout, Document factory) { + Element mergeElem = DomUtil.createElement(factory, XML_MERGE, NAMESPACE); + Element source = DomUtil.addChildElement(mergeElem, DavConstants.XML_SOURCE, DavConstants.NAMESPACE); + for (int i = 0; i < mergeSource.length; i++) { + DomUtil.addChildElement(source, DavConstants.XML_HREF, DavConstants.NAMESPACE, mergeSource[i]); + } + if (isNoAutoMerge) { + DomUtil.addChildElement(mergeElem, XML_N0_AUTO_MERGE, NAMESPACE); + } + if (isNoCheckout) { + DomUtil.addChildElement(mergeElem, XML_N0_CHECKOUT, NAMESPACE); + } + return mergeElem; + } } Modified: jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/UpdateInfo.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/UpdateInfo.java?rev=421206&r1=421205&r2=421206&view=diff ============================================================================== --- jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/UpdateInfo.java (original) +++ jackrabbit/trunk/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/version/UpdateInfo.java Wed Jul 12 02:38:25 2006 @@ -65,8 +65,8 @@ public static final int UPDATE_BY_WORKSPACE = 2; private Element updateElement; - private DavPropertyNameSet propertyNameSet = new DavPropertyNameSet(); + private DavPropertyNameSet propertyNameSet = new DavPropertyNameSet(); private String[] source; private int type; @@ -192,24 +192,45 @@ if (updateElement != null) { elem = (Element)document.importNode(updateElement, true); } else { - elem = DomUtil.createElement(document, XML_UPDATE, NAMESPACE); - switch (type) { - case UPDATE_BY_VERSION: - Element vE = DomUtil.addChildElement(elem, XML_VERSION, NAMESPACE); - for (int i = 0; i < source.length; i++) { - vE.appendChild(DomUtil.hrefToXml(source[i], document)); - } - break; - case UPDATE_BY_LABEL: - DomUtil.addChildElement(elem, XML_LABEL_NAME, NAMESPACE, source[0]); - break; - case UPDATE_BY_WORKSPACE: - DomUtil.addChildElement(elem, XML_WORKSPACE, NAMESPACE, source[0]); - // no default. - } + elem = createUpdateElement(source, type, document); } if (!propertyNameSet.isEmpty()) { elem.appendChild(propertyNameSet.toXml(document)); + } + return elem; + } + + /** + * Factory method to create the basic structure of an UpdateInfo + * object. + * + * @param updateSource + * @param updateType + * @param factory + * @return + */ + public static Element createUpdateElement(String[] updateSource, int updateType, Document factory) { + if (updateSource == null || updateSource.length == 0) { + throw new IllegalArgumentException("Update source must specific at least a single resource used to run the update."); + } + + Element elem = DomUtil.createElement(factory, XML_UPDATE, NAMESPACE); + switch (updateType) { + case UPDATE_BY_VERSION: + Element vE = DomUtil.addChildElement(elem, XML_VERSION, NAMESPACE); + for (int i = 0; i < updateSource.length; i++) { + vE.appendChild(DomUtil.hrefToXml(updateSource[i], factory)); + } + break; + case UPDATE_BY_LABEL: + DomUtil.addChildElement(elem, XML_LABEL_NAME, NAMESPACE, updateSource[0]); + break; + case UPDATE_BY_WORKSPACE: + DomUtil.addChildElement(elem, XML_WORKSPACE, NAMESPACE, updateSource[0]); + break; + // no default. + default: + throw new IllegalArgumentException("Invalid update type: " + updateType); } return elem; }