You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2015/05/15 19:01:21 UTC

svn commit: r1679605 - in /sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src: main/java/org/apache/sling/nosql/generic/adapter/ main/java/org/apache/sling/nosql/generic/resource/ main/java/org/apache/sling/nosql/generic/resource/impl/ test/j...

Author: sseifert
Date: Fri May 15 17:01:21 2015
New Revision: 1679605

URL: http://svn.apache.org/r1679605
Log:
new approach

Removed:
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlModifyingResourceProvider.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlQueriableResourceProvider.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlReadonlyResourceProvider.java
Modified:
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlAdapter.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlData.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/AbstractNoSqlResourceProviderFactory.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResource.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/PathUtil.java
    sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/test/java/org/apache/sling/nosql/generic/simple/SimpleNoSqlAdapter.java

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlAdapter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlAdapter.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlAdapter.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlAdapter.java Fri May 15 17:01:21 2015
@@ -24,6 +24,13 @@ import java.util.Iterator;
  * Adapter for NoSQL databases to be hooked into the Generic NoSQL resource provider.
  */
 public interface NoSqlAdapter {
+    
+    /**
+     * True if the given path is valid and supported by the NoSQL database.
+     * @param path Path
+     * @return true if valid, false if invalid
+     */
+    boolean validPath(String path);
 
     /**
      * Get data for a single resource from NoSQL database.

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlData.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlData.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlData.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/adapter/NoSqlData.java Fri May 15 17:01:21 2015
@@ -20,6 +20,9 @@ package org.apache.sling.nosql.generic.a
 
 import java.util.Map;
 
+/**
+ * Wrapper for properties of a NoSQL document for a given path.
+ */
 public final class NoSqlData {
 
     private final String path;

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/AbstractNoSqlResourceProviderFactory.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/AbstractNoSqlResourceProviderFactory.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/AbstractNoSqlResourceProviderFactory.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/AbstractNoSqlResourceProviderFactory.java Fri May 15 17:01:21 2015
@@ -27,6 +27,10 @@ import org.apache.sling.nosql.generic.ad
 import org.apache.sling.nosql.generic.resource.impl.NoSqlResourceProvider;
 import org.osgi.service.event.EventAdmin;
 
+/**
+ * Abstract implementation of resource provider factory.
+ * NoSQL resource providers implement this, add their own configuration support and and provide the matching NoSQL adapter implementation.
+ */
 public abstract class AbstractNoSqlResourceProviderFactory implements ResourceProviderFactory {
 
     public final ResourceProvider getResourceProvider(Map<String, Object> authenticationInfo) throws LoginException {

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResource.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResource.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResource.java Fri May 15 17:01:21 2015
@@ -26,8 +26,13 @@ import org.apache.sling.api.resource.Mod
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ModifiableValueMapDecorator;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.nosql.generic.adapter.NoSqlData;
 
+/**
+ * Generic implementaiton of a NoSQL database resource.
+ */
 public class NoSqlResource extends AbstractResource {
     
     private final NoSqlData data;
@@ -60,13 +65,14 @@ public class NoSqlResource extends Abstr
         return metadata;
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
         if (type == ValueMap.class || type == Map.class) {
-            // TODO: implement value map adaption
+            return (AdapterType)new ValueMapDecorator(data.getProperties());
         }
         if (type == ModifiableValueMap.class) {
-            // TODO: implement value map adaption
+            return (AdapterType)new ModifiableValueMapDecorator(data.getProperties());
         }
         return super.adaptTo(type);
     }

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java Fri May 15 17:01:21 2015
@@ -18,14 +18,21 @@
  */
 package org.apache.sling.nosql.generic.resource.impl;
 
+import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.resource.ModifyingResourceProvider;
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.QueriableResourceProvider;
@@ -35,61 +42,223 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.nosql.generic.adapter.NoSqlAdapter;
 import org.apache.sling.nosql.generic.adapter.NoSqlData;
+import org.osgi.service.event.Event;
 import org.osgi.service.event.EventAdmin;
 
+/**
+ * Generic implementation of a NoSQL resource provider.
+ * The mapping to the NoSQL database implementation details is done via the provided {@link NoSqlAdapter}.
+ */
 public class NoSqlResourceProvider implements ResourceProvider, ModifyingResourceProvider, QueriableResourceProvider {
     
-    private final NoSqlReadonlyResourceProvider readonlyDelegate;
-    private final NoSqlModifyingResourceProvider modifyingDelegate;
-    private final NoSqlQueriableResourceProvider queriableDelegate;
-    
+    private final NoSqlAdapter adapter;
+    private final EventAdmin eventAdmin;
     private final Map<String, NoSqlData> changedResources = new HashMap<String, NoSqlData>();
     private final Set<String> deletedResources = new HashSet<String>();
     
     public NoSqlResourceProvider(NoSqlAdapter adapter, EventAdmin eventAdmin) {
-        this.readonlyDelegate = new NoSqlReadonlyResourceProvider(adapter, changedResources, deletedResources);
-        this.modifyingDelegate = new NoSqlModifyingResourceProvider(adapter, eventAdmin, changedResources, deletedResources);
-        this.queriableDelegate = new NoSqlQueriableResourceProvider(adapter);
+        this.adapter = adapter;
+        this.eventAdmin = eventAdmin;
     }
 
+    
+    // ### READONLY ACCESS ###
+    
     public Resource getResource(ResourceResolver resourceResolver, String path) {
-        return readonlyDelegate.getResource(resourceResolver, path);
+        if (!adapter.validPath(path)) {
+            return null;
+        }
+        if (this.deletedResources.contains(path)) {
+            return null;
+        }
+        if (this.changedResources.containsKey(path)) {
+            return new NoSqlResource(this.changedResources.get(path), resourceResolver);
+        }
+        NoSqlData data = adapter.get(path);
+        if (data != null) {
+            return new NoSqlResource(data, resourceResolver);
+        }
+        return null;
     }
 
     public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest request, String path) {
-        return readonlyDelegate.getResource(resourceResolver, request, path);
+        return getResource(resourceResolver, path);
     }
 
     public Iterator<Resource> listChildren(Resource parent) {
-        return readonlyDelegate.listChildren(parent);
+        
+        // use map to consolidate data from adapter minus deleted plus changed resources
+        // always sorty result alphabetically to have a consistent ordering - the nosql data source does not support ordering
+        SortedMap<String, Resource> children = new TreeMap<String, Resource>();
+        
+        Iterator<NoSqlData> fromAdapter = adapter.getChildren(parent.getPath());
+        while (fromAdapter.hasNext()) {
+            NoSqlData item = fromAdapter.next();
+            if (isDeleted(item.getPath()) || changedResources.containsKey(item.getPath())) {
+                continue;
+            }
+            children.put(item.getPath(), new NoSqlResource(item, parent.getResourceResolver()));
+        }
+        
+        Pattern childPathPattern = PathUtil.getChildPathPattern(parent.getPath());
+        for (NoSqlData item : changedResources.values()) {
+            if (childPathPattern.matcher(item.getPath()).matches()) {
+                children.put(item.getPath(), new NoSqlResource(item, parent.getResourceResolver()));
+            }
+        }
+        
+        return children.values().iterator();
+    }
+
+    private boolean isDeleted(String path) {
+        for (String deletedPath : deletedResources) {
+            return StringUtils.equals(path, deletedPath) || StringUtils.startsWith(path, deletedPath + "/");
+        }
+        return false;
     }
 
-    public Resource create(ResourceResolver resolver, String path, Map<String, Object> properties) throws PersistenceException {
-        return modifyingDelegate.create(resolver, path, properties);
+    
+    // ### WRITE ACCESS ###
+    
+    public Resource create(ResourceResolver resolver, String path, Map<String, Object> properties)
+            throws PersistenceException {
+        if (!adapter.validPath(path)) {
+            throw new PersistenceException("Illegal path - unable to create resource at " + path, null, path, null);
+        }
+
+        // check if already exists
+        boolean deleted = this.deletedResources.remove(path);
+        boolean exists = changedResources.containsKey(path) || this.adapter.get(path) != null;
+        if (!deleted && exists) {
+            throw new PersistenceException("Resource already exists at " + path, null, path, null);
+        }
+        
+        // create new resource in changeset
+        NoSqlData data = new NoSqlData(path, properties);
+        changedResources.put(path, data);
+        return new NoSqlResource(data, resolver);
     }
-
+    
     public void delete(ResourceResolver resolver, String path) throws PersistenceException {
-        modifyingDelegate.delete(resolver, path);
+        if (!adapter.validPath(path)) {
+            throw new PersistenceException("Unable to delete resource at {}" + path, null, path, null);
+        }
+
+        Pattern pathsToDeletePattern = PathUtil.getSameOrDescendantPathPattern(path);
+
+        // remove all existing path and probably descendant paths from list of deleted paths
+        Iterator<String> deletedResourcesIterator = deletedResources.iterator();
+        while (deletedResourcesIterator.hasNext()) {
+            String deletedPath = deletedResourcesIterator.next();
+            if (pathsToDeletePattern.matcher(deletedPath).matches()) {
+                deletedResourcesIterator.remove();
+            }
+        }
+        
+        // remove all changed descendant items from changeset
+        Iterator<Map.Entry<String, NoSqlData>> changeResourcesIterator = changedResources.entrySet().iterator();
+        while (changeResourcesIterator.hasNext()) {
+            Map.Entry<String, NoSqlData> entry = changeResourcesIterator.next();
+            if (pathsToDeletePattern.matcher(entry.getKey()).matches()) {
+                changeResourcesIterator.remove();
+            }
+        }
+        
+        // add path to delete
+        deletedResources.add(path);
     }
-
+    
     public void revert(ResourceResolver resolver) {
-        modifyingDelegate.revert(resolver);
+        changedResources.clear();
+        deletedResources.clear();
     }
-
+    
     public void commit(ResourceResolver resolver) throws PersistenceException {
-        modifyingDelegate.commit(resolver);
+        try {
+            for (String path : deletedResources) {
+               adapter.deleteRecursive(path); 
+               notifyRemoved(path);
+            }
+            for (NoSqlData item : changedResources.values()) {
+                boolean created = adapter.store(item);
+                if (created) {
+                    notifyAdded(item.getPath());
+                }
+                else {
+                    notifyUpdated(item.getPath());
+                }
+            }
+        }
+        finally {
+            this.revert(resolver);
+        }
     }
-
+    
     public boolean hasChanges(ResourceResolver resolver) {
-        return modifyingDelegate.hasChanges(resolver);
+        return !(changedResources.isEmpty() && deletedResources.isEmpty());
     }
-
-    public Iterator<Resource> findResources(ResourceResolver resolver, String query, String language) {
-        return queriableDelegate.findResources(resolver, query, language);
+    
+    private void notifyAdded(String path) {
+        final Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(SlingConstants.PROPERTY_PATH, path);
+        props.put("event.distribute", "");
+        final Event event = new Event(SlingConstants.TOPIC_RESOURCE_ADDED, props);
+        this.eventAdmin.postEvent(event);
+    }
+
+    private void notifyUpdated(String path) {
+        final Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(SlingConstants.PROPERTY_PATH, path);
+        props.put("event.distribute", "");
+        final Event event = new Event(SlingConstants.TOPIC_RESOURCE_CHANGED, props);
+        this.eventAdmin.postEvent(event);
+    }    
+
+    private void notifyRemoved(String path) {
+        final Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(SlingConstants.PROPERTY_PATH, path);
+        props.put("event.distribute", "");
+        final Event event = new Event(SlingConstants.TOPIC_RESOURCE_REMOVED, props);
+        this.eventAdmin.postEvent(event);
     }
 
-    public Iterator<ValueMap> queryResources(ResourceResolver resolver, String query, String language) {
-        return queryResources(resolver, query, language);
+    
+    // ### QUERY ACCESS ###
+    
+    public Iterator<Resource> findResources(final ResourceResolver resolver, final String query, final String language) {
+        final Iterator<NoSqlData> result = adapter.query(query, language);
+        if (result == null) {
+            return null;
+        }
+        return new Iterator<Resource>() {
+            public boolean hasNext() {
+                return result.hasNext();
+            }
+            public Resource next() {
+                return new NoSqlResource(result.next(), resolver);
+            }
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public Iterator<ValueMap> queryResources(final ResourceResolver resolver, final String query, final String language) {
+        final Iterator<Resource> result = findResources(resolver, query, language);
+        if (result == null) {
+            return null;
+        }
+        return new Iterator<ValueMap>() {
+            public boolean hasNext() {
+                return result.hasNext();
+            }
+            public ValueMap next() {
+                return result.next().getValueMap();
+            }
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
     }
-
+    
 }

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/PathUtil.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/PathUtil.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/PathUtil.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/main/java/org/apache/sling/nosql/generic/resource/impl/PathUtil.java Fri May 15 17:01:21 2015
@@ -20,6 +20,9 @@ package org.apache.sling.nosql.generic.r
 
 import java.util.regex.Pattern;
 
+/**
+ * Helper functions for handling paths.
+ */
 public final class PathUtil {
 
     private PathUtil() {

Modified: sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/test/java/org/apache/sling/nosql/generic/simple/SimpleNoSqlAdapter.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/test/java/org/apache/sling/nosql/generic/simple/SimpleNoSqlAdapter.java?rev=1679605&r1=1679604&r2=1679605&view=diff
==============================================================================
--- sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/test/java/org/apache/sling/nosql/generic/simple/SimpleNoSqlAdapter.java (original)
+++ sling/whiteboard/sseifert/SLING-4381_nosql/nosql-generic/src/test/java/org/apache/sling/nosql/generic/simple/SimpleNoSqlAdapter.java Fri May 15 17:01:21 2015
@@ -18,12 +18,14 @@
  */
 package org.apache.sling.nosql.generic.simple;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.regex.Pattern;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.nosql.generic.adapter.NoSqlAdapter;
 import org.apache.sling.nosql.generic.adapter.NoSqlData;
 import org.apache.sling.nosql.generic.resource.impl.PathUtil;
@@ -36,6 +38,10 @@ public class SimpleNoSqlAdapter implemen
     
     private final Map<String, Map<String,Object>> store = new HashMap<String, Map<String,Object>>();
 
+    public boolean validPath(String path) {
+        return true;
+    }
+
     public NoSqlData get(String path) {
         Map<String,Object> properties = store.get(path);
         if (properties != null) {
@@ -84,8 +90,23 @@ public class SimpleNoSqlAdapter implemen
     }
 
     public Iterator<NoSqlData> query(String query, String language) {
-        // not supported
-        return null;
+        // implement simple dummy query
+        if (StringUtils.equals(language, "simple") && StringUtils.equals(query, "all")) {
+            final Iterator<Entry<String, Map<String,Object>>> entries = store.entrySet().iterator();
+            return new Iterator<NoSqlData>() {
+                public boolean hasNext() {
+                    return entries.hasNext();
+                }
+                public NoSqlData next() {
+                    Entry<String, Map<String,Object>> entry = entries.next();
+                    return new NoSqlData(entry.getKey(), entry.getValue());
+                }
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+        }
+        return Collections.<NoSqlData>emptyList().iterator();
     }
 
 }