You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by re...@apache.org on 2020/07/09 11:40:40 UTC

svn commit: r1879702 - in /jackrabbit/branches/2.20: ./ jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/...

Author: reschke
Date: Thu Jul  9 11:40:40 2020
New Revision: 1879702

URL: http://svn.apache.org/viewvc?rev=1879702&view=rev
Log:
JCR-4166: support GZIP content coding in requests (merged r1878706 into 2.20)

Added:
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/ContentCodingAwareRequest.java
      - copied unchanged from r1878706, jackrabbit/trunk/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/ContentCodingAwareRequest.java
Modified:
    jackrabbit/branches/2.20/   (props changed)
    jackrabbit/branches/2.20/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavException.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/package-info.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/package-info.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java
    jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/package-info.java

Propchange: jackrabbit/branches/2.20/
------------------------------------------------------------------------------
  Merged /jackrabbit/trunk:r1878706

Modified: jackrabbit/branches/2.20/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java Thu Jul  9 11:40:40 2020
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.webdav.server;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -25,7 +24,7 @@ import java.util.zip.GZIPOutputStream;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.message.BasicHeader;
 import org.apache.jackrabbit.webdav.DavConstants;
@@ -54,7 +53,7 @@ public class ContentCodingTest extends W
             entity.setContentEncoding(new BasicHeader("Content-Encoding", "qux"));
             put.setEntity(entity);
             status = this.client.execute(put, this.context).getStatusLine().getStatusCode();
-            assertTrue("server must signal error for unknown content coding", status == 415);
+            assertTrue("server must signal error for unknown content coding, got: " + status, status == 415);
         } finally {
             if (status / 2 == 100) {
                 delete(testUri);
@@ -68,22 +67,19 @@ public class ContentCodingTest extends W
         try {
             byte bytes[] = "foobarfoobarfoobar".getBytes("UTF-8");
             HttpPut put = new HttpPut(testUri);
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            OutputStream gos = new GZIPOutputStream(bos);
-            gos.write(bytes);
-            gos.flush();
-            assertTrue(bos.toByteArray().length != bytes.length);
-            InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(bos.toByteArray()));
+            byte gzbytes[] = asGzipOctets(bytes);
+            assertTrue(gzbytes.length != bytes.length);
+            ByteArrayEntity entity = new ByteArrayEntity(gzbytes);
             entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip"));
             put.setEntity(entity);
             status = this.client.execute(put, this.context).getStatusLine().getStatusCode();
-            assertTrue("server create or signal error", status == 201 || status == 415);
+            assertTrue("server create or signal error, got: " + status, status == 201 || status == 415);
             if (status / 2 == 100) {
                 // check length
                 HttpHead head = new HttpHead(testUri);
                 HttpResponse response = this.client.execute(head, this.context);
                 assertEquals(200, response.getStatusLine().getStatusCode());
-                assertEquals(bytes.length, response.getFirstHeader("Content-Length").getValue());
+                assertEquals(bytes.length, Integer.parseInt(response.getFirstHeader("Content-Length").getValue()));
             }
         } finally {
             if (status / 2 == 100) {
@@ -98,13 +94,59 @@ public class ContentCodingTest extends W
         assertEquals(207, status);
     }
 
+    private static String PF = "<D:propfind xmlns:D=\"DAV:\"><D:prop><D:resourcetype/></D:prop></D:propfind>";
+
     public void testPropfindUnknownContentCoding() throws IOException {
         HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0);
-        StringEntity entity = new StringEntity(
-                "<D:propfind xmlns:D=\"DAV:\"><D:prop xmlns:R=\"http://ns.example.com/boxschema/\"><R:bigbox/></D:prop></D:propfind>");
+        StringEntity entity = new StringEntity(PF);
         entity.setContentEncoding(new BasicHeader("Content-Encoding", "qux"));
         propfind.setEntity(entity);
+        HttpResponse response = this.client.execute(propfind, this.context);
+        int status = response.getStatusLine().getStatusCode();
+        assertTrue("server must signal error for unknown content coding, got: " + status, status == 415);
+        assertEquals("gzip", response.getFirstHeader("Accept").getValue());
+    }
+
+    public void testPropfindGzipContentCoding() throws IOException {
+        HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0);
+        ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(PF));
+        entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip"));
+        propfind.setEntity(entity);
+        int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode();
+        assertEquals(207, status);
+    }
+
+    // double encoded, empty list member in field value, mixed upper/lower in
+    // coding name
+    public void testPropfindGzipContentCodingTwice() throws IOException {
+        HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0);
+        ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(asGzipOctets(PF)));
+        entity.setContentEncoding(new BasicHeader("Content-Encoding", "gziP,, Gzip"));
+        propfind.setEntity(entity);
         int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode();
-        assertTrue("server must signal error for unknown content coding", status == 415);
+        assertEquals(207, status);
+    }
+
+    // double encoded, but only when encoding in header field
+    public void testPropfindGzipContentCodingBadSpec() throws IOException {
+        HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0);
+        ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(asGzipOctets(PF)));
+        entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip"));
+        propfind.setEntity(entity);
+        int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode();
+        assertEquals(400, status);
+    }
+
+    private static byte[] asGzipOctets(String input) throws IOException {
+        return asGzipOctets(input.getBytes("UTF-8"));
+    }
+
+    private static byte[] asGzipOctets(byte[] input) throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        OutputStream gos = new GZIPOutputStream(bos);
+        gos.write(input);
+        gos.flush();
+        gos.close();
+        return bos.toByteArray();
     }
 }

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavException.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavException.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavException.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavException.java Thu Jul  9 11:40:40 2020
@@ -139,6 +139,15 @@ public class DavException extends Except
     }
 
     /**
+     * Return the error condition attached to this <code>DavException</code>.
+     *
+     * @return errorCondition
+     */
+    public Element getErrorCondition() {
+        return errorCondition;
+    }
+
+    /**
      * Returns a DAV:error element containing the error condition or
      * <code>null</code> if no specific condition is available. See
      * <a href="http://www.ietf.org/rfc/rfc3253.txt">RFC 3253</a>
@@ -163,4 +172,4 @@ public class DavException extends Except
             return null;
         }
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java Thu Jul  9 11:40:40 2020
@@ -30,12 +30,15 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.zip.GZIPInputStream;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.jackrabbit.webdav.bind.BindInfo;
@@ -62,6 +65,7 @@ import org.apache.jackrabbit.webdav.prop
 import org.apache.jackrabbit.webdav.property.DavPropertySet;
 import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
 import org.apache.jackrabbit.webdav.property.PropEntry;
+import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet;
 import org.apache.jackrabbit.webdav.transaction.TransactionConstants;
 import org.apache.jackrabbit.webdav.transaction.TransactionInfo;
 import org.apache.jackrabbit.webdav.version.LabelInfo;
@@ -80,7 +84,7 @@ import org.xml.sax.SAXException;
 /**
  * <code>WebdavRequestImpl</code>...
  */
-public class WebdavRequestImpl implements WebdavRequest, DavConstants {
+public class WebdavRequestImpl implements WebdavRequest, DavConstants, ContentCodingAwareRequest {
 
     private static Logger log = LoggerFactory.getLogger(WebdavRequestImpl.class);
 
@@ -308,7 +312,7 @@ public class WebdavRequestImpl implement
         }
         // try to parse the request body
         try {
-            InputStream in = httpRequest.getInputStream();
+            InputStream in = getDecodedInputStream(httpRequest);
             if (in != null) {
                 // use a buffered input stream to find out whether there actually
                 // is a request body
@@ -324,7 +328,8 @@ public class WebdavRequestImpl implement
             if (log.isDebugEnabled()) {
                 log.debug("Unable to build an XML Document from the request body: " + e.getMessage());
             }
-            throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+            Throwable cause = e.getCause();
+            throw (cause instanceof DavException) ? (DavException) cause : new DavException(DavServletResponse.SC_BAD_REQUEST);
         } catch (ParserConfigurationException e) {
             if (log.isDebugEnabled()) {
                 log.debug("Unable to build an XML Document from the request body: " + e.getMessage());
@@ -367,6 +372,39 @@ public class WebdavRequestImpl implement
         return propfindProps;
     }
 
+    private static InputStream getDecodedInputStream(HttpServletRequest request) throws IOException {
+        List<String> contentCodings = AbstractWebdavServlet.getContentCodings(request);
+        int len = contentCodings.size();
+
+        log.trace("content codings: " + contentCodings);
+        InputStream result = request.getInputStream();
+ 
+        for (int i = 1; i <= len; i++) {
+            String s = contentCodings.get(len - i);
+            log.trace("decoding: " + s);
+            if ("gzip".equals(s)) {
+                result = new GZIPInputStream(result);
+            } else {
+                String message = "Unsupported content coding: " + s;
+                try {
+                    Element condition = DomUtil.createElement(
+                            DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(), PRECONDITION_SUPPORTED);
+                    throw new IOException(
+                            new DavException(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message, null, condition));
+                } catch (ParserConfigurationException ex) {
+                    throw new IOException(message);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public String getAcceptableCodings() {
+        return "gzip";
+    }
+
     /**
      * Parse the propfind request body in order to determine the type of the propfind
      * and the set of requested property.
@@ -940,7 +978,7 @@ public class WebdavRequestImpl implement
     }
 
     public ServletInputStream getInputStream() throws IOException {
-        return httpRequest.getInputStream();
+        return new MyServletInputStream(getDecodedInputStream(httpRequest));
     }
 
     public String getParameter(String s) {
@@ -1030,4 +1068,78 @@ public class WebdavRequestImpl implement
     public int getLocalPort() {
         return httpRequest.getLocalPort();
     }
+
+    private static class MyServletInputStream extends ServletInputStream {
+
+        private final InputStream delegate;
+
+        public MyServletInputStream(InputStream delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public int available() throws IOException {
+            return delegate.available();
+        }
+
+        @Override
+        public void close() throws IOException {
+            delegate.close();
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return delegate.equals(other);
+        }
+
+        @Override
+        public int hashCode() {
+            return delegate.hashCode();
+        }
+
+        @Override
+        public void mark(int readlimit) {
+            delegate.mark(readlimit);
+        }
+
+        @Override
+        public boolean markSupported() {
+            return delegate.markSupported();
+        }
+
+        @Override
+        public int read() throws IOException {
+            return delegate.read();
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            return delegate.read(b, off, len);
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            return delegate.read(b);
+        }
+
+        @Override
+        public int readLine(byte[] b, int off, int len) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void reset() throws IOException {
+            delegate.reset();
+        }
+
+        @Override
+        public long skip(long n) throws IOException {
+            return delegate.skip(n);
+        }
+
+        @Override
+        public String toString() {
+            return delegate.toString();
+        }
+    }
 }

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/package-info.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/package-info.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/package-info.java Thu Jul  9 11:40:40 2020
@@ -14,5 +14,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@org.osgi.annotation.versioning.Version("1.3.0")
+@org.osgi.annotation.versioning.Version("1.4.0")
 package org.apache.jackrabbit.webdav;

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java Thu Jul  9 11:40:40 2020
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.webdav.server;
 
+import org.apache.jackrabbit.webdav.ContentCodingAwareRequest;
 import org.apache.jackrabbit.webdav.DavCompliance;
 import org.apache.jackrabbit.webdav.DavConstants;
 import org.apache.jackrabbit.webdav.DavException;
@@ -76,9 +77,11 @@ import org.apache.jackrabbit.webdav.vers
 import org.apache.jackrabbit.webdav.version.VersionableResource;
 import org.apache.jackrabbit.webdav.version.report.Report;
 import org.apache.jackrabbit.webdav.version.report.ReportInfo;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -91,6 +94,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * <code>AbstractWebdavServlet</code>
@@ -290,13 +294,15 @@ abstract public class AbstractWebdavServ
 
             // JCR-4165: reject any content-coding in request until we can
             // support it (see JCR-4166)
-            List<String> ces = getContentCodings(request);
-            if (!ces.isEmpty()) {
-                webdavResponse.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
-                webdavResponse.setHeader("Accept-Encoding", "identity");
-                webdavResponse.setContentType("text/plain; charset=UTF-8");
-                webdavResponse.getWriter().println("Content-Encodings not supported, but received: " + ces);
-                webdavResponse.getWriter().flush();
+            if (!(webdavRequest instanceof ContentCodingAwareRequest)) {
+                List<String> ces = getContentCodings(request);
+                if (!ces.isEmpty()) {
+                    webdavResponse.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
+                    webdavResponse.setHeader("Accept-Encoding", "identity");
+                    webdavResponse.setContentType("text/plain; charset=UTF-8");
+                    webdavResponse.getWriter().println("Content-Encodings not supported, but received: " + ces);
+                    webdavResponse.getWriter().flush();
+                }
             }
 
             // check matching if=header for lock-token relevant operations
@@ -308,12 +314,14 @@ abstract public class AbstractWebdavServ
             if (!execute(webdavRequest, webdavResponse, methodCode, resource)) {
                 super.service(request, response);
             }
-
         } catch (DavException e) {
-            if (e.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED) {
-                sendUnauthorized(webdavRequest, webdavResponse, e);
+            handleDavException(webdavRequest, webdavResponse, e);
+        } catch (IOException ex) {
+            Throwable cause = ex.getCause();
+            if (cause instanceof DavException) {
+                handleDavException(webdavRequest, webdavResponse, (DavException) cause);
             } else {
-                webdavResponse.sendError(e);
+                throw ex;
             }
         } finally {
             WebdavRequestContextHolder.clearContext();
@@ -321,6 +329,21 @@ abstract public class AbstractWebdavServ
         }
     }
 
+    private void handleDavException(WebdavRequest webdavRequest, WebdavResponse webdavResponse, DavException ex)
+            throws IOException {
+        if (ex.getErrorCode() == HttpServletResponse.SC_UNAUTHORIZED) {
+            sendUnauthorized(webdavRequest, webdavResponse, ex);
+        } else {
+            Element condition = ex.getErrorCondition();
+            if (DomUtil.matches(condition, ContentCodingAwareRequest.PRECONDITION_SUPPORTED)) {
+                if (webdavRequest instanceof ContentCodingAwareRequest) {
+                    webdavResponse.setHeader("accept", ((ContentCodingAwareRequest) webdavRequest).getAcceptableCodings());
+                }
+            }
+            webdavResponse.sendError(ex);
+        }
+    }
+
     /**
      * Sets the "WWW-Authenticate" header and writes the appropriate error
      * to the given webdav response.
@@ -1415,7 +1438,11 @@ abstract public class AbstractWebdavServ
         return new OutputContextImpl(response, out);
     }
 
-    private List<String> getContentCodings(HttpServletRequest request) {
+    /**
+     * Obtain the (ordered!) list of content codings that have been used in the
+     * request
+     */
+    public static List<String> getContentCodings(HttpServletRequest request) {
         List<String> result = Collections.emptyList();
         for (@SuppressWarnings("unchecked")
         Enumeration<String> ceh = request.getHeaders("Content-Encoding"); ceh.hasMoreElements();) {
@@ -1424,7 +1451,7 @@ abstract public class AbstractWebdavServ
                     if (result.isEmpty()) {
                         result = new ArrayList<String>();
                     }
-                    result.add(h.trim());
+                    result.add(h.trim().toLowerCase(Locale.ENGLISH));
                 }
             }
         }

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/package-info.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/package-info.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/package-info.java Thu Jul  9 11:40:40 2020
@@ -14,5 +14,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@org.osgi.annotation.versioning.Version("1.1.0")
+@org.osgi.annotation.versioning.Version("1.2.0")
 package org.apache.jackrabbit.webdav.server;

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/DomUtil.java Thu Jul  9 11:40:40 2020
@@ -530,6 +530,19 @@ public class DomUtil {
     }
 
     /**
+     * Create a new DOM element with the specified local name and namespace.
+     *
+     * @param factory
+     * @param qName
+     * @return a new DOM element
+     * @see Document#createElement(String)
+     * @see Document#createElementNS(String, String)
+     */
+    public static Element createElement(Document factory, QName elementName) {
+        return factory.createElementNS(elementName.getNamespaceURI(), getPrefixedName(elementName));
+    }
+
+    /**
      * Create a new DOM element with the specified local name and namespace and
      * add the specified text as Text node to it.
      *
@@ -764,15 +777,34 @@ public class DomUtil {
      * @see Document#createElementNS(String, String)
      */
     public static String getPrefixedName(String localName, Namespace namespace) {
-        if (namespace == null
-            || Namespace.EMPTY_NAMESPACE.equals(namespace)
-            || Namespace.EMPTY_NAMESPACE.getPrefix().equals(namespace.getPrefix())) {
+        return getPrefixName(namespace.getURI(), namespace.getPrefix(), localName);
+    }
+
+    /**
+     * Return the qualified name of a DOM node consisting of
+     * namespace prefix + ":" + local name. If the specified namespace is <code>null</code>
+     * or contains an empty prefix, the local name is returned.<br>
+     * NOTE, that this is the value to be used for the 'qualified Name' parameter
+     * expected with the namespace sensitive factory methods.
+     *
+     * @param qName
+     * @return qualified name consisting of prefix, ':' and local name.
+     * @see Document#createAttributeNS(String, String)
+     * @see Document#createElementNS(String, String)
+     */
+    public static String getPrefixedName(QName name) {
+        return getPrefixName(name.getNamespaceURI(), name.getPrefix(), name.getLocalPart());
+    }
+
+    private static String getPrefixName(String namespaceURI, String prefix, String localName) {
+        if (namespaceURI == null || prefix == null || "".equals(namespaceURI) || "".equals(prefix)) {
             return localName;
+        } else {
+            StringBuffer buf = new StringBuffer(prefix);
+            buf.append(":");
+            buf.append(localName);
+            return buf.toString();
         }
-        StringBuffer buf = new StringBuffer(namespace.getPrefix());
-        buf.append(":");
-        buf.append(localName);
-        return buf.toString();
     }
 
     /**

Modified: jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/package-info.java?rev=1879702&r1=1879701&r2=1879702&view=diff
==============================================================================
--- jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/package-info.java (original)
+++ jackrabbit/branches/2.20/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/xml/package-info.java Thu Jul  9 11:40:40 2020
@@ -14,5 +14,5 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@org.osgi.annotation.versioning.Version("1.1.0")
+@org.osgi.annotation.versioning.Version("1.2.0")
 package org.apache.jackrabbit.webdav.xml;