Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Fix for JCR-3535 - Davex remoting should support absolute path hrefs #13

Open
wants to merge 2 commits into from

1 participant

@tmaret

unit test passed successfully
spi2dav integration tests passed successfully

tmaret added some commits
@tmaret tmaret JCR-3535 - Davex remoting should support absolute path hrefs
- use the path instead of the uri as keys for the cache
9a059ff
@tmaret tmaret JCR-3535 - Davex remoting should support absolute path hrefs
- enable LRU policy for the caches
- improved doc
4374a3e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 21, 2013
  1. @tmaret

    JCR-3535 - Davex remoting should support absolute path hrefs

    tmaret authored
    - use the path instead of the uri as keys for the cache
  2. @tmaret

    JCR-3535 - Davex remoting should support absolute path hrefs

    tmaret authored
    - enable LRU policy for the caches
    - improved doc
This page is out of date. Refresh to see the latest.
View
97 jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/IdPathCache.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi2dav;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.jackrabbit.spi.ItemId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>IdPathCache</code> maintains a bidirectional LRU cache from itemId to its path.
+ */
+class IdPathCache {
+
+ private static Logger log = LoggerFactory.getLogger(IdPathCache.class);
+
+ private LRUCache<ItemId, String> idToPathCache;
+ private LRUCache<String, ItemId> pathToIdCache;
+
+ public IdPathCache(int limit) {
+ idToPathCache = new LRUCache<ItemId, String>(limit);
+ pathToIdCache = new LRUCache<String, ItemId>(limit);
+ }
+
+ public ItemId getItemId(String path) {
+ return pathToIdCache.get(path);
+ }
+
+ public String getPath(ItemId itemId) {
+ return idToPathCache.get(itemId);
+ }
+
+ public boolean containsPath(String path) {
+ return pathToIdCache.containsKey(path);
+ }
+
+ public boolean containsItemId(ItemId itemId) {
+ return idToPathCache.containsKey(itemId);
+ }
+
+ public void add(String path, ItemId itemId) {
+ pathToIdCache.put(path, itemId);
+ idToPathCache.put(itemId, path);
+ log.debug("Added: ItemId = " + itemId + " PATH = {}", path);
+ }
+
+ public void remove(String path) {
+ ItemId itemId = pathToIdCache.remove(path);
+ if (itemId != null) {
+ idToPathCache.remove(itemId);
+ }
+ log.debug("Removed: ItemId = " + itemId + " PATH = {}", path);
+ }
+
+ public void remove(ItemId itemId) {
+ String path = idToPathCache.remove(itemId);
+ if (path != null) {
+ pathToIdCache.remove(path);
+ }
+ log.debug("Removed: ItemId = " + itemId + " PATH = {}", path);
+ }
+
+ public void clear() {
+ idToPathCache.clear();
+ pathToIdCache.clear();
+ }
+
+ private class LRUCache<K, V> extends LinkedHashMap<K, V> {
+
+ private final int limit;
+
+ public LRUCache(int limit) {
+ super(16, 0.75f, true);
+ this.limit = limit;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
+ return size() > limit;
+ }
+ }
+}
View
111 jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/IdURICache.java
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.jackrabbit.spi2dav;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import org.apache.jackrabbit.spi.ItemId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * <code>IdURICache</code>...
- */
-class IdURICache {
- private static Logger log = LoggerFactory.getLogger(IdURICache.class);
-
- /**
- * @see <a href="https://issues.apache.org/jira/browse/JCR-3305">JCR-3305</a>: limit cache size
- */
- private static final int CACHESIZE = 10000;
-
- private final String workspaceUri;
- private Map<ItemId, String> idToUriCache;
- private Map<String, ItemId> uriToIdCache;
-
- IdURICache(String workspaceUri) {
- this.workspaceUri = workspaceUri;
- idToUriCache = new LinkedHashMap<ItemId, String>(CACHESIZE, 1) {
- @Override
- protected boolean removeEldestEntry(Map.Entry<ItemId, String> eldest) {
- return this.size() > CACHESIZE;
- }
- };
- uriToIdCache = new LinkedHashMap<String, ItemId>(CACHESIZE, 1) {
- @Override
- protected boolean removeEldestEntry(Map.Entry<String, ItemId> eldest) {
- return this.size() > CACHESIZE;
- }
- };
- }
-
- public ItemId getItemId(String uri) {
- return uriToIdCache.get(getCleanUri(uri));
- }
-
- public String getUri(ItemId itemId) {
- return idToUriCache.get(itemId);
- }
-
- public boolean containsUri(String uri) {
- return uriToIdCache.containsKey(getCleanUri(uri));
- }
-
- public boolean containsItemId(ItemId itemId) {
- return idToUriCache.containsKey(itemId);
- }
-
- public void add(String uri, ItemId itemId) {
- if (!uri.startsWith(workspaceUri)) {
- throw new IllegalArgumentException("Workspace missmatch.");
- }
- String cleanUri = getCleanUri(uri);
- uriToIdCache.put(cleanUri, itemId);
- idToUriCache.put(itemId, cleanUri);
- log.debug("Added: ItemId = " + itemId + " URI = " + cleanUri);
- }
-
- public void remove(String uri) {
- String cleanUri = getCleanUri(uri);
- ItemId itemId = uriToIdCache.remove(cleanUri);
- if (itemId != null) {
- idToUriCache.remove(itemId);
- }
- log.debug("Removed: ItemId = " + itemId + " URI = " + cleanUri);
- }
-
- public void remove(ItemId itemId) {
- String uri = idToUriCache.remove(itemId);
- if (uri != null) {
- uriToIdCache.remove(uri);
- }
- log.debug("Removed: ItemId = " + itemId + " URI = " + uri);
- }
-
- public void clear() {
- idToUriCache.clear();
- uriToIdCache.clear();
- }
-
- private static String getCleanUri(String uri) {
- if (uri.endsWith("/")) {
- return uri.substring(0, uri.length() - 1);
- } else {
- return uri;
- }
- }
-}
View
99 jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/URIResolverImpl.java
@@ -40,11 +40,14 @@
import org.apache.jackrabbit.webdav.property.DavPropertySet;
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 javax.jcr.ItemNotFoundException;
import javax.jcr.RepositoryException;
import java.io.IOException;
+import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
@@ -53,13 +56,20 @@
*/
class URIResolverImpl implements URIResolver {
+ private static Logger log = LoggerFactory.getLogger(URIResolverImpl.class);
+
+ /**
+ * @see <a href="https://issues.apache.org/jira/browse/JCR-3305">JCR-3305</a>: limit cache size
+ */
+ private static final int CACHESIZE = 10000;
+
private final URI repositoryUri;
private final RepositoryServiceImpl service;
private final Document domFactory;
// TODO: to-be-fixed. uri/id-caches don't get updated
// for each workspace a separate idUri-cache is created
- private final Map<String, IdURICache> idURICaches = new HashMap<String, IdURICache>();
+ private final Map<String, IdPathCache> idPathCaches = new HashMap<String, IdPathCache>();
URIResolverImpl(URI repositoryUri, RepositoryServiceImpl service, Document domFactory) {
this.repositoryUri = repositoryUri;
@@ -67,13 +77,14 @@
this.domFactory = domFactory;
}
- private IdURICache getCache(String workspaceName) {
- if (idURICaches.containsKey(workspaceName)) {
- return idURICaches.get(workspaceName);
+ private IdPathCache getCache(String workspaceName) {
+ IdPathCache cache = idPathCaches.get(workspaceName);
+ if (cache != null) {
+ return cache;
} else {
- IdURICache c = new IdURICache(getWorkspaceUri(workspaceName));
- idURICaches.put(workspaceName, c);
- return c;
+ IdPathCache emptyCache = new IdPathCache(CACHESIZE);
+ idPathCaches.put(workspaceName, emptyCache);
+ return emptyCache;
}
}
@@ -95,10 +106,10 @@ String getRootItemUri(String workspaceName) {
String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo)
throws RepositoryException {
- IdURICache cache = getCache(workspaceName);
+ IdPathCache cache = getCache(workspaceName);
// check if uri is available from cache
if (cache.containsItemId(itemId)) {
- return cache.getUri(itemId);
+ return getUri(cache.getPath(itemId));
} else {
StringBuffer uriBuffer = new StringBuffer();
@@ -110,7 +121,7 @@ String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo)
ItemId uuidId = (path == null) ? itemId : service.getIdFactory().createNodeId(uniqueID);
if (path != null && cache.containsItemId(uuidId)) {
// append uri of parent node, that is already cached
- uriBuffer.append(cache.getUri(uuidId));
+ uriBuffer.append(getUri(cache.getPath(uuidId)));
} else {
// try to request locate-by-uuid report to build the uri
ReportInfo rInfo = new ReportInfo(JcrRemotingConstants.REPORT_LOCATE_BY_UUID, ItemResourceConstants.NAMESPACE);
@@ -126,7 +137,7 @@ String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo)
MultiStatus ms = rm.getResponseBodyAsMultiStatus();
if (ms.getResponses().length == 1) {
uriBuffer.append(ms.getResponses()[0].getHref());
- cache.add(ms.getResponses()[0].getHref(), uuidId);
+ cache.add(getPath(ms.getResponses()[0].getHref()), uuidId);
} else {
throw new ItemNotFoundException("Cannot identify item with uniqueID " + uniqueID);
}
@@ -155,7 +166,7 @@ String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo)
}
String itemUri = uriBuffer.toString();
if (!cache.containsItemId(itemId)) {
- cache.add(itemUri, itemId);
+ cache.add(getPath(itemUri), itemId);
}
return itemUri;
}
@@ -163,7 +174,7 @@ String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo)
NodeId buildNodeId(NodeId parentId, MultiStatusResponse response,
String workspaceName, NamePathResolver resolver) throws RepositoryException {
- IdURICache cache = getCache(workspaceName);
+ IdPathCache cache = getCache(workspaceName);
NodeId nodeId;
DavPropertySet propSet = response.getProperties(DavServletResponse.SC_OK);
@@ -181,15 +192,16 @@ NodeId buildNodeId(NodeId parentId, MultiStatusResponse response,
}
}
// cache
- cache.add(response.getHref(), nodeId);
+ cache.add(getPath(response.getHref()), nodeId);
return nodeId;
}
PropertyId buildPropertyId(NodeId parentId, MultiStatusResponse response,
String workspaceName, NamePathResolver resolver) throws RepositoryException {
- IdURICache cache = getCache(workspaceName);
- if (cache.containsUri(response.getHref())) {
- ItemId id = cache.getItemId(response.getHref());
+ IdPathCache cache = getCache(workspaceName);
+ String path = getPath(response.getHref());
+ if (cache.containsPath(path)) {
+ ItemId id = cache.getItemId(path);
if (!id.denotesNode()) {
return (PropertyId) id;
}
@@ -200,22 +212,15 @@ PropertyId buildPropertyId(NodeId parentId, MultiStatusResponse response,
Name name = resolver.getQName(propSet.get(JcrRemotingConstants.JCR_NAME_LN, ItemResourceConstants.NAMESPACE).getValue().toString());
PropertyId propertyId = service.getIdFactory().createPropertyId(parentId, name);
- cache.add(response.getHref(), propertyId);
+ cache.add(getPath(response.getHref()), propertyId);
return propertyId;
} catch (NameException e) {
throw new RepositoryException(e);
}
}
- void clearCacheEntries(ItemId itemId, SessionInfo sessionInfo) {
- IdURICache cache = getCache(sessionInfo.getWorkspaceName());
- if (cache.containsItemId(itemId)) {
- cache.remove(itemId);
- }
- }
-
void clearCacheEntries(SessionInfo sessionInfo) {
- IdURICache cache = getCache(sessionInfo.getWorkspaceName());
+ IdPathCache cache = getCache(sessionInfo.getWorkspaceName());
cache.clear();
}
@@ -233,10 +238,11 @@ private static String getCleanURI(String uri) {
}
private NodeId getNodeId(String uri, SessionInfo sessionInfo, boolean nodeIsGone) throws RepositoryException {
- IdURICache cache = getCache(sessionInfo.getWorkspaceName());
- if (cache.containsUri(uri)) {
+ IdPathCache cache = getCache(sessionInfo.getWorkspaceName());
+ String path = getPath(uri);
+ if (cache.containsPath(path)) {
// id has been accessed before and is cached
- ItemId id = cache.getItemId(uri);
+ ItemId id = cache.getItemId(path);
if (id.denotesNode()) {
return (NodeId) id;
}
@@ -281,6 +287,32 @@ private NodeId getNodeId(String uri, SessionInfo sessionInfo, boolean nodeIsGone
}
}
+ /**
+ * @param uri the uri to be parsed
+ * @return the path (trailing slash removed) extracted from the given uri or <code>null</code> if the uri could not
+ * be parsed.
+ */
+ private String getPath(String uri) {
+ try {
+ String path = new java.net.URI(uri).getPath();
+ if (path.endsWith("/") && ! "/".equals(path)) {
+ return path.substring(0, path.length() - 1);
+ }
+ return path;
+ } catch (URISyntaxException e) {
+ log.warn("Failed to parse the URI = {}", uri);
+ }
+ return null;
+ }
+
+ private String getUri(String path) {
+ String baseUri = getRepositoryUri();
+ if (baseUri.endsWith("/")) {
+ return baseUri.substring(0, baseUri.length() - 1) + Text.escapePath(path);
+ }
+ return baseUri + Text.escapePath(path);
+ }
+
//-------------------------------------------------------< URI resolver >---
/**
* @inheritDoc
@@ -319,9 +351,10 @@ public NodeId getNodeIdAfterEvent(String uri, SessionInfo sessionInfo, boolean n
* @inheritDoc
*/
public PropertyId getPropertyId(String uri, SessionInfo sessionInfo) throws RepositoryException {
- IdURICache cache = getCache(sessionInfo.getWorkspaceName());
- if (cache.containsUri(uri)) {
- ItemId id = cache.getItemId(uri);
+ IdPathCache cache = getCache(sessionInfo.getWorkspaceName());
+ String path = getPath(uri);
+ if (cache.containsPath(path)) {
+ ItemId id = cache.getItemId(path);
if (!id.denotesNode()) {
return (PropertyId) id;
}
@@ -337,7 +370,7 @@ public PropertyId getPropertyId(String uri, SessionInfo sessionInfo) throws Repo
try {
Name name = service.getNamePathResolver(sessionInfo).getQName(propName);
PropertyId propertyId = service.getIdFactory().createPropertyId(parentId, name);
- cache.add(uri, propertyId);
+ cache.add(getPath(uri), propertyId);
return propertyId;
} catch (NameException e) {
Something went wrong with that request. Please try again.