You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:24:49 UTC
[sling-org-apache-sling-mongodb] 01/36: New MongoDB resource
provider
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-mongodb.git
commit 45b6b2a64ee774e7c8d66eb42d08df58382f6d93
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Wed Aug 1 17:53:47 2012 +0000
New MongoDB resource provider
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1368139 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 90 ++++++
.../sling/mongodb/impl/ChangeableValueMap.java | 90 ++++++
.../mongodb/impl/MongoDBCollectionResource.java | 90 ++++++
.../apache/sling/mongodb/impl/MongoDBContext.java | 93 ++++++
.../apache/sling/mongodb/impl/MongoDBResource.java | 146 +++++++++
.../mongodb/impl/MongoDBResourceProvider.java | 355 +++++++++++++++++++++
.../impl/MongoDBResourceProviderFactory.java | 109 +++++++
.../sling/mongodb/impl/ReadableValueMap.java | 227 +++++++++++++
.../OSGI-INF/metatype/metatype.properties | 42 +++
9 files changed, 1242 insertions(+)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d43dcc1
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>13</version>
+ </parent>
+
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.mongodb</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <name>org.apache.sling.mongodb</name>
+ <url>http://maven.apache.org</url>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Private-Package>
+ org.apache.sling.mongodb.impl
+ </Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.2.5-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.osgi</artifactId>
+ <version>2.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mongodb</groupId>
+ <artifactId>mongo-java-driver</artifactId>
+ <version>2.8.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/mongodb/impl/ChangeableValueMap.java b/src/main/java/org/apache/sling/mongodb/impl/ChangeableValueMap.java
new file mode 100644
index 0000000..493a737
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/ChangeableValueMap.java
@@ -0,0 +1,90 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.ModifiableValueMap;
+
+public class ChangeableValueMap
+ extends ReadableValueMap
+ implements ModifiableValueMap {
+
+ private final MongoDBResource resource;
+
+ public ChangeableValueMap(final MongoDBResource resource) {
+ super(resource.getProperties());
+ this.resource = resource;
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear() {
+ throw new UnsupportedOperationException("clear");
+ }
+
+ /**
+ * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+ */
+ public Object put(final String name, final Object value) {
+ final Object oldValue = this.valueMap.get(name);
+ final String key;
+ if ( name.startsWith("_") ) {
+ key = "_" + name;
+ } else {
+ key = name;
+ }
+ this.resource.getProperties().put(key, value);
+
+ // update map and resource
+ this.createValueMap(this.resource.getProperties());
+ this.resource.changed();
+
+ return oldValue;
+ }
+
+ /**
+ * @see java.util.Map#putAll(java.util.Map)
+ */
+ public void putAll(final Map<? extends String, ? extends Object> m) {
+ for(final Map.Entry<? extends String, ? extends Object> e : m.entrySet() ) {
+ this.put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * @see java.util.Map#remove(java.lang.Object)
+ */
+ public Object remove(final Object name) {
+ final Object result = this.valueMap.get(name);
+ if ( result != null ) {
+ final String key;
+ if ( name.toString().startsWith("_") ) {
+ key = "_" + name;
+ } else {
+ key = name.toString();
+ }
+ this.resource.getProperties().removeField(key);
+
+ // update map and resource
+ this.createValueMap(this.resource.getProperties());
+ this.resource.changed();
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/sling/mongodb/impl/MongoDBCollectionResource.java b/src/main/java/org/apache/sling/mongodb/impl/MongoDBCollectionResource.java
new file mode 100644
index 0000000..6cccb4a
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/MongoDBCollectionResource.java
@@ -0,0 +1,90 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.sling.api.resource.AbstractResource;
+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.ValueMapDecorator;
+
+public class MongoDBCollectionResource extends AbstractResource {
+
+ private final String path;
+
+ private final ResourceResolver resourceResolver;
+
+ private final ResourceMetadata metadata = new ResourceMetadata();
+
+ public MongoDBCollectionResource(final ResourceResolver resolver, final String path) {
+ this.resourceResolver = resolver;
+ this.path = path;
+ this.metadata.setResolutionPath(this.path);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getPath()
+ */
+ public String getPath() {
+ return this.path;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceType()
+ */
+ public String getResourceType() {
+ return "mongodb:collection";
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceSuperType()
+ */
+ public String getResourceSuperType() {
+ return null;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceMetadata()
+ */
+ public ResourceMetadata getResourceMetadata() {
+ return this.metadata;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceResolver()
+ */
+ public ResourceResolver getResourceResolver() {
+ return this.resourceResolver;
+ }
+
+ /**
+ * @see org.apache.sling.api.adapter.SlingAdaptable#adaptTo(java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <AdapterType> AdapterType adaptTo(final Class<AdapterType> type) {
+ if ( type == ValueMap.class ) {
+ return (AdapterType) new ValueMapDecorator(Collections.EMPTY_MAP);
+ } else if ( type == Map.class ) {
+ return (AdapterType) Collections.EMPTY_MAP;
+ }
+ return super.adaptTo(type);
+ }
+}
diff --git a/src/main/java/org/apache/sling/mongodb/impl/MongoDBContext.java b/src/main/java/org/apache/sling/mongodb/impl/MongoDBContext.java
new file mode 100644
index 0000000..c801e8b
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/MongoDBContext.java
@@ -0,0 +1,93 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.mongodb.DB;
+
+public class MongoDBContext {
+
+ /** The roots. */
+ private final String[] roots;
+
+ /** The roots ended by a slash. */
+ private final String[] rootsWithSlash;
+
+ /** Don't show these collections. */
+ private final Set<String> filterCollectionNames = new HashSet<String>();
+
+ /** The database to be used. */
+ private final DB database;
+
+ public MongoDBContext(final DB database,
+ final String[] configuredRoots,
+ final String[] configuredFilterCollectionNames) {
+ this.database = database;
+ if ( configuredRoots != null ) {
+ final List<String> rootsList = new ArrayList<String>();
+ final List<String> rootsWithSlashList = new ArrayList<String>();
+ for(final String r : configuredRoots) {
+ if ( r != null ) {
+ final String value = r.trim();
+ if ( value.length() > 0 ) {
+ if ( value.endsWith("/") ) {
+ rootsWithSlashList.add(value);
+ rootsList.add(value.substring(0, value.length() - 1));
+ } else {
+ rootsWithSlashList.add(value + "/");
+ rootsList.add(value);
+ }
+ }
+ }
+ }
+ this.roots = rootsList.toArray(new String[rootsList.size()]);
+ this.rootsWithSlash = rootsWithSlashList.toArray(new String[rootsWithSlashList.size()]);
+ } else {
+ this.roots = new String[0];
+ this.rootsWithSlash = new String[0];
+ }
+ if ( configuredFilterCollectionNames != null ) {
+ for(final String name : configuredFilterCollectionNames) {
+ this.filterCollectionNames.add(name);
+ }
+ }
+ }
+
+ public String[] getRoots() {
+ return roots;
+ }
+
+ public String[] getRootsWithSlash() {
+ return this.rootsWithSlash;
+ }
+
+ public boolean isFilterCollectionName(final String name) {
+ return this.filterCollectionNames.contains(name);
+ }
+
+ public Set<String> getFilterCollectionNames() {
+ return this.filterCollectionNames;
+ }
+
+ public DB getDatabase() {
+ return this.database;
+ }
+}
diff --git a/src/main/java/org/apache/sling/mongodb/impl/MongoDBResource.java b/src/main/java/org/apache/sling/mongodb/impl/MongoDBResource.java
new file mode 100644
index 0000000..6ad685c
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/MongoDBResource.java
@@ -0,0 +1,146 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.AbstractResource;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+
+import com.mongodb.DBObject;
+
+public class MongoDBResource extends AbstractResource {
+
+ /** The complete resource path. */
+ private final String resourcePath;
+
+ /** The collection */
+ private final String collection;
+
+ /** The resource resolver. */
+ private final ResourceResolver resourceResolver;
+
+ /** Metadata. */
+ private final ResourceMetadata metadata = new ResourceMetadata();
+
+ /** The db object. */
+ private DBObject dbObject;
+
+ /** The MongoDB resource provider. */
+ private final MongoDBResourceProvider provider;
+
+ public MongoDBResource(final ResourceResolver resolver,
+ final String resourcePath,
+ final String collection,
+ final DBObject dbObject,
+ final MongoDBResourceProvider provider) {
+ this.resourceResolver = resolver;
+ this.resourcePath = resourcePath;
+ this.collection = collection;
+ this.dbObject = dbObject;
+ this.provider = provider;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getPath()
+ */
+ public String getPath() {
+ return this.resourcePath;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceType()
+ */
+ public String getResourceType() {
+ // get resource type from data
+ final Object rt = this.dbObject.get("sling:resourceType");
+ if ( rt != null ) {
+ return rt.toString();
+ }
+ return "nt:unstructured";
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceSuperType()
+ */
+ public String getResourceSuperType() {
+ // get resource type from data
+ final Object rt = this.dbObject.get("sling:resourceSuperType");
+ if ( rt != null ) {
+ return rt.toString();
+ }
+ return null;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceMetadata()
+ */
+ public ResourceMetadata getResourceMetadata() {
+ return this.metadata;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.Resource#getResourceResolver()
+ */
+ public ResourceResolver getResourceResolver() {
+ return this.resourceResolver;
+ }
+
+ /**
+ * @see org.apache.sling.api.adapter.SlingAdaptable#adaptTo(java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public <AdapterType> AdapterType adaptTo(final Class<AdapterType> type) {
+ if ( type == ValueMap.class || type == Map.class ) {
+ this.dbObject = this.provider.getUpdatedDBObject(this.resourcePath, this.dbObject);
+ return (AdapterType) new ReadableValueMap(this.dbObject);
+ } else if ( type == ModifiableValueMap.class ) {
+ this.dbObject = this.provider.getUpdatedDBObject(this.resourcePath, this.dbObject);
+ return (AdapterType) new ChangeableValueMap(this);
+ }
+
+ return super.adaptTo(type);
+ }
+
+ /**
+ * Return the collection.
+ */
+ public String getCollection() {
+ return this.collection;
+ }
+
+ /**
+ * Get the current properties.
+ */
+ public DBObject getProperties() {
+ return this.dbObject;
+ }
+
+ @Override
+ public String toString() {
+ return "MongoDBResource [resourcePath=" + resourcePath + ", dbPath=" + this.dbObject.get(MongoDBResourceProvider.PROP_PATH) + ", collection=" + collection
+ + ", resourceResolver=" + resourceResolver + "]";
+ }
+
+ public void changed() {
+ this.provider.changed(this);
+ }
+}
diff --git a/src/main/java/org/apache/sling/mongodb/impl/MongoDBResourceProvider.java b/src/main/java/org/apache/sling/mongodb/impl/MongoDBResourceProvider.java
new file mode 100644
index 0000000..86a31c5
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/MongoDBResourceProvider.java
@@ -0,0 +1,355 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.ModifyingResourceProvider;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.BasicDBObjectBuilder;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+
+/**
+ * The MongoDB resource provider creates resources based on MongoDB entries.
+ * The resources contain all properties stored in the MongoDB except those starting with a "_".
+ */
+public class MongoDBResourceProvider implements ResourceProvider, ModifyingResourceProvider {
+
+ /** The special path property containing the (relative) path of the resource in the tree. */
+ public static final String PROP_PATH = "_path";
+
+ /** The id property. */
+ public static final String PROP_ID = "_id";
+
+ /** Logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** The global context .*/
+ private final MongoDBContext context;
+
+ private final Map<String, MongoDBResource> changedResources = new HashMap<String, MongoDBResource>();
+
+ private final Set<String> deletedResources = new HashSet<String>();
+
+ public MongoDBResourceProvider(final MongoDBContext context) {
+ this.context = context;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ModifyingResourceProvider#create(org.apache.sling.api.resource.ResourceResolver, java.lang.String, java.util.Map)
+ */
+ public Resource create(final ResourceResolver resolver, final String path, final Map<String, Object> properties)
+ throws PersistenceException {
+ final String[] info = this.extractResourceInfo(path);
+ if ( info != null && info.length == 2) {
+ final boolean deleted = this.deletedResources.remove(path);
+ final MongoDBResource oldResource = (MongoDBResource)this.getResource(resolver, path, info);
+ if ( !deleted && oldResource != null ) {
+ throw new PersistenceException("Resource already exists at " + path, null, path, null);
+ }
+ final DBObject dbObj = new BasicDBObject();
+ dbObj.put(PROP_PATH, info[1]);
+ if ( properties != null ) {
+ for(Map.Entry<String, Object> entry : properties.entrySet()) {
+ final String key;
+ if ( entry.getKey().startsWith("_") ) {
+ key = "_" + entry.getKey();
+ } else {
+ key = entry.getKey();
+ }
+ dbObj.put(key, entry.getValue());
+ }
+ }
+ if ( deleted && oldResource != null ) {
+ dbObj.put(PROP_ID, oldResource.getProperties().get(PROP_ID));
+ }
+ final MongoDBResource rsrc = new MongoDBResource(resolver, path, info[0], dbObj, this);
+ this.changedResources.put(path, rsrc);
+
+ return rsrc;
+ }
+ throw new PersistenceException("Illegal path - unable to create resource at " + path, null, path, null);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ModifyingResourceProvider#delete(org.apache.sling.api.resource.ResourceResolver, java.lang.String)
+ */
+ public void delete(final ResourceResolver resolver, final String path)
+ throws PersistenceException {
+ final Resource rsrc = this.getResource(resolver, path);
+ if ( rsrc != null ) {
+ // TODO - delete all child resources!
+ this.deletedResources.add(path);
+ this.changedResources.remove(path);
+ }
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ModifyingResourceProvider#revert()
+ */
+ public void revert() {
+ this.changedResources.clear();
+ this.deletedResources.clear();
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ModifyingResourceProvider#commit()
+ */
+ public void commit() throws PersistenceException {
+ try {
+ for(final String deleted : this.deletedResources) {
+ final String[] info = this.extractResourceInfo(deleted);
+
+ // check if the database still exists
+ if ( this.hasDatabase(info[0]) ) {
+ final DBCollection col = this.context.getDatabase().getCollection(info[0]);
+ if ( col != null ) {
+ col.findAndRemove(QueryBuilder.start(PROP_PATH).is(info[1]).get());
+ }
+ }
+ }
+ for(final MongoDBResource changed : this.changedResources.values()) {
+
+ final DBCollection col = this.context.getDatabase().getCollection(changed.getCollection());
+ if ( col != null ) {
+ // create or update?
+ if ( changed.getProperties().get(PROP_ID) != null ) {
+ col.update(QueryBuilder.start(PROP_PATH).is(changed.getProperties().get(PROP_PATH)).get(),
+ changed.getProperties());
+ } else {
+ // create
+ col.save(changed.getProperties());
+ }
+ } else {
+ throw new PersistenceException("Unable to create collection " + changed.getCollection(), null, changed.getPath(), null);
+ }
+ }
+ } finally {
+ this.revert();
+ }
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ModifyingResourceProvider#hasChanges()
+ */
+ public boolean hasChanges() {
+ return this.changedResources.size() > 0 || this.deletedResources.size() > 0;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ResourceProvider#getResource(org.apache.sling.api.resource.ResourceResolver, java.lang.String)
+ */
+ public Resource getResource(final ResourceResolver resourceResolver, final String path) {
+ if ( this.deletedResources.contains(path) ) {
+ return null;
+ }
+ if ( this.changedResources.containsKey(path) ) {
+ return this.changedResources.get(path);
+ }
+ final String[] info = this.extractResourceInfo(path);
+ if ( info != null ) {
+ return this.getResource(resourceResolver, path, info);
+ }
+ return null;
+ }
+
+ public void changed(final MongoDBResource resource) {
+ this.deletedResources.remove(resource.getPath());
+ this.changedResources.put(resource.getPath(), resource);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ResourceProvider#listChildren(org.apache.sling.api.resource.Resource)
+ */
+ public Iterator<Resource> listChildren(final Resource parent) {
+ final String[] info = this.extractResourceInfo(parent.getPath());
+ if ( info != null ) {
+ if ( info.length == 0 ) {
+ // all collections
+ final Set<String> names = new HashSet<String>(context.getDatabase().getCollectionNames());
+ names.removeAll(this.context.getFilterCollectionNames());
+ final Iterator<String> i = names.iterator();
+ return new Iterator<Resource>() {
+
+ public boolean hasNext() {
+ return i.hasNext();
+ }
+
+ public Resource next() {
+ final String name = i.next();
+ return new MongoDBCollectionResource(parent.getResourceResolver(), parent.getPath() + '/' + name);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ };
+ }
+ if ( this.hasDatabase(info[0]) ) {
+ final DBCollection col = this.context.getDatabase().getCollection(info[0]);
+ if ( col != null ) {
+ final String pattern;
+ if ( info.length == 1 ) {
+ pattern = "^([^/])*$";
+ } else {
+ pattern = "^" + Pattern.quote(info[1]) + "/([^/])*$";
+ }
+
+ final DBObject query = QueryBuilder.start(PROP_PATH).regex(Pattern.compile(pattern)).get();
+ final DBCursor cur = col.find(query).
+ sort(BasicDBObjectBuilder.start(PROP_PATH, 1).get());
+ return new Iterator<Resource>() {
+
+ public boolean hasNext() {
+ return cur.hasNext();
+ }
+
+ public Resource next() {
+ final DBObject obj = cur.next();
+ final String objPath = obj.get(PROP_PATH).toString();
+ final int lastSlash = objPath.lastIndexOf('/');
+ final String name;
+ if (lastSlash == -1) {
+ name = objPath;
+ } else {
+ name = objPath.substring(lastSlash + 1);
+ }
+ return new MongoDBResource(parent.getResourceResolver(),
+ parent.getPath() + '/' + name,
+ info[0],
+ obj,
+ MongoDBResourceProvider.this);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ };
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ResourceProvider#getResource(org.apache.sling.api.resource.ResourceResolver, javax.servlet.http.HttpServletRequest, java.lang.String)
+ */
+ @SuppressWarnings("javadoc")
+ public Resource getResource(final ResourceResolver resourceResolver,
+ final HttpServletRequest request,
+ final String path) {
+ return this.getResource(resourceResolver, path);
+ }
+
+ /**
+ * Extract info about collection and path
+ */
+ private String[] extractResourceInfo(final String path) {
+ for(final String root : this.context.getRootsWithSlash()) {
+ if ( path.startsWith(root) ) {
+ if ( path.length() == root.length() ) {
+ // special resource - show all collections
+ return new String[0];
+ }
+ final String info = path.substring(root.length());
+ final int slashPos = info.indexOf('/');
+ if ( slashPos != -1 ) {
+ return new String[] {info.substring(0, slashPos), info.substring(slashPos + 1)};
+ }
+ // special resource - collection
+ return new String[] {info};
+ }
+ }
+ for(final String root : this.context.getRoots()) {
+ if ( path.equals(root) ) {
+ // special resource - show all collections
+ return new String[0];
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check if a database with a given name exists
+ */
+ private boolean hasDatabase(final String name) {
+ final Set<String> names = this.context.getDatabase().getCollectionNames();
+ return names.contains(name) && !this.context.isFilterCollectionName(name);
+ }
+
+ /**
+ * Create a resource
+ */
+ private Resource getResource(final ResourceResolver resourceResolver, final String path, final String[] info) {
+ if ( info.length == 0 ) {
+ // special resource : all collections
+ return new MongoDBCollectionResource(resourceResolver, path);
+ } else if ( info.length == 1 ) {
+ // special resource : collection
+ if ( this.hasDatabase(info[0]) ) {
+ return new MongoDBCollectionResource(resourceResolver, path);
+ }
+ return null;
+ }
+ logger.info("Searching {} in {}", info[1], info[0]);
+ if ( this.hasDatabase(info[0]) ) {
+ final DBCollection col = this.context.getDatabase().getCollection(info[0]);
+ if ( col != null ) {
+ final DBObject obj = col.findOne(QueryBuilder.start(PROP_PATH).is(info[1]).get());
+ logger.info("Result={}", obj);
+ if ( obj != null ) {
+ return new MongoDBResource(resourceResolver,
+ path,
+ info[0],
+ obj,
+ this);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check if there is a newer db object for that path.
+ */
+ public DBObject getUpdatedDBObject(final String path, final DBObject dbObj) {
+ final MongoDBResource stored = this.changedResources.get(path);
+ if ( stored != null ) {
+ return stored.getProperties();
+ }
+ return dbObj;
+ }
+}
diff --git a/src/main/java/org/apache/sling/mongodb/impl/MongoDBResourceProviderFactory.java b/src/main/java/org/apache/sling/mongodb/impl/MongoDBResourceProviderFactory.java
new file mode 100644
index 0000000..a632215
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/MongoDBResourceProviderFactory.java
@@ -0,0 +1,109 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.util.Map;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyUnbounded;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceProviderFactory;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.mongodb.DB;
+import com.mongodb.Mongo;
+
+/**
+ * The MongoDB resource provider factory allows to provided resources stored
+ * in MongoDB.
+ */
+@Component(label="%factory.name",
+ description="%factory.description",
+ configurationFactory=true,
+ policy=ConfigurationPolicy.REQUIRE,
+ metatype=true)
+@Service(value=ResourceProviderFactory.class)
+@Properties({
+ @Property(name=ResourceProvider.ROOTS, value="/mongo")
+})
+public class MongoDBResourceProviderFactory implements ResourceProviderFactory {
+
+ private static final String DEFAULT_HOST = "localhost";
+
+ private static final int DEFAULT_PORT = 27017;
+
+ private static final String DEFAULT_DB = "sling";
+
+ @Property(value=DEFAULT_HOST)
+ private static final String PROP_HOST = "host";
+
+ @Property(intValue=DEFAULT_PORT)
+ private static final String PROP_PORT = "port";
+
+ @Property(value=DEFAULT_DB)
+ private static final String PROP_DB = "db";
+
+ @Property(unbounded=PropertyUnbounded.ARRAY, value="system.indexes")
+ private static final String PROP_FILTER_COLLECTIONS = "filter.collections";
+
+ /** Logger. */
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ /** The global context passed to each resource provider. */
+ private MongoDBContext context;
+
+ @Activate
+ protected void activate(final Map<String, Object> props) throws Exception {
+ final String host = PropertiesUtil.toString(props.get(PROP_HOST), DEFAULT_HOST);
+ final int port = PropertiesUtil.toInteger(props.get(PROP_PORT), DEFAULT_PORT);
+ final String db = PropertiesUtil.toString(props.get(PROP_DB), DEFAULT_DB);
+ logger.info("Starting MongoDB resource provider with host={}, port={}, db={}",
+ new Object[] {host, port, db});
+
+ final Mongo m = new Mongo( host , port );
+ final DB database = m.getDB( db );
+ logger.info("Connected to database {}", database);
+
+ this.context = new MongoDBContext(database,
+ PropertiesUtil.toStringArray(props.get(ResourceProvider.ROOTS)),
+ PropertiesUtil.toStringArray(props.get(PROP_FILTER_COLLECTIONS)));
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ResourceProviderFactory#getResourceProvider(java.util.Map)
+ */
+ public ResourceProvider getResourceProvider(final Map<String, Object> authenticationInfo) throws LoginException {
+ // for now we allow anonymous access
+ return new MongoDBResourceProvider(this.context);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ResourceProviderFactory#getAdministrativeResourceProvider(java.util.Map)
+ */
+ public ResourceProvider getAdministrativeResourceProvider(final Map<String, Object> authenticationInfo) throws LoginException {
+ // for now we allow anonymous access
+ return new MongoDBResourceProvider(this.context);
+ }
+}
diff --git a/src/main/java/org/apache/sling/mongodb/impl/ReadableValueMap.java b/src/main/java/org/apache/sling/mongodb/impl/ReadableValueMap.java
new file mode 100644
index 0000000..8da52d8
--- /dev/null
+++ b/src/main/java/org/apache/sling/mongodb/impl/ReadableValueMap.java
@@ -0,0 +1,227 @@
+/*
+ * 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.sling.mongodb.impl;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sling.api.resource.ValueMap;
+
+import com.mongodb.DBObject;
+
+public class ReadableValueMap implements ValueMap {
+
+ protected Map<String, Object> valueMap;
+
+ public ReadableValueMap(final DBObject dbObject) {
+ this.createValueMap(dbObject);
+ }
+
+ protected void createValueMap(final DBObject dbObject) {
+ @SuppressWarnings("unchecked")
+ final Map<String, Object> map = new HashMap<String, Object>(dbObject.toMap());
+ final Set<String> names = new HashSet<String>(map.keySet());
+ for(final String name : names) {
+ if ( name.startsWith("__") ) {
+ final Object value = map.remove(name);
+ map.put(name.substring(1), value);
+ } else if ( name.startsWith("_") ) {
+ // remove internal props, like _id, _path
+ map.remove(name);
+ }
+ }
+ this.valueMap = Collections.unmodifiableMap(map);
+ }
+
+ /**
+ * @see java.util.Map#containsKey(java.lang.Object)
+ */
+ public boolean containsKey(final Object key) {
+ return this.valueMap.containsKey(key);
+ }
+
+ /**
+ * @see java.util.Map#containsValue(java.lang.Object)
+ */
+ public boolean containsValue(final Object value) {
+ return this.valueMap.containsValue(value);
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set<java.util.Map.Entry<String, Object>> entrySet() {
+ return this.valueMap.entrySet();
+ }
+
+ /**
+ * @see java.util.Map#get(java.lang.Object)
+ */
+ public Object get(final Object key) {
+ return this.valueMap.get(key);
+ }
+
+ /**
+ * @see java.util.Map#isEmpty()
+ */
+ public boolean isEmpty() {
+ return this.valueMap.isEmpty();
+ }
+
+ /**
+ * @see java.util.Map#keySet()
+ */
+ public Set<String> keySet() {
+ return this.valueMap.keySet();
+ }
+
+ /**
+ * @see java.util.Map#size()
+ */
+ public int size() {
+ return this.valueMap.size();
+ }
+
+ /**
+ * @see java.util.Map#values()
+ */
+ public Collection<Object> values() {
+ return this.valueMap.values();
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(final String key, final Class<T> type) {
+ if (type == null) {
+ return (T) get(key);
+ }
+
+ final Object val = this.get(key);
+ if ( val == null ) {
+ return null;
+ }
+ return convertToType(val, type);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Object)
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(final String key,final T defaultValue) {
+ if (defaultValue == null) {
+ return (T) get(key);
+ }
+
+ T value = get(key, (Class<T>) defaultValue.getClass());
+ if (value == null) {
+ value = defaultValue;
+ }
+
+ return value;
+ }
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear() {
+ throw new UnsupportedOperationException("clear");
+ }
+
+ /**
+ * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+ */
+ public Object put(String key, Object value) {
+ throw new UnsupportedOperationException("put");
+ }
+
+ /**
+ * @see java.util.Map#putAll(java.util.Map)
+ */
+ public void putAll(Map<? extends String, ? extends Object> m) {
+ throw new UnsupportedOperationException("putAll");
+ }
+
+ /**
+ * @see java.util.Map#remove(java.lang.Object)
+ */
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ /**
+ * Converts the object to the given type.
+ * @param obj object
+ * @param type type
+ * @return the converted object
+ */
+ @SuppressWarnings("unchecked")
+ private <T> T convertToType(final Object obj, final Class<T> type) {
+ // todo: do smarter checks
+ try {
+ if (obj == null) {
+ return null;
+ } else if (type.isAssignableFrom(obj.getClass())) {
+ return (T) obj;
+ } else if (type.isArray()) {
+ return (T) convertToArray(obj, type.getComponentType());
+ } else if (type == String.class) {
+ return (T) String.valueOf(obj);
+ } else if (type == Integer.class) {
+ return (T) (Integer) Integer.parseInt(obj.toString());
+ } else if (type == Long.class) {
+ return (T) (Long) Long.parseLong(obj.toString());
+ } else if (type == Double.class) {
+ return (T) (Double) Double.parseDouble(obj.toString());
+ } else if (type == Boolean.class) {
+ return (T) (Boolean) Boolean.parseBoolean(obj.toString());
+ } else {
+ return null;
+ }
+ } catch (final NumberFormatException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Converts the object to an array of the given type
+ * @param obj tje object or object array
+ * @param type the component type of the array
+ * @return and array of type T
+ */
+ private <T> T[] convertToArray(Object obj, Class<T> type) {
+ List<T> values = new LinkedList<T>();
+ if (obj.getClass().isArray()) {
+ for (Object o: (Object[]) obj) {
+ values.add(convertToType(o, type));
+ }
+ } else {
+ values.add(convertToType(obj, type));
+ }
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(type, values.size());
+ return values.toArray(result);
+ }
+}
diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties
new file mode 100644
index 0000000..4d380cc
--- /dev/null
+++ b/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+
+#
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the SCR plugin
+
+#
+# Localizations for FsResourceProvider configuration
+factory.name = Apache Sling MongoDB Resource Provider
+factory.description = Configure an instance of the MongoDB \
+ resource provider in terms of provider root and connection
+
+provider.roots.name = Provider Root
+provider.roots.description = Location in the virtual resource tree where the \
+ resources are mapped in. This property must not be an empty string.
+host.name = MongoDB Host
+host.description = The host to connect to.
+
+port.name = MongoDB Port
+port.description = The port to connect to.
+
+db.name MongoDB Database
+db.description = The database to use.
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.