jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r161660 [6/7] - in incubator/jackrabbit/trunk/contrib/jcr-server: ./ client/ client/src/java/org/apache/jackrabbit/webdav/ client/src/java/org/apache/jackrabbit/webdav/client/ client/src/java/org/apache/jackrabbit/webdav/client/methods/ server/ server/src/java/org/apache/jackrabbit/ server/src/java/org/apache/jackrabbit/server/ server/src/java/org/apache/jackrabbit/server/io/ server/src/java/org/apache/jackrabbit/server/jcr/ server/src/java/org/apache/jackrabbit/server/simple/ server/src/java/org/apache/jackrabbit/server/simple/dav/ server/src/java/org/apache/jackrabbit/webdav/simple/ server/src/java/org/apache/jackrabbit/webdav/spi/ server/src/java/org/apache/jackrabbit/webdav/spi/lock/ server/src/java/org/apache/jackrabbit/webdav/spi/nodetype/ server/src/java/org/apache/jackrabbit/webdav/spi/observation/ server/src/java/org/apache/jackrabbit/webdav/spi/property/ server/src/java/org/apache/jackrabbit/webdav/spi/search/ server/src/java/org/apache/jackrabbit/webdav/spi/transaction/ server/src/java/org/apache/jackrabbit/webdav/spi/version/ server/src/java/org/apache/jackrabbit/webdav/spi/version/report/ webapp/ webapp/src/webapp/WEB-INF/ webdav/ webdav/src/java/org/apache/jackrabbit/ webdav/src/java/org/apache/jackrabbit/webdav/ webdav/src/java/org/apache/jackrabbit/webdav/header/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/lock/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/version/ webdav/src/java/org/apache/jackrabbit/webdav/jcr/version/report/ webdav/src/java/org/apache/jackrabbit/webdav/lock/ webdav/src/java/org/apache/jackrabbit/webdav/search/
Date Sun, 17 Apr 2005 14:52:15 GMT
Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeElement.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeElement.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeProperty.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeProperty.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeProperty.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeProperty.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.nodetype;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.property.DavPropertyName;
+import org.apache.jackrabbit.webdav.property.DavProperty;
+import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
+import org.jdom.Element;
+
+import javax.jcr.nodetype.NodeType;
+import java.util.*;
+
+/**
+ * <code>NodeTypeProperty</code>...
+ */
+public class NodeTypeProperty extends AbstractDavProperty {
+
+    private static Logger log = Logger.getLogger(NodeTypeProperty.class);
+
+    private final NodeTypeElement[] value;
+
+    public NodeTypeProperty(DavPropertyName name, NodeType nodeType, boolean isProtected) {
+        super(name, isProtected);
+        value = new NodeTypeElement[] {new NodeTypeElement(nodeType)};
+    }
+
+    public NodeTypeProperty(DavPropertyName name, NodeType[] nodeTypes, boolean isProtected) {
+        super(name, isProtected);
+        value = NodeTypeElement.create(nodeTypes);
+    }
+
+    /**
+     * Create a new <code>NodeTypeProperty</code> from the specified general
+     * DavProperty object.
+     *
+     * @param property
+     * @throws IllegalArgumentException if the content of the specified property
+     * contains elements other than {@link NodeTypeConstants#XML_NODETYPE}.
+     */
+    public NodeTypeProperty(DavProperty property) {
+        super(property.getName(), property.isProtected());
+
+        if (property.getValue() instanceof List) {
+            List ntElemList = new ArrayList();
+            Iterator it = ((List) property.getValue()).iterator();
+            while (it.hasNext()) {
+                Object content = it.next();
+                if (content instanceof Element) {
+                    ntElemList.add(new NodeTypeElement((Element)content));
+                }
+            }
+            value = (NodeTypeElement[]) ntElemList.toArray(new NodeTypeElement[ntElemList.size()]);
+        } else if (property.getValue() instanceof Element) {
+	    NodeTypeElement ntElem = new NodeTypeElement((Element)property.getValue());
+	    value = new NodeTypeElement [] {ntElem};
+	} else {
+            value = new NodeTypeElement[0];
+        }
+    }
+
+    /**
+     * Return a set of nodetype names present in this property.
+     *
+     * @return set of nodetype names
+     */
+    public Set getNodeTypeNames() {
+        HashSet names = new HashSet();
+        Object val = getValue();
+        if (val instanceof NodeTypeElement[]) {
+            NodeTypeElement[] elems = (NodeTypeElement[])val;
+            for (int i = 0; i < elems.length; i++) {
+                String ntName = elems[i].getNodeTypeName();
+                if (ntName != null) {
+                    names.add(ntName);
+                }
+            }
+        }
+        return names;
+    }
+
+    /**
+     * Returns the value of this property which is an array of {@link NodeTypeElement}
+     * objects.
+     *
+     * @return an array of {@link NodeTypeElement}s
+     */
+    public Object getValue() {
+        return value;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeProperty.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/NodeTypeProperty.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/PropertyDefinitionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/PropertyDefinitionImpl.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/PropertyDefinitionImpl.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/PropertyDefinitionImpl.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.nodetype;
+
+import org.apache.log4j.Logger;
+import org.jdom.Element;
+
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.Value;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+/**
+ * <code>PropertyDefinitionImpl</code>...
+ */
+public final class PropertyDefinitionImpl extends ItemDefinitionImpl implements PropertyDefinition {
+
+    private static Logger log = Logger.getLogger(PropertyDefinitionImpl.class);
+
+    private final int type;
+    private final String[] valueConstraints;
+    private final Value[] defaultValues;
+    private final boolean isMultiple;
+
+    private PropertyDefinitionImpl(PropertyDefinition definition) {
+	super(definition);
+
+	type = definition.getRequiredType();
+	valueConstraints = definition.getValueConstraints();
+	defaultValues = definition.getDefaultValues();
+	isMultiple = definition.isMultiple();
+    }
+
+    public static PropertyDefinitionImpl create(PropertyDefinition definition) {
+	if (definition instanceof PropertyDefinitionImpl) {
+	    return (PropertyDefinitionImpl)definition;
+	} else {
+	    return new PropertyDefinitionImpl(definition);
+	}
+    }
+
+    //----------------------------------------< PropertyDefintion interface >---
+    /**
+     * @see PropertyDefinition#getRequiredType()
+     */
+    public int getRequiredType() {
+	return type;
+    }
+
+    /**
+     * @see PropertyDefinition#getValueConstraints()
+     */
+    public String[] getValueConstraints() {
+	return valueConstraints;
+    }
+
+    /**
+     * @see PropertyDefinition#getDefaultValues()
+     */
+    public Value[] getDefaultValues() {
+	return defaultValues;
+    }
+
+    /**
+     * @see PropertyDefinition#isMultiple()
+     */
+    public boolean isMultiple() {
+	return isMultiple;
+    }
+
+    //-------------------------------------< implementation specific method >---
+    /**
+     * Return xml representation
+     *
+     * @return xml representation
+     */
+    public Element toXml() {
+	Element elem = super.toXml();
+
+        elem.setAttribute(MULTIPLE_ATTRIBUTE, Boolean.toString(isMultiple()));
+        elem.setAttribute(REQUIREDTYPE_ATTRIBUTE, PropertyType.nameFromValue(getRequiredType()));
+
+        // default values may be 'null'
+        Value[] values = getDefaultValues();
+        if (values != null) {
+            Element dvElement = new Element(DEFAULTVALUES_ELEMENT);
+            for (int i = 0; i < values.length; i++) {
+                try {
+                    Element valElem = new Element(DEFAULTVALUE_ELEMENT).setText(values[i].getString());
+                    dvElement.addContent(valElem);
+                } catch (RepositoryException e) {
+                    // should not occur
+                    log.error(e.getMessage());
+                }
+            }
+            elem.addContent(dvElement);
+        }
+        // value constraints array is never null.
+        Element constrElem = new Element(VALUECONSTRAINTS_ELEMENT);
+        String[] constraints = getValueConstraints();
+        for (int i = 0; i < constraints.length; i++) {
+            constrElem.addContent(new Element(VALUECONSTRAINT_ELEMENT).setText(constraints[i]));
+        }
+        elem.addContent(constrElem);
+
+        return elem;
+    }
+
+    /**
+     * Returns {@link #PROPERTYDEFINITION_ELEMENT}.
+     * 
+     * @return always returns {@link #PROPERTYDEFINITION_ELEMENT}
+     */
+    public String getElementName() {
+	return PROPERTYDEFINITION_ELEMENT;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/PropertyDefinitionImpl.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/nodetype/PropertyDefinitionImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.observation;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.DavResourceLocator;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.observation.*;
+import org.apache.jackrabbit.webdav.util.XmlUtil;
+import org.apache.jackrabbit.core.util.uuid.UUID;
+import org.jdom.Element;
+
+import javax.jcr.observation.*;
+import javax.jcr.observation.EventListener;
+import javax.jcr.RepositoryException;
+import java.util.*;
+
+/**
+ * The <code>Subscription</code> class encapsulates a single subscription with
+ * the following responsibilities:<ul>
+ * <li>Providing access to the subscription info,</li>
+ * <li>Recording events this subscription is interested in,</li>
+ * <li>Providing access to the events.</li>
+ * </ul>
+ */
+public class SubscriptionImpl implements Subscription, ObservationConstants, EventListener {
+
+    private static Logger log = Logger.getLogger(SubscriptionImpl.class);
+    private static final long DEFAULT_TIMEOUT = 300000; // 5 minutes
+
+    private SubscriptionInfo info;
+
+    private final DavResourceLocator locator;
+    private final String subscriptionId = UUID.randomUUID().toString();
+    private final List eventBundles = new ArrayList();
+
+    /**
+     * Create a new <code>Subscription</code> with the given {@link SubscriptionInfo}
+     * and {@link org.apache.jackrabbit.webdav.observation.ObservationResource resource}.
+     *
+     * @param info
+     * @param resource
+     */
+    public SubscriptionImpl(SubscriptionInfo info, ObservationResource resource) {
+        setInfo(info);
+        locator = resource.getLocator();
+    }
+
+    /**
+     * Returns the id of this subscription.
+     *
+     * @return subscriptionId
+     */
+    public String getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    /**
+     * Return the Xml representation of this <code>Subscription</code> as required
+     * for the {@link org.apache.jackrabbit.webdav.observation.SubscriptionDiscovery} webdav property that in included
+     * in the response body of a sucessful SUBSCRIBE request or as part of a
+     * PROPFIND response.
+     *
+     * @return Xml representation
+     */
+    public Element toXml() {
+        Element subscr = new Element(XML_SUBSCRIPTION, NAMESPACE);
+        Element[] elems = info.toXml();
+        for (int i = 0; i < elems.length; i++) {
+            subscr.addContent(elems[i]);
+        }
+
+        Element id = new Element(XML_SUBSCRIPTIONID);
+        id.addContent(XmlUtil.hrefToXml(subscriptionId));
+        subscr.addContent(id);
+        return subscr;
+    }
+
+    /**
+     * Modify the {@link SubscriptionInfo} for this subscription.
+     *
+     * @param info
+     */
+    void setInfo(SubscriptionInfo info) {
+        this.info = info;
+        // validate the timeout and adjust value, if it is invalid or missing
+        long timeout = info.getTimeOut();
+        if (timeout == DavConstants.UNDEFINED_TIMEOUT) {
+            info.setTimeOut(DEFAULT_TIMEOUT);
+        }
+    }
+
+    /**
+     * @return JCR compliant integer representation of the event types defined
+     * for this {@link SubscriptionInfo}.
+     */
+    int getEventTypes() {
+        Iterator xmlTypes = info.getEventTypes().iterator();
+        int eventTypes = 0;
+        while (xmlTypes.hasNext()) {
+            eventTypes |= nametoTypeConstant(((Element)xmlTypes.next()).getName());
+        }
+        return eventTypes;
+    }
+
+    /**
+     * @return a String array with size > 0 or <code>null</code>
+     */
+    String[] getUuidFilters() {
+        return info.getFilters(XML_UUID);
+    }
+
+    /**
+     * @return a String array with size > 0 or <code>null</code>
+     */
+    String[] getNodetypeNameFilters() {
+        return info.getFilters(XML_NODETYPE_NAME);
+    }
+
+    /**
+     *
+     * @return true if a {@link ObservationConstants#XML_NOLOCAL} element
+     * is present in the {@link SubscriptionInfo}.
+     */
+    boolean isNoLocal() {
+        return info.isNoLocal();
+    }
+
+    /**
+     * @return true if this subscription is intended to be deep.
+     */
+    boolean isDeep() {
+        return info.isDeep();
+    }
+
+    /**
+     * @return the locator of the {@link ObservationResource resource} this
+     * <code>Subscription</code> was requested for.
+     */
+    DavResourceLocator getLocator() {
+        return locator;
+    }
+
+    /**
+     * Returns true if this <code>Subscription</code> matches the given
+     * resource.
+     *
+     * @param resource
+     * @return true if this <code>Subscription</code> matches the given
+     * resource.
+     */
+    boolean isSubscribedToResource(ObservationResource resource) {
+        return locator.equals(resource.getLocator());
+    }
+
+    /**
+     * Returns true if this <code>Subscription</code> is expired and therefore
+     * stopped recording events.
+     *
+     * @return true if this <code>Subscription</code> is expired
+     */
+    boolean isExpired() {
+        return System.currentTimeMillis() > info.getTimeOut() + System.currentTimeMillis();
+    }
+
+    /**
+     * Returns a {@link org.apache.jackrabbit.webdav.observation.EventDiscovery} object listing all events that were
+     * recorded since the last call to this method and clears the list of event
+     * bundles.
+     *
+     * @return object listing all events that were recorded.
+     * @see #onEvent(EventIterator)
+     */
+    synchronized EventDiscovery discoverEvents() {
+        EventDiscovery ed = new EventDiscovery();
+        Iterator it = eventBundles.iterator();
+        while (it.hasNext()) {
+            EventBundle eb = (EventBundle) it.next();
+            ed.addEventBundle(eb.toXml());
+        }
+        // clear list
+        eventBundles.clear();
+        return ed;
+    }
+
+    //--------------------------------------------< EventListener interface >---
+    /**
+     * Records the events passed as a new event bundle in order to make them
+     * available with the next {@link #discoverEvents()} request.
+     *
+     * @param events to be recorded.
+     * @see EventListener#onEvent(EventIterator)
+     * @see #discoverEvents()
+     */
+    public synchronized void onEvent(EventIterator events) {
+        // TODO: correct not to accept events after expiration? without unsubscribing?
+        if (!isExpired()) {
+            eventBundles.add(new EventBundle(events));
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * Static utility method in order to convert the types defined by
+     * {@link javax.jcr.observation.Event} into their Xml representation.
+     *
+     * @param jcrEventType
+     * @return Xml representation of the event type.
+     */
+    static Element typeConstantToXml(int jcrEventType) {
+        String eventName;
+        switch (jcrEventType) {
+            case Event.NODE_ADDED:
+                eventName = EVENT_NODEADDED;
+                break;
+            case Event.NODE_REMOVED:
+                eventName = EVENT_NODEREMOVED;
+                break;
+            case Event.PROPERTY_ADDED:
+                eventName = EVENT_PROPERTYADDED;
+                break;
+            case Event.PROPERTY_CHANGED:
+                eventName = EVENT_PROPERTYCHANGED;
+                break;
+            default:
+                //Event.PROPERTY_REMOVED:
+                eventName = EVENT_PROPERTYREMOVED;
+                break;
+        }
+        return new Element(eventName, NAMESPACE);
+    }
+
+    /**
+     * Static utility method to convert an event type name as present in the
+     * Xml request body into the corresponding constant defined by
+     * {@link javax.jcr.observation.Event}.
+     *
+     * @param eventTypeName
+     * @return event type as defined by {@link javax.jcr.observation.Event}.
+     * @throws IllegalArgumentException if the given element cannot be translated
+     * to any of the events defined by {@link javax.jcr.observation.Event}.
+     */
+    static int nametoTypeConstant(String eventTypeName) {
+        int eType;
+        if (EVENT_NODEADDED.equals(eventTypeName)) {
+            eType = Event.NODE_ADDED;
+        } else if (EVENT_NODEREMOVED.equals(eventTypeName)) {
+            eType = Event.NODE_REMOVED;
+        } else if (EVENT_PROPERTYADDED.equals(eventTypeName)) {
+            eType = Event.PROPERTY_ADDED;
+        } else if (EVENT_PROPERTYCHANGED.equals(eventTypeName)) {
+            eType = Event.PROPERTY_CHANGED;
+        } else if (EVENT_PROPERTYREMOVED.equals(eventTypeName)) {
+            eType = Event.PROPERTY_REMOVED;
+        } else {
+            throw new IllegalArgumentException("Invalid event type: "+eventTypeName);
+        }
+        return eType;
+    }
+
+    /**
+     * Inner class <code>EventBundle</code> encapsulats an event bundle as
+     * recorded {@link SubscriptionImpl#onEvent(EventIterator) on event} and
+     * provides the possibility to retrieve the Xml representation of the
+     * bundle and the events included in order to respond to a POLL request.
+     *
+     * @see SubscriptionImpl#discoverEvents()
+     */
+    private class EventBundle {
+
+        private final EventIterator events;
+
+        private EventBundle(EventIterator events) {
+            this.events = events;
+        }
+
+        private Element toXml() {
+            Element bundle = new Element(XML_EVENTBUNDLE, NAMESPACE);
+            while (events.hasNext()) {
+                Event event = events.nextEvent();
+
+                Element eventElem = new Element(XML_EVENT, NAMESPACE);
+                // href
+                String eHref = "";
+                try {
+                    boolean isCollection = (event.getType() == Event.NODE_ADDED || event.getType() == Event.NODE_REMOVED);
+                    eHref = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), event.getPath()).getHref(isCollection);
+                } catch (RepositoryException e) {
+                    // should not occur....
+                    log.error(e.getMessage());
+                }
+                eventElem.addContent(XmlUtil.hrefToXml(eHref));
+                // eventtype
+                Element eType = new Element(XML_EVENTTYPE, NAMESPACE).addContent(typeConstantToXml(event.getType()));
+                eventElem.addContent(eType);
+                // user id
+                Element eUserId = new Element(XML_EVENTUSERID, NAMESPACE).setText(event.getUserID());
+                eventElem.addContent(eUserId);
+            }
+            return bundle;
+        }
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.observation;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.*;
+import org.apache.jackrabbit.webdav.jcr.JcrDavException;
+import org.apache.jackrabbit.webdav.observation.*;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.observation.ObservationManager;
+import java.util.*;
+
+/**
+ * <code>SubscriptionManager</code> collects all subscriptions requested, handles
+ * the subscription timeout and provides METHODS to discover subscriptions
+ * present on a given resource as well as events for an specific subscription.
+ *
+ * @todo make sure all expired subscriptions are removed!
+ */
+public class SubscriptionManagerImpl implements SubscriptionManager {
+
+    private static Logger log = Logger.getLogger(SubscriptionManager.class);
+
+    /**
+     * Map containing all {@link org.apache.jackrabbit.webdav.observation.Subscription subscriptions}.
+     */
+    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.
+     *
+     * @param resource
+     * @todo is it correct to return subscriptions made by another session?
+     */
+    public SubscriptionDiscovery getSubscriptionDiscovery(ObservationResource resource) {
+        Subscription[] subsForResource = subscriptions.getByPath(resource.getLocator());
+        return new SubscriptionDiscovery(subsForResource);
+    }
+
+    /**
+     * Create a new <code>Subscription</code> or update an existing <code>Subscription</code>
+     * and add it as eventlistener to the {@link javax.jcr.observation.ObservationManager}.
+     *
+     * @param info
+     * @param subscriptionId
+     * @param resource
+     * @return <code>Subscription</code> that has been added to the {@link javax.jcr.observation.ObservationManager}
+     * @throws DavException if the subscription fails
+     */
+    public Subscription subscribe(SubscriptionInfo info, String subscriptionId,
+                                  ObservationResource resource)
+            throws DavException {
+
+        SubscriptionImpl subscription;
+        DavSession session = resource.getSession();
+        if (subscriptionId == null) {
+            // new subscription
+            subscription = new SubscriptionImpl(info, resource);
+            registerSubscription(subscription, session);
+
+            // ajust references to this subscription
+            subscriptions.put(subscription.getSubscriptionId(), subscription);
+            session.addReference(subscription.getSubscriptionId());
+        } else {
+            // refresh/modify existing one
+            subscription = validate(subscriptionId, resource);
+            subscription.setInfo(info);
+            registerSubscription(subscription, session);
+        }
+        return subscription;
+    }
+
+    /**
+     * Register the event listener defined by the given subscription to the
+     * repository's observation manager.
+     *
+     * @param subscription
+     * @param session
+     * @throws DavException
+     */
+    private void registerSubscription(SubscriptionImpl subscription, DavSession session)
+            throws DavException {
+        try {
+            ObservationManager oMgr = session.getRepositorySession().getWorkspace().getObservationManager();
+            oMgr.addEventListener(subscription, subscription.getEventTypes(),
+                    subscription.getLocator().getResourcePath(), subscription.isDeep(),
+                    subscription.getUuidFilters(),
+                    subscription.getNodetypeNameFilters(),
+                    subscription.isNoLocal());
+        } catch (RepositoryException e) {
+            log.error("Unable to register eventlistener: "+e.getMessage());
+            throw new JcrDavException(e);
+        }
+    }
+
+    /**
+     * Unsubscribe the <code>Subscription</code> with the given id and remove it
+     * from the {@link javax.jcr.observation.ObservationManager} as well as
+     * from the internal map.
+     *
+     * @param subscriptionId
+     * @param resource
+     * @throws DavException
+     */
+    public void unsubscribe(String subscriptionId, ObservationResource resource)
+            throws DavException {
+
+        SubscriptionImpl subs = validate(subscriptionId, resource);
+        unregisterSubscription(subs, resource.getSession());
+    }
+
+    /**
+     * Remove the event listener defined by the specified subscription from
+     * the repository's observation manager.
+     *
+     * @param subscription
+     * @param session
+     * @throws DavException
+     */
+    private void unregisterSubscription(SubscriptionImpl subscription,
+                                        DavSession session) throws DavException {
+        try {
+            session.getRepositorySession().getWorkspace().getObservationManager().removeEventListener(subscription);
+            String sId = subscription.getSubscriptionId();
+
+            // clean up any references
+            subscriptions.remove(sId);
+            session.removeReference(sId);
+
+        } catch (RepositoryException e) {
+            log.error("Unable to remove eventlistener: "+e.getMessage());
+            throw new JcrDavException(e);
+        }
+    }
+
+    /**
+     * Retrieve all event bundles accumulated since for the subscription specified
+     * by the given id.
+     *
+     * @param subscriptionId
+     * @param resource
+     * @return object encapsulating the events.
+     */
+    public EventDiscovery poll(String subscriptionId, ObservationResource resource)
+            throws DavException {
+
+        SubscriptionImpl subs = validate(subscriptionId, resource);
+        return subs.discoverEvents();
+    }
+
+    /**
+     * Validate the given subscription id. The validation will fail under the following
+     * conditions:<ul>
+     * <li>The subscription with the given id does not exist,</li>
+     * <li>DavResource path does not match the subscription id,</li>
+     * <li>The subscription with the given id is already expired.</li>
+     * </ul>
+     *
+     * @param subscriptionId
+     * @param resource
+     * @return <code>Subscription</code> with the given id.
+     * @throws DavException if an error occured while retrieving the <code>Subscription</code>
+     */
+    private SubscriptionImpl validate(String subscriptionId, ObservationResource resource)
+            throws DavException {
+
+        SubscriptionImpl subs;
+        if (subscriptions.contains(subscriptionId)) {
+            subs = (SubscriptionImpl) subscriptions.get(subscriptionId);
+            if (!subs.isSubscribedToResource(resource)) {
+                throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to operate on subscription with invalid resource path.");
+            }
+            if (subs.isExpired()) {
+                unregisterSubscription(subs, resource.getSession());
+                throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to  operate on expired subscription.");
+            }
+            return subs;
+        } else {
+            throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to modify or to poll for non-existing subscription.");
+        }
+    }
+
+    /**
+     * Private inner class <code>SubscriptionMap</code> that allows for quick
+     * access by resource path as well as by subscription id.
+     */
+    private class SubscriptionMap {
+
+        private HashMap subscriptions = new HashMap();
+        private HashMap paths = new HashMap();
+
+        private boolean contains(String subscriptionId) {
+            return subscriptions.containsKey(subscriptionId);
+        }
+
+        private Subscription get(String subscriptionId) {
+            return (Subscription) subscriptions.get(subscriptionId);
+        }
+
+        private void put(String subscriptionId, SubscriptionImpl subscription) {
+            subscriptions.put(subscriptionId, subscription);
+            DavResourceLocator key = subscription.getLocator();
+            Set idSet;
+            if (paths.containsKey(key)) {
+                idSet = (Set) paths.get(key);
+            } else {
+                idSet = new HashSet();
+                paths.put(key, idSet);
+            }
+            if (!idSet.contains(subscriptionId)) {
+                idSet.add(subscriptionId);
+            }
+        }
+
+        private void remove(String subscriptionId) {
+            SubscriptionImpl sub = (SubscriptionImpl) subscriptions.remove(subscriptionId);
+            ((Set)paths.get(sub.getLocator())).remove(subscriptionId);
+        }
+
+        private Subscription[] getByPath(DavResourceLocator locator) {
+            Set idSet = (Set) paths.get(locator);
+            if (idSet != null && !idSet.isEmpty()) {
+                Iterator idIterator = idSet.iterator();
+                Subscription[] subsForResource = new Subscription[idSet.size()];
+                int i = 0;
+                while (idIterator.hasNext()) {
+                    subsForResource[i] = (Subscription) subscriptions.get(idIterator.next());
+                }
+                return subsForResource;
+            } else {
+                return new Subscription[0];
+            }
+        }
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/observation/SubscriptionManagerImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/package.html
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/package.html?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/package.html (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/package.html Sun Apr 17 07:51:59 2005
@@ -0,0 +1,3 @@
+<body>
+Contains JCR specific implementations.
+</body>
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/LengthsProperty.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/LengthsProperty.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/LengthsProperty.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/LengthsProperty.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,56 @@
+/*
+* Copyright 2004 The Apache Software Foundation.
+*
+* Licensed 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.jcr.property;
+
+import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
+import org.apache.jackrabbit.webdav.jcr.ItemResourceConstants;
+import org.jdom.Element;
+
+/**
+ * <code>LengthsProperty</code> extends {@link org.apache.jackrabbit.webdav.property.DavProperty} providing
+ * utilities to handle the multiple lengths of the property item represented
+ * by this resource.
+ */
+public class LengthsProperty extends AbstractDavProperty implements ItemResourceConstants {
+
+    private final Element[] value;
+
+    /**
+     * Create a new <code>LengthsProperty</code> from the given long array.
+     *
+     * @param lengths as retrieved from the JCR property
+     */
+    public LengthsProperty(long[] lengths) {
+	super(JCR_LENGTHS, false);
+
+	Element[] elems = new Element[lengths.length];
+	for (int i = 0; i < lengths.length; i++) {
+	    elems[i] = new Element(XML_LENGTH, ItemResourceConstants.NAMESPACE);
+	    elems[i].addContent(String.valueOf(lengths[i]));
+	}
+	this.value = elems;
+    }
+
+    /**
+     * Returns an array of {@link Element}s representing the value of this
+     * property.
+     *
+     * @return an array of {@link Element}s
+     */
+    public Object getValue() {
+	return value;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/LengthsProperty.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/LengthsProperty.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ValuesProperty.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ValuesProperty.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ValuesProperty.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ValuesProperty.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,120 @@
+/*
+* Copyright 2004 The Apache Software Foundation.
+*
+* Licensed 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.jcr.property;
+
+import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
+import org.apache.jackrabbit.webdav.property.DavProperty;
+import org.apache.jackrabbit.webdav.jcr.ItemResourceConstants;
+import org.apache.jackrabbit.core.util.ValueHelper;
+import org.jdom.Element;
+
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.RepositoryException;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * <code>ValuesProperty</code> extends {@link org.apache.jackrabbit.webdav.property.DavProperty} providing
+ * utilities to handle the multiple values of the property item represented
+ * by this resource.
+ */
+public class ValuesProperty extends AbstractDavProperty implements ItemResourceConstants {
+
+    private final Element[] value;
+
+    /**
+     * Create a new <code>ValuesProperty</code> from the given {@link javax.jcr.Value Value
+     * array}.
+     *
+     * @param values Array of Value objects as obtained from the JCR property.
+     */
+    public ValuesProperty(Value[] values) throws ValueFormatException, RepositoryException {
+	super(JCR_VALUES, false);
+
+	Element[] propValue = new Element[values.length];
+	for (int i = 0; i < values.length; i++) {
+	    propValue[i] = new Element(XML_VALUE, ItemResourceConstants.NAMESPACE);
+	    propValue[i].addContent(ValueHelper.serialize(values[i], false));
+	}
+	// finally set the value to the DavProperty
+	value = propValue;
+    }
+    
+    /**
+     * Wrap the specified <code>DavProperty</code> in a new <code>ValuesProperty</code>.
+     *
+     * @param property
+     */
+    public ValuesProperty(DavProperty property) {
+	super(JCR_VALUES, false);
+
+	if (!JCR_VALUES.equals(property.getName())) {
+	    throw new IllegalArgumentException("ValuesProperty may only be created with a property that has name="+JCR_VALUES.getName());
+	}
+
+	Element[] elems = new Element[0];
+	if (property.getValue() instanceof List) {
+	    Iterator elemIt = ((List)property.getValue()).iterator();
+	    ArrayList valueElements = new ArrayList();
+	    while (elemIt.hasNext()) {
+		Object el = elemIt.next();
+		/* make sure, only Elements with name 'value' are used for
+		* the 'value' field. any other content (other elements, text,
+		* comment etc.) is ignored. NO bad-request/conflict error is
+		* thrown.
+		*/
+		if (el instanceof Element && XML_VALUE.equals(((Element)el).getName())) {
+		    valueElements.add(el);
+		}
+	    }
+	    /* fill the 'value' with the valid 'value' elements found before */
+	    elems = (Element[])valueElements.toArray(new Element[valueElements.size()]);
+	} else {
+	    new IllegalArgumentException("ValuesProperty may only be created with a property that has a list of 'value' elements as content.");
+	}
+	// finally set the value to the DavProperty
+	value = elems;
+    }
+
+    /**
+     * Converts the value of this property to a {@link javax.jcr.Value value array}.
+     * Please note, that the convertion is done by using the {@link org.apache.jackrabbit.core.util.ValueHelper}
+     * class that is not part of the JSR170 API.
+     *
+     * @return Array of Value objects
+     * @throws RepositoryException
+     */
+    public Value[] getValues(int propertyType) throws ValueFormatException, RepositoryException {
+	Element[] propValue = (Element[])getValue();
+	Value[] values = new Value[propValue.length];
+	for (int i = 0; i < propValue.length; i++) {
+	    values[i] = ValueHelper.deserialize(propValue[i].getText(), propertyType, false);
+	}
+	return values;
+    }
+
+    /**
+     * Returns an array of {@link Element}s representing the value of this
+     * property.
+     *
+     * @return an array of {@link Element}s
+     */
+    public Object getValue() {
+	return value;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ValuesProperty.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/property/ValuesProperty.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResourceImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResourceImpl.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResourceImpl.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResourceImpl.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.search;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.*;
+import org.apache.jackrabbit.webdav.search.SearchResource;
+import org.apache.jackrabbit.webdav.search.QueryGrammerSet;
+import org.apache.jackrabbit.webdav.search.SearchInfo;
+import org.apache.jackrabbit.webdav.jcr.JcrDavException;
+import org.apache.jackrabbit.JcrConstants;
+import org.jdom.Namespace;
+
+import javax.jcr.*;
+import javax.jcr.query.*;
+
+/**
+ * <code>SearchResourceImpl</code>...
+ */
+public class SearchResourceImpl implements SearchResource {
+
+    private static Logger log = Logger.getLogger(SearchResourceImpl.class);
+
+    private final DavSession session;
+    private final DavResourceLocator locator;
+
+    public SearchResourceImpl(DavResourceLocator locator, DavSession session) {
+        this.session = session;
+        this.locator = locator;
+    }
+
+    //-------------------------------------------< SearchResource interface >---
+    /**
+     * @see SearchResource#getQueryGrammerSet()
+     */
+    public QueryGrammerSet getQueryGrammerSet()  {
+        QueryGrammerSet qgs;
+        try {
+            QueryManager qMgr = session.getRepositorySession().getWorkspace().getQueryManager();
+            String[] langs = qMgr.getSupportedQueryLanguages();
+            qgs = new QueryGrammerSet();
+            for (int i = 0; i < langs.length; i++) {
+                // todo: define proper namespace
+                qgs.addQueryLanguage(langs[i], Namespace.NO_NAMESPACE);
+            }
+        } catch (RepositoryException e) {
+            qgs = new QueryGrammerSet(new String[0]);
+        }
+        return qgs;
+    }
+
+    /**
+     * Execute the query defined by the given <code>sInfo</code>.
+     *
+     * @see SearchResource#search(org.apache.jackrabbit.webdav.search.SearchInfo)
+     */
+    public MultiStatus search(SearchInfo sInfo) throws DavException {
+        try {
+            Query q = getQuery(sInfo);
+            QueryResult qR = q.execute();
+            return queryResultToMultiStatus(qR);
+
+        } catch (RepositoryException e) {
+            throw new JcrDavException(e);
+        }
+    }
+
+    /**
+     * Create a query from the information present in the <code>sInfo</code>
+     * object.<br>The following JCR specific logic is applied:
+     * <ul>
+     * <li>If the requested resource represents a node with nodetype nt:query, the
+     * request body is ignored and the query defined with the node is executed
+     * instead.</li>
+     * <li>If the requested resource does not represent an existing item, the
+     * specified query is persisted by calling {@link Query#storeAsNode(String)}.</li>
+     * </ul>
+     * @param sInfo defining the query to be executed
+     * @return <code>Query</code> object.
+     * @throws InvalidQueryException if the query defined by <code>sInfo</code> is invalid
+     * @throws RepositoryException the query manager cannot be accessed or if
+     * another error occurs.
+     * @throws DavException if <code>sInfo</code> is <code>null</code> and
+     * the underlaying repository item is not an nt:query node or if an error
+     * occurs when calling {@link Query#storeAsNode(String)}/
+     */
+    private Query getQuery(SearchInfo sInfo)
+            throws InvalidQueryException, RepositoryException, DavException {
+
+        Node rootNode = session.getRepositorySession().getRootNode();
+        QueryManager qMgr = session.getRepositorySession().getWorkspace().getQueryManager();
+        String resourcePath = locator.getResourcePath();
+
+        // test if query is defined by requested repository node
+        if (!rootNode.getPath().equals(resourcePath)) {
+            String qNodeRelPath = resourcePath.substring(1);
+            if (rootNode.hasNode(qNodeRelPath)) {
+                Node qNode = rootNode.getNode(qNodeRelPath);
+                if (qNode.isNodeType(JcrConstants.NT_QUERY)) {
+                    return qMgr.getQuery(qNode);
+                }
+            }
+        }
+
+        Query q;
+        if (sInfo != null) {
+            q = qMgr.createQuery(sInfo.getQuery(), sInfo.getLanguageName());
+        } else {
+            throw new DavException(DavServletResponse.SC_BAD_REQUEST, resourcePath + " is not a nt:query node -> searchRequest body required.");
+        }
+
+        /* test if resource path does not exist -> thus indicating that
+        the query must be made persistent by calling Query.save(String) */
+        if (!session.getRepositorySession().itemExists(resourcePath)) {
+            try {
+                q.storeAsNode(resourcePath);
+            } catch (RepositoryException e) {
+                // ItemExistsException should never occur.
+                new JcrDavException(e);
+            }
+        }
+        return q;
+    }
+
+    /**
+     * Build a <code>MultiStatus</code> object from the specified query result.
+     *
+     * @param qResult <code>QueryResult</code> as obtained from {@link javax.jcr.query.Query#execute()}.
+     * @return <code>MultiStatus</code> object listing the query result in
+     * Webdav compatible form.
+     * @throws RepositoryException
+     */
+    private MultiStatus queryResultToMultiStatus(QueryResult qResult)
+            throws RepositoryException {
+        MultiStatus ms = new MultiStatus();
+
+        String[] columnNames = qResult.getColumnNames();
+        RowIterator rowIter = qResult.getRows();
+        while (rowIter.hasNext()) {
+            Row row = rowIter.nextRow();
+            Value[] values = row.getValues();
+
+            // get the jcr:path column indicating the node path
+            String nodePath = row.getValue(JcrConstants.JCR_PATH).getString();
+            // create a new ms-response for this row of the result set
+            DavResourceLocator loc = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), nodePath);
+            String nodeHref = loc.getHref(true);
+            MultiStatusResponse resp = new MultiStatusResponse(nodeHref);
+            // build the s-r-property
+            SearchResultProperty srp = new SearchResultProperty(columnNames, values);
+            resp.add(srp);
+            ms.addResponse(resp);
+        }
+        return ms;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResourceImpl.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResourceImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResultProperty.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResultProperty.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResultProperty.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResultProperty.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.search;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.property.DavPropertyName;
+import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
+import org.apache.jackrabbit.webdav.property.DavProperty;
+import org.apache.jackrabbit.webdav.jcr.ItemResourceConstants;
+import org.apache.jackrabbit.core.util.ValueHelper;
+import org.jdom.Element;
+
+import javax.jcr.Value;
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+import java.util.*;
+
+/**
+ * <code>SearchResultProperty</code>...
+ */
+public class SearchResultProperty extends AbstractDavProperty implements ItemResourceConstants {
+
+    private static Logger log = Logger.getLogger(SearchResultProperty.class);
+
+    public static final DavPropertyName SEARCH_RESULT_PROPERTY = DavPropertyName.create("search-result-property", ItemResourceConstants.NAMESPACE);
+
+    private final String[] columnNames;
+    private final Value[] values;
+
+    /**
+     * Creates a new <code>SearchResultProperty</code>.
+     *
+     * @param columnNames the column names of the search row represented by this
+     * dav property.
+     * @param values the values present in the columns
+     */
+    public SearchResultProperty(String[] columnNames, Value[] values) {
+        super(SEARCH_RESULT_PROPERTY, true);
+        this.columnNames = columnNames;
+        this.values = values;
+    }
+
+    /**
+     * Wrap the specified <code>DavProperty</code> in a new <code>SearchResultProperty</code>.
+     *
+     * @param property
+     * @throws RepositoryException if an error occurs while build the property value
+     * @throws IllegalArgumentException if the specified property does have the
+     * required form.
+     */
+    public SearchResultProperty(DavProperty property) throws RepositoryException {
+        super(SEARCH_RESULT_PROPERTY, true);
+        if (!SEARCH_RESULT_PROPERTY.equals(property.getName())) {
+	    throw new IllegalArgumentException("SearchResultProperty may only be created with a property that has name="+SEARCH_RESULT_PROPERTY.getName());
+	}
+
+        List colList = new ArrayList();
+        List valList = new ArrayList();
+
+        if (property.getValue() instanceof List) {
+            List l = (List) property.getValue();
+
+            String name = null;
+            String value = null;
+            int i = 0;
+            Iterator elemIt = l.iterator();
+            while (elemIt.hasNext()) {
+                Object el = elemIt.next();
+                if (el instanceof Element) {
+                    String txt = ((Element)el).getText();
+                    if (JCR_NAME.getName().equals(((Element)el).getName())) {
+                        name = txt;
+                    } else if (JCR_VALUE.getName().equals(((Element)el).getName())) {
+                        value = txt;
+                    } else if (JCR_TYPE.getName().equals(((Element)el).getName())) {
+                        int type = PropertyType.valueFromName(txt);
+                        if (name == null) {
+                            throw new IllegalArgumentException("SearchResultProperty requires a set of 'jcr:name','jcr:value' and 'jcr:type' xml elements.");
+                        }
+                        colList.add(name);
+                        valList.add((value == null) ? null : ValueHelper.deserialize(value, type, false));
+                        // reset...
+                        name = null;
+                        value = null;
+                        i++;
+                    }
+                }
+            }
+        } else {
+            new IllegalArgumentException("SearchResultProperty requires a set of 'jcr:name','jcr:value' and 'jcr:type' xml elements.");
+        }
+
+        columnNames = (String[]) colList.toArray(new String[colList.size()]);
+        values = (Value[]) valList.toArray(new Value[valList.size()]);
+    }
+
+    /**
+     * Return the column names representing the names of the properties present
+     * in the {@link #getValues() values}.
+     *
+     * @return columnNames
+     */
+    public String[] getColumnNames() {
+        return columnNames;
+    }
+
+    /**
+     * Return the values representing the values of that row in the search
+     * result table.
+     *
+     * @return values
+     * @see javax.jcr.query.Row#getValues()
+     */
+    public Value[] getValues() {
+        return values;
+    }
+
+    /**
+     * Return the value of this webdav property i.e. an list of xml
+     * {@link Element}s. For every value in the query result row represented by
+     * this webdav property a jcr:name, jcr:value and jcr:type element is created.
+     * Example:
+     * <pre>
+     * -----------------------------------------------------------
+     *   col-name  |   bla   |   bli   |  jcr:path  |  jcr:score
+     * -----------------------------------------------------------
+     *   value     |   xxx   |   111   |  /aNode    |    1
+     *   type      |    1    |    3    |     8      |    3
+     * -----------------------------------------------------------
+     * </pre>
+     * results in:
+     * <pre>
+     * &lt;jcr:name&gt;bla&lt;jcr:name/&gt;
+     * &lt;jcr:value&gt;xxx&lt;jcr:value/&gt;
+     * &lt;jcr:type&gt;String&lt;jcr:value/&gt;
+     * &lt;jcr:name&gt;bli&lt;jcr:name/&gt;
+     * &lt;jcr:value&gt;111&lt;jcr:value/&gt;
+     * &lt;jcr:type&gt;Long&lt;jcr:value/&gt;
+     * &lt;jcr:name&gt;jcr:path&lt;jcr:name/&gt;
+     * &lt;jcr:value&gt;/aNode&lt;jcr:value/&gt;
+     * &lt;jcr:type&gt;Path&lt;jcr:value/&gt;
+     * &lt;jcr:name&gt;jcr:score&lt;jcr:name/&gt;
+     * &lt;jcr:value&gt;1&lt;jcr:value/&gt;
+     * &lt;jcr:type&gt;Long&lt;jcr:value/&gt;
+     * </pre>
+     *
+     * @return value of this webdav property consisting of an list of xml elements.
+     * @see org.apache.jackrabbit.webdav.property.DavProperty#getValue()
+     */
+    public Object getValue() {
+        List value = new ArrayList();
+        for (int i = 0; i < columnNames.length; i++) {
+            String propertyName = columnNames[i];
+            Value propertyValue = values[i];
+            String valueStr = null;
+            if (propertyValue != null) {
+                try {
+                    valueStr = propertyValue.getString();
+                } catch (RepositoryException e) {
+                    log.error(e.getMessage());
+                }
+            }
+            String type = (propertyValue == null) ? PropertyType.TYPENAME_STRING : PropertyType.nameFromValue(propertyValue.getType());
+
+            value.add(JCR_NAME.toXml().setText(propertyName));
+            value.add(JCR_VALUE.toXml().setText(valueStr));
+            value.add(JCR_TYPE.toXml().setText(type));
+        }
+        return value;
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResultProperty.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/search/SearchResultProperty.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/TxLockManagerImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/TxLockManagerImpl.java?view=auto&rev=161660
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/TxLockManagerImpl.java (added)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/TxLockManagerImpl.java Sun Apr 17 07:51:59 2005
@@ -0,0 +1,671 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.jcr.transaction;
+
+import org.apache.log4j.Logger;
+import org.apache.jackrabbit.webdav.*;
+import org.apache.jackrabbit.webdav.jcr.JcrDavException;
+import org.apache.jackrabbit.webdav.transaction.*;
+import org.apache.jackrabbit.webdav.util.Text;
+import org.apache.jackrabbit.webdav.lock.*;
+import org.apache.jackrabbit.core.XASession;
+
+import javax.jcr.*;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.Xid;
+import java.util.*;
+
+/**
+ * <code>TxLockManagerImpl</code> manages locks with locktype
+ * '{@link TransactionConstants#TRANSACTION jcr:transaction}'.
+ *
+ * todo: removing all expired locks
+ * todo: 'local' and 'global' are not accurate terms in the given context > replace
+ * todo: the usage of the 'global' transaction is not according to the JTA specification,
+ * which explicitely requires any transaction present on a servlet to be completed before
+ * the service method returns. Starting/completing transactions on the session object,
+ * which is possible with the jackrabbit implementation is a hack.
+ * todo: review of this transaction part is therefore required. Is there a use-case
+ * for those 'global' transactions at all...
+ */
+public class TxLockManagerImpl implements TxLockManager {
+
+    private static Logger log = Logger.getLogger(TxLockManagerImpl.class);
+
+    private TransactionMap map = new TransactionMap();
+
+    /**
+     * Create a new lock.
+     *
+     * @param lockInfo as present in the request body.
+     * @param resource
+     * @return the lock
+     * @throws DavException if the lock could not be obtained.
+     * @throws IllegalArgumentException if the resource is <code>null</code> or
+     * does not implement {@link TransactionResource} interface.
+     * @see LockManager#createLock(org.apache.jackrabbit.webdav.lock.LockInfo, org.apache.jackrabbit.webdav.DavResource)
+     */
+    public ActiveLock createLock(LockInfo lockInfo, DavResource resource)
+	    throws DavException {
+	if (resource == null || !(resource instanceof TransactionResource)) {
+	    throw new IllegalArgumentException("Invalid resource");
+	}
+	return createLock(lockInfo, (TransactionResource)resource);
+    }
+
+    /**
+     * Create a new lock.
+     *
+     * @param lockInfo
+     * @param resource
+     * @return the lock
+     * @throws DavException if the request lock has the wrong lock type or if
+     * the lock could not be obtained for any reason.
+     */
+    private synchronized ActiveLock createLock(LockInfo lockInfo, TransactionResource resource)
+	    throws DavException {
+	if (!lockInfo.isDeep() || !TransactionConstants.TRANSACTION.equals(lockInfo.getType())) {
+	    throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
+	}
+
+	ActiveLock existing = getLock(lockInfo.getType(), lockInfo.getScope(), resource);
+	if (existing != null) {
+	    throw new DavException(DavServletResponse.SC_LOCKED);
+	}
+	// TODO: check for locks on member resources is required as well for lock is always deep!
+
+	Transaction tx = createTransaction(resource.getLocator(), lockInfo);
+	tx.start(resource);
+
+	// keep references to this lock
+	addReferences(tx, getMap(resource), resource);
+
+	return tx.getLock();
+    }
+
+    /**
+     * Build the transaction object associated by the lock.
+     *
+     * @param locator
+     * @param lockInfo
+     * @return
+     */
+    private Transaction createTransaction(DavResourceLocator locator, LockInfo lockInfo) {
+	if (TransactionConstants.GLOBAL.equals(lockInfo.getScope())) {
+	    return new GlobalTransaction(locator, new TxActiveLock(lockInfo));
+	} else {
+	    return new LocalTransaction(locator, new TxActiveLock(lockInfo));
+	}
+    }
+
+    /**
+     * Refresh the lock indentified by the given lock token.
+     *
+     * @param lockInfo
+     * @param lockToken
+     * @param resource
+     * @return the lock
+     * @throws DavException
+     * @throws IllegalArgumentException if the resource is <code>null</code> or
+     * does not implement {@link TransactionResource} interface.
+     * @see LockManager#refreshLock(org.apache.jackrabbit.webdav.lock.LockInfo, String, org.apache.jackrabbit.webdav.DavResource)
+     */
+    public ActiveLock refreshLock(LockInfo lockInfo, String lockToken,
+				  DavResource resource) throws DavException {
+	if (resource == null || !(resource instanceof TransactionResource)) {
+	    throw new IllegalArgumentException("Invalid resource");
+	}
+	return refreshLock(lockInfo, lockToken, (TransactionResource)resource);
+    }
+
+    /**
+     * Reset the timeout of the lock identified by the given lock token.
+     *
+     * @param lockInfo
+     * @param lockToken
+     * @param resource
+     * @return
+     * @throws DavException if the lockdid not exist or is expired.
+     */
+    private synchronized ActiveLock refreshLock(LockInfo lockInfo, String lockToken,
+						TransactionResource resource) throws DavException {
+
+	TransactionMap responsibleMap = getMap(resource);
+	Transaction tx = responsibleMap.get(lockToken);
+	if (tx == null) {
+	    throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "No valid transaction lock found for resource '" + resource.getResourcePath()+ "'");
+	} else if (tx.getLock().isExpired()) {
+	    removeExpired(tx, responsibleMap, resource);
+	    throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Transaction lock for resource '" + resource.getResourcePath()+ "' was already expired.");
+	} else {
+	    tx.getLock().setTimeout(lockInfo.getTimeout());
+	}
+	return tx.getLock();
+    }
+
+    /**
+     * Throws UnsupportedOperationException.
+     *
+     * @param lockToken
+     * @param resource
+     * @throws DavException
+     * @see LockManager#releaseLock(String, org.apache.jackrabbit.webdav.DavResource)
+     */
+    public void releaseLock(String lockToken, DavResource resource)
+	    throws DavException {
+	throw new UnsupportedOperationException("A transaction lock can only be release with a TransactionInfo object and a lock token.");
+    }
+
+    /**
+     * Release the lock identified by the given lock token.
+     *
+     * @param lockInfo
+     * @param lockToken
+     * @param resource
+     * @throws DavException
+     */
+    public synchronized void releaseLock(TransactionInfo lockInfo, String lockToken,
+					 TransactionResource resource) throws DavException {
+	if (resource == null) {
+	    throw new IllegalArgumentException("Resource must not be null.");
+	}
+	TransactionMap responsibleMap = getMap(resource);
+	Transaction tx = responsibleMap.get(lockToken);
+
+	if (tx == null) {
+	    throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "No transaction lock found for resource '" + resource.getResourcePath()+ "'");
+	} else if (tx.getLock().isExpired()) {
+	    removeExpired(tx, responsibleMap, resource);
+	    throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Transaction lock for resource '" + resource.getResourcePath()+ "' was already expired.");
+	} else {
+	    if (TransactionConstants.XML_COMMIT.equals(lockInfo.getStatus())) {
+		tx.commit(resource);
+	    } else {
+		tx.rollback(resource);
+	    }
+	    removeReferences(tx, responsibleMap, resource);
+	}
+    }
+
+    /**
+     * Always returns null
+     *
+     * @param type
+     * @param scope
+     * @param resource
+     * @return null
+     * @see #getLock(Type, Scope, TransactionResource)
+     * @see LockManager#getLock(org.apache.jackrabbit.webdav.lock.Type, org.apache.jackrabbit.webdav.lock.Scope, org.apache.jackrabbit.webdav.DavResource)
+     */
+    public ActiveLock getLock(Type type, Scope scope, DavResource resource) {
+	return null;
+    }
+
+    /**
+     * Return the lock applied to the given resource or <code>null</code>
+     *
+     * @param type
+     * @param scope
+     * @param resource
+     * @return lock applied to the given resource or <code>null</code>
+     * @see LockManager#getLock(Type, Scope, DavResource)
+     * todo: is it correct to return one that specific lock, the current session is token-holder of?
+     */
+    public ActiveLock getLock(Type type, Scope scope, TransactionResource resource) {
+	ActiveLock lock = null;
+	if (TransactionConstants.TRANSACTION.equals(type)) {
+	    String[] sessionTokens = resource.getSession().getRepositorySession().getLockTokens();
+	    int i = 0;
+	    while (lock == null && i < sessionTokens.length) {
+		String lockToken = sessionTokens[i];
+		lock = getLock(lockToken, scope, resource);
+		i++;
+	    }
+	}
+	return lock;
+    }
+
+    /**
+     * @param lockToken
+     * @param resource
+     * @return
+     */
+    private ActiveLock getLock(String lockToken, Scope scope, DavResource resource) {
+	if (!(resource instanceof TransactionResource)) {
+	    log.info("");
+	    return null;
+	}
+
+	ActiveLock lock = null;
+	Transaction tx = null;
+	TransactionMap m = map;
+	// check if main-map contains that txId
+	if (m.containsKey(lockToken)) {
+	    tx = m.get(lockToken);
+	} else {
+	    // look through all the nested tx-maps (i.e. global txs) for the given txId
+	    Iterator it = m.values().iterator();
+	    while (it.hasNext() && tx == null) {
+		Transaction txMap = (Transaction) it.next();
+		if (!txMap.isLocal()) {
+		    m = ((TransactionMap)txMap);
+		    if (m.containsKey(lockToken)) {
+			tx = ((TransactionMap)txMap).get(lockToken);
+		    }
+		}
+	    }
+	}
+
+	if (tx != null) {
+	    if (tx.getLock().isExpired()) {
+		removeExpired(tx, m, (TransactionResource)resource);
+	    } else if (tx.appliesToResource(resource) && (scope == null || tx.getLock().getScope().equals(scope))) {
+		lock = tx.getLock();
+	    }
+	}
+	return lock;
+    }
+
+    /**
+     * Returns true if the given lock token belongs to a lock that applies to
+     * the given resource, false otherwise. The token may either be retrieved
+     * from the {@link DavConstants#HEADER_LOCK_TOKEN Lock-Token header} or
+     * from the {@link TransactionConstants#HEADER_TRANSACTIONID TransactionId header}.
+     *
+     * @param token
+     * @param resource
+     * @return
+     * @see LockManager#hasLock(String token, DavResource resource)
+     */
+    public boolean hasLock(String token, DavResource resource) {
+	return getLock(token, null, resource) != null;
+    }
+
+    /**
+     * Return the map that may contain a transaction lock for the given resource.
+     * In case the resource provides a transactionId, the map must be a
+     * repository transaction that is identified by the given id and which in
+     * turn can act as map.
+     *
+     * @param resource
+     * @return responsible map.
+     * @throws DavException if no map could be retrieved.
+     */
+    private TransactionMap getMap(TransactionResource resource)
+	    throws DavException {
+
+	String txKey = resource.getTransactionId();
+	if (txKey == null) {
+	    return map;
+	} else {
+	    if (!map.containsKey(txKey)) {
+		throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Transaction map '" + map + " does not contain a transaction with TransactionId '" + txKey + "'.");
+	    }
+	    Transaction tx = map.get(txKey);
+	    if (tx.isLocal()) {
+		throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "TransactionId '" + txKey + "' points to a local transaction, that cannot act as transaction map");
+	    } else if (tx.getLock() != null && tx.getLock().isExpired()) {
+		removeExpired(tx, map, resource);
+		throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Attempt to retrieve an expired global transaction.");
+	    }
+	    // tx is a global transaction that acts as map as well.
+	    return (TransactionMap)tx;
+	}
+    }
+
+    /**
+     * Rollbacks the specified transaction and releases the lock. This includes
+     * the removal of all references.
+     *
+     * @param tx
+     * @param responsibleMap
+     * @param resource
+     */
+    private static void removeExpired(Transaction tx, TransactionMap responsibleMap,
+				      TransactionResource resource) {
+	log.info("Removing expired transaction lock " + tx);
+	try {
+	    tx.rollback(resource);
+	    removeReferences(tx, responsibleMap, resource);
+	} catch (DavException e) {
+	    log.error("Error while removing expired transaction lock: " + e.getMessage());
+	}
+    }
+
+    /**
+     * Create the required references to the new transaction specified by tx.
+     *
+     * @param tx
+     * @param responsibleMap
+     * @param resource
+     * @throws DavException
+     */
+    private static void addReferences(Transaction tx, TransactionMap responsibleMap,
+				      TransactionResource resource) throws DavException {
+	log.info("Adding transactionId '" + tx.getId() + "' as session lock token.");
+	resource.getSession().getRepositorySession().addLockToken(tx.getId());
+
+	responsibleMap.put(tx.getId(), tx);
+	resource.getSession().addReference(tx.getId());
+    }
+
+    /**
+     * Remove all references to the specified transaction.
+     *
+     * @param tx
+     * @param responsibleMap
+     * @param resource
+     */
+    private static void removeReferences(Transaction tx, TransactionMap responsibleMap,
+					 TransactionResource resource) {
+	log.info("Removing transactionId '" + tx.getId() + "' from session lock tokens.");
+	resource.getSession().getRepositorySession().removeLockToken(tx.getId());
+
+	responsibleMap.remove(tx.getId());
+	resource.getSession().removeReference(tx.getId());
+    }
+    //------------------------------------------< inner classes, interfaces >---
+    /**
+     * Internal <code>Transaction</code> interface
+     */
+    private interface Transaction {
+
+	TxActiveLock getLock();
+
+	/**
+	 * @return the id of this transaction.
+	 */
+	String getId();
+
+	/**
+	 * @return path of the lock holding resource
+	 */
+	String getResourcePath();
+
+	/**
+	 * @param resource
+	 * @return true if the lock defined by this transaction applies to the
+	 * given resource, either due to the resource holding that lock or due
+	 * to a deep lock hold by any ancestor resource.
+	 */
+	boolean appliesToResource(DavResource resource);
+
+	/**
+	 * @return true if this transaction is used to allow for transient changes
+	 * on the underlaying repository, that may be persisted with the final
+	 * UNLOCK request only.
+	 */
+	boolean isLocal();
+
+	/**
+	 * Start this transaction.
+	 *
+	 * @param resource
+	 * @throws DavException if an error occurs.
+	 */
+	void start(TransactionResource resource) throws DavException ;
+
+	/**
+	 * Commit this transaction
+	 *
+	 * @param resource
+	 * @throws DavException if an error occurs.
+	 */
+	void commit(TransactionResource resource) throws DavException ;
+
+	/**
+	 * Rollback this transaction.
+	 *
+	 * @param resource
+	 * @throws DavException if an error occurs.
+	 */
+	void rollback(TransactionResource resource) throws DavException ;
+    }
+
+    /**
+     * Abstract transaction covering functionally to both implementations.
+     */
+    private abstract static class AbstractTransaction extends TransactionMap implements Transaction {
+
+	private final DavResourceLocator locator;
+	private final TxActiveLock lock;
+
+	private AbstractTransaction(DavResourceLocator locator, TxActiveLock lock) {
+	    this.locator = locator;
+	    this.lock = lock;
+	}
+
+	/**
+	 * @see #getLock()
+	 */
+	public TxActiveLock getLock() {
+	    return lock;
+	}
+
+	/**
+	 * @see #getId()
+	 */
+	public String getId() {
+	    return lock.getToken();
+	}
+
+	/**
+	 * @see #getResourcePath()
+	 */
+	public String getResourcePath() {
+	    return locator.getResourcePath();
+	}
+
+	/**
+	 * @see #appliesToResource(DavResource)
+	 */
+	public boolean appliesToResource(DavResource resource) {
+	    if (locator.isSameWorkspace(resource.getLocator())) {
+		String lockResourcePath = getResourcePath();
+		String resPath = resource.getResourcePath();
+
+		while (!"".equals(resPath)) {
+		    if (lockResourcePath.equals(resPath)) {
+			return true;
+		    }
+		    resPath = Text.getRelativeParent(resPath, 1);
+		}
+	    }
+	    return false;
+	}
+    }
+
+    /**
+     *
+     */
+    private final static class LocalTransaction extends AbstractTransaction {
+
+	private LocalTransaction(DavResourceLocator locator, TxActiveLock lock) {
+	    super(locator, lock);
+	}
+
+	public boolean isLocal() {
+	    return true;
+	}
+
+        public void start(TransactionResource resource) throws DavException {
+            try {
+                // make sure, the given resource represents an existing repository item
+                if (!resource.getSession().getRepositorySession().itemExists(getResourcePath())) {
+                    throw new DavException(DavServletResponse.SC_CONFLICT, "Unable to start local transaction: no repository item present at "+getResourcePath());
+                }
+            } catch (RepositoryException e) {
+                log.error("Unexpected error: " + e.getMessage());
+                throw new JcrDavException(e);
+            }
+        }
+
+	public void commit(TransactionResource resource) throws DavException {
+	    try {
+		DavSession session = resource.getSession();
+		session.getRepositorySession().getItem(getResourcePath()).save();
+	    } catch (RepositoryException e) {
+		throw new JcrDavException(e);
+	    }
+	}
+
+	public void rollback(TransactionResource resource) throws DavException  {
+	    try {
+		DavSession session = resource.getSession();
+		session.getRepositorySession().getItem(getResourcePath()).refresh(false);
+	    } catch (RepositoryException e) {
+		throw new JcrDavException(e);
+	    }
+	}
+
+	public Transaction put(String key, Transaction value) throws DavException {
+	    throw new DavException(WebdavResponse.SC_PRECONDITION_FAILED, "Attempt to nest a new transaction into a local one.");
+	}
+    }
+
+    /**
+     *
+     */
+    private static class GlobalTransaction extends AbstractTransaction {
+
+	private Xid xid;
+
+	private GlobalTransaction(DavResourceLocator locator, TxActiveLock lock) {
+	    super(locator, lock);
+	    xid = new XidImpl(lock.getToken());
+	}
+
+	public boolean isLocal() {
+	    return false;
+	}
+
+	public void start(TransactionResource resource) throws DavException {
+	    XAResource xaRes = getXAResource(resource);
+	    try {
+		xaRes.setTransactionTimeout((int)getLock().getTimeout()/1000);
+		xaRes.start(xid, XAResource.TMNOFLAGS);
+	    } catch (XAException e) {
+		throw new DavException(DavServletResponse.SC_FORBIDDEN, e.getMessage());
+	    }
+	}
+
+	public void commit(TransactionResource resource) throws DavException {
+	    XAResource xaRes = getXAResource(resource);
+	    try {
+		xaRes.commit(xid, false);
+		removeLocalTxReferences(resource);
+	    } catch (XAException e) {
+		throw new DavException(DavServletResponse.SC_FORBIDDEN, e.getMessage());
+	    }
+	}
+
+	public void rollback(TransactionResource resource) throws DavException {
+	    XAResource xaRes = getXAResource(resource);
+	    try {
+		xaRes.rollback(xid);
+		removeLocalTxReferences(resource);
+	    } catch (XAException e) {
+		throw new DavException(DavServletResponse.SC_FORBIDDEN, e.getMessage());
+	    }
+	}
+
+	private XAResource getXAResource(TransactionResource resource) throws DavException {
+	    Session session = resource.getSession().getRepositorySession();
+	    if (session instanceof XASession) {
+		return ((XASession)session).getXAResource();
+	    } else {
+		throw new DavException(DavServletResponse.SC_FORBIDDEN);
+	    }
+	}
+
+	private void removeLocalTxReferences(TransactionResource resource) {
+	    Iterator it = values().iterator();
+	    while (it.hasNext()) {
+		Transaction tx = (Transaction) it.next();
+		removeReferences(tx, this, resource);
+	    }
+	}
+
+	public Transaction put(String key, Transaction value) throws DavException {
+	    if (!(value instanceof LocalTransaction)) {
+		throw new DavException(WebdavResponse.SC_PRECONDITION_FAILED, "Attempt to nest global transaction into a global one.");
+	    }
+	    return (Transaction) super.put(key, value);
+	}
+    }
+
+    /**
+     *
+     */
+    private static class TransactionMap extends HashMap {
+
+	public Transaction get(String key) {
+	    Transaction tx = null;
+	    if (containsKey(key)) {
+		tx = (Transaction) super.get(key);
+	    }
+	    return tx;
+	}
+
+	public Transaction put(String key, Transaction value) throws DavException {
+	    // any global an local transactions allowed.
+	    return (Transaction) super.put(key, value);
+	}
+    }
+
+    /**
+     * Private class implementing Xid interface.
+     */
+    private static class XidImpl implements Xid {
+
+	private final String id;
+
+	/**
+	 * Create a new Xid
+	 *
+	 * @param id
+	 */
+	private XidImpl(String id) {
+	    this.id = id;
+	}
+
+	/**
+	 * @return 1
+	 * @see javax.transaction.xa.Xid#getFormatId()
+	 */
+	public int getFormatId() {
+	    // todo: define reasonable format id
+	    return 1;
+	}
+
+	/**
+	 * @return an empty byte array.
+	 * @see javax.transaction.xa.Xid#getBranchQualifier()
+	 */
+	public byte[] getBranchQualifier() {
+	    return new byte[0];
+	}
+
+	/**
+	 * @return id as byte array
+	 * @see javax.transaction.xa.Xid#getGlobalTransactionId()
+	 */
+	public byte[] getGlobalTransactionId() {
+	    return id.getBytes();
+	}
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/TxLockManagerImpl.java
------------------------------------------------------------------------------
    svn = 

Propchange: incubator/jackrabbit/trunk/contrib/jcr-server/webdav/src/java/org/apache/jackrabbit/webdav/jcr/transaction/TxLockManagerImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message