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-nosql-mongodb-resourceprovider.git
commit da9cdca3a5dba06c88f77425e9488ca1cbdebe37 Author: Stefan Seifert <[email protected]> AuthorDate: Mon Sep 14 21:22:00 2015 +0000 SLING-5024 Sling NoSQL Resource Provider for MongoDB (based on nosql.generic) git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1703063 13f79535-47bb-0310-9956-ffa450edef68 --- .../resourceprovider/impl/MongoDBNoSqlAdapter.java | 109 ++++++++++++++++++++ .../impl/MongoDBNoSqlResourceProviderFactory.java | 113 +++++++++++++++++++++ .../MongoDBNoSqlResourceProviderIT.java | 69 +++++++++++++ .../MongoDBNoSqlResourceProviderRootIT.java | 42 ++++++++ ...ongoDBNoSqlResourceProviderTransactionalIT.java | 69 +++++++++++++ 5 files changed, 402 insertions(+) diff --git a/src/main/java/org/apache/sling/nosql/mongodb/resourceprovider/impl/MongoDBNoSqlAdapter.java b/src/main/java/org/apache/sling/nosql/mongodb/resourceprovider/impl/MongoDBNoSqlAdapter.java new file mode 100644 index 0000000..fe509f2 --- /dev/null +++ b/src/main/java/org/apache/sling/nosql/mongodb/resourceprovider/impl/MongoDBNoSqlAdapter.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.nosql.mongodb.resourceprovider.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.sling.nosql.generic.adapter.AbstractNoSqlAdapter; +import org.apache.sling.nosql.generic.adapter.MultiValueMode; +import org.apache.sling.nosql.generic.adapter.NoSqlData; +import org.bson.Document; + +import com.mongodb.MongoClient; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoCursor; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.result.DeleteResult; +import com.mongodb.client.result.UpdateResult; + +/** + * {@link org.apache.sling.nosql.generic.adapter.NoSqlAdapter} implementation for MongoDB. + */ +public final class MongoDBNoSqlAdapter extends AbstractNoSqlAdapter { + + private static final String ID_PROPERTY = "_id"; + private static final String DATA_PROPERTY = "_data"; + + private final MongoCollection<Document> collection; + + /** + * @param mongoClient MongoDB client + * @param database MongoDB database + * @param collection MongoDB collection + */ + public MongoDBNoSqlAdapter(MongoClient mongoClient, String database, String collection) { + MongoDatabase db = mongoClient.getDatabase(database); + this.collection = db.getCollection(collection); + } + + @Override + public NoSqlData get(String path) { + Document wrapper = collection.find(Filters.eq(ID_PROPERTY, path)).first(); + if (wrapper == null) { + return null; + } + else { + return new NoSqlData(path, wrapper.get(DATA_PROPERTY, Document.class), MultiValueMode.LISTS); + } + } + + @Override + public Iterator<NoSqlData> getChildren(String parentPath) { + List<NoSqlData> children = new ArrayList<>(); + Pattern directChildren = Pattern.compile("^" + Pattern.quote(parentPath) + "/[^/]+$"); + FindIterable<Document> result = collection.find(Filters.regex(ID_PROPERTY, directChildren)); + try (MongoCursor<Document> wrappers = result.iterator()) { + while (wrappers.hasNext()) { + Document wrapper = wrappers.next(); + String path = wrapper.get(ID_PROPERTY, String.class); + Document data = wrapper.get(DATA_PROPERTY, Document.class); + children.add(new NoSqlData(path, data, MultiValueMode.LISTS)); + } + } + return children.iterator(); + } + + @Override + public boolean store(NoSqlData data) { + Document wrapper = new Document(); + wrapper.put(ID_PROPERTY, data.getPath()); + wrapper.put(DATA_PROPERTY, new Document(data.getProperties(MultiValueMode.LISTS))); + + UpdateResult result = collection.replaceOne(Filters.eq(ID_PROPERTY, data.getPath()), wrapper, new UpdateOptions().upsert(true)); + + // return true if a new entry was inserted, false if an existing was replaced + return (result.getMatchedCount() == 0); + } + + @Override + public boolean deleteRecursive(String path) { + Pattern decendantsAndSelf = Pattern.compile("^" + Pattern.quote(path) + "(/.+)?$"); + DeleteResult result = collection.deleteMany(Filters.regex(ID_PROPERTY, decendantsAndSelf)); + + // return true if any document was deleted + return result.getDeletedCount() > 0; + } + +} diff --git a/src/main/java/org/apache/sling/nosql/mongodb/resourceprovider/impl/MongoDBNoSqlResourceProviderFactory.java b/src/main/java/org/apache/sling/nosql/mongodb/resourceprovider/impl/MongoDBNoSqlResourceProviderFactory.java new file mode 100644 index 0000000..cf697f0 --- /dev/null +++ b/src/main/java/org/apache/sling/nosql/mongodb/resourceprovider/impl/MongoDBNoSqlResourceProviderFactory.java @@ -0,0 +1,113 @@ +/* + * 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.nosql.mongodb.resourceprovider.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.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.resource.ResourceProvider; +import org.apache.sling.api.resource.ResourceProviderFactory; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.nosql.generic.adapter.MetricsNoSqlAdapterWrapper; +import org.apache.sling.nosql.generic.adapter.NoSqlAdapter; +import org.apache.sling.nosql.generic.resource.AbstractNoSqlResourceProviderFactory; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.event.EventAdmin; +import org.slf4j.LoggerFactory; + +import com.mongodb.MongoClient; + +import aQute.bnd.annotation.component.Deactivate; + +/** + * {@link ResourceProviderFactory} implementation that uses MongoDB as persistence. + */ +@Component(immediate = true, metatype = true, + name="org.apache.sling.nosql.mongodb.resourceprovider.MongoDBNoSqlResourceProviderFactory.factory.config", + label = "Apache Sling NoSQL MongoDB Resource Provider Factory", + description = "Defines a resource provider factory with MongoDB persistence.", + configurationFactory = true, policy = ConfigurationPolicy.REQUIRE) +@Service(value = ResourceProviderFactory.class) +@Property(name = "webconsole.configurationFactory.nameHint", + value = "Root paths: {" + MongoDBNoSqlResourceProviderFactory.PROVIDER_ROOTS_PROPERTY + "}") +public final class MongoDBNoSqlResourceProviderFactory extends AbstractNoSqlResourceProviderFactory { + + @Property(label = "Root paths", description = "Root paths for resource provider.", cardinality = Integer.MAX_VALUE) + static final String PROVIDER_ROOTS_PROPERTY = ResourceProvider.ROOTS; + + @Property(label = "Connection String", + description = "MongoDB connection String. Example: 'localhost:27017,localhost:27018,localhost:27019'", + value = MongoDBNoSqlResourceProviderFactory.CONNECTION_STRING_DEFAULT) + static final String CONNECTION_STRING_PROPERTY = "connectionString"; + private static final String CONNECTION_STRING_DEFAULT = "localhost:27017"; + + @Property(label = "Database", + description = "MongoDB database to store resource data in.", + value = MongoDBNoSqlResourceProviderFactory.DATABASE_DEFAULT) + static final String DATABASE_PROPERTY = "database"; + private static final String DATABASE_DEFAULT = "sling"; + + @Property(label = "Collection", + description = "MongoDB collection to store resource data in.", + value = MongoDBNoSqlResourceProviderFactory.COLLECTION_DEFAULT) + static final String COLLECTION_PROPERTY = "collection"; + private static final String COLLECTION_DEFAULT = "resources"; + + @Reference + private EventAdmin eventAdmin; + + private MongoClient mongoClient; + private NoSqlAdapter noSqlAdapter; + + @Activate + private void activate(ComponentContext componentContext, Map<String, Object> config) { + String connectionString = PropertiesUtil.toString(config.get(CONNECTION_STRING_PROPERTY), CONNECTION_STRING_DEFAULT); + String database = PropertiesUtil.toString(config.get(DATABASE_PROPERTY), DATABASE_DEFAULT); + String collection = PropertiesUtil.toString(config.get(COLLECTION_PROPERTY), COLLECTION_DEFAULT); + + mongoClient = new MongoClient(connectionString); + NoSqlAdapter mongodbAdapter = new MongoDBNoSqlAdapter(mongoClient, database, collection); + + // enable call logging and metrics for {@link MongoDBNoSqlAdapter} + noSqlAdapter = new MetricsNoSqlAdapterWrapper(mongodbAdapter, LoggerFactory.getLogger(MongoDBNoSqlAdapter.class)); + } + + @Deactivate + private void deactivate() { + if (mongoClient != null) { + mongoClient.close(); + } + } + + @Override + protected NoSqlAdapter getNoSqlAdapter() { + return noSqlAdapter; + } + + @Override + protected EventAdmin getEventAdmin() { + return eventAdmin; + } + +} diff --git a/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderIT.java b/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderIT.java new file mode 100644 index 0000000..4126f54 --- /dev/null +++ b/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderIT.java @@ -0,0 +1,69 @@ +/* + * 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.nosql.mongodb.resourceprovider.integration; + +import java.util.UUID; + +import org.apache.jackrabbit.JcrConstants; +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.nosql.generic.resource.impl.AbstractNoSqlResourceProviderTest; +import org.apache.sling.nosql.mongodb.resourceprovider.impl.MongoDBNoSqlResourceProviderFactory; + +import com.google.common.collect.ImmutableMap; + +/** + * Test basic ResourceResolver and ValueMap with different data types. + */ +public class MongoDBNoSqlResourceProviderIT extends AbstractNoSqlResourceProviderTest { + + private Resource testRoot; + + @Override + protected void registerResourceProviderFactory() { + context.registerInjectActivateService(new MongoDBNoSqlResourceProviderFactory(), ImmutableMap.<String, Object>builder() + .put(ResourceProvider.ROOTS, "/test") + .put("connectionString", System.getProperty("connectionString", "localhost:27017")) + .put("database", System.getProperty("database", "sling")) + .put("collection", System.getProperty("collection", "resources")) + .build()); + } + + @Override + protected Resource testRoot() { + if (this.testRoot == null) { + try { + Resource root = context.resourceResolver().getResource("/"); + Resource providerRoot = root.getChild("test"); + if (providerRoot == null) { + providerRoot = context.resourceResolver().create(root, "test", + ImmutableMap.<String, Object>of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED)); + } + this.testRoot = context.resourceResolver().create(providerRoot, UUID.randomUUID().toString(), + ImmutableMap.<String, Object>of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED)); + } + catch (PersistenceException ex) { + throw new RuntimeException(ex); + } + } + return this.testRoot; + } + +} diff --git a/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderRootIT.java b/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderRootIT.java new file mode 100644 index 0000000..4a28224 --- /dev/null +++ b/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderRootIT.java @@ -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. + */ +package org.apache.sling.nosql.mongodb.resourceprovider.integration; + +import org.apache.sling.api.resource.ResourceProvider; +import org.apache.sling.nosql.generic.resource.impl.AbstractNoSqlResourceProviderRootTest; +import org.apache.sling.nosql.mongodb.resourceprovider.impl.MongoDBNoSqlResourceProviderFactory; + +import com.google.common.collect.ImmutableMap; + +/** + * Test basic ResourceResolver and ValueMap with different data types. + */ +public class MongoDBNoSqlResourceProviderRootIT extends AbstractNoSqlResourceProviderRootTest { + + @Override + protected void registerResourceProviderFactoryAsRoot() { + context.registerInjectActivateService(new MongoDBNoSqlResourceProviderFactory(), ImmutableMap.<String, Object>builder() + .put(ResourceProvider.ROOTS, "/") + .put("connectionString", System.getProperty("connectionString", "localhost:27017")) + .put("database", System.getProperty("database", "sling")) + .put("collection", System.getProperty("collection", "resources")) + .build()); + } + +} diff --git a/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderTransactionalIT.java b/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderTransactionalIT.java new file mode 100644 index 0000000..9325496 --- /dev/null +++ b/src/test/java/org/apache/sling/nosql/mongodb/resourceprovider/integration/MongoDBNoSqlResourceProviderTransactionalIT.java @@ -0,0 +1,69 @@ +/* + * 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.nosql.mongodb.resourceprovider.integration; + +import java.util.UUID; + +import org.apache.jackrabbit.JcrConstants; +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.nosql.generic.resource.impl.AbstractNoSqlResourceProviderTransactionalTest; +import org.apache.sling.nosql.mongodb.resourceprovider.impl.MongoDBNoSqlResourceProviderFactory; + +import com.google.common.collect.ImmutableMap; + +/** + * Test basic ResourceResolver and ValueMap with different data types. + */ +public class MongoDBNoSqlResourceProviderTransactionalIT extends AbstractNoSqlResourceProviderTransactionalTest { + + private Resource testRoot; + + @Override + protected void registerResourceProviderFactory() { + context.registerInjectActivateService(new MongoDBNoSqlResourceProviderFactory(), ImmutableMap.<String, Object>builder() + .put(ResourceProvider.ROOTS, "/test") + .put("connectionString", System.getProperty("connectionString", "localhost:27017")) + .put("database", System.getProperty("database", "sling")) + .put("collection", System.getProperty("collection", "resources")) + .build()); + } + + @Override + protected Resource testRoot() { + if (this.testRoot == null) { + try { + Resource root = context.resourceResolver().getResource("/"); + Resource providerRoot = root.getChild("test"); + if (providerRoot == null) { + providerRoot = context.resourceResolver().create(root,"test", + ImmutableMap.<String, Object>of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED)); + } + this.testRoot = context.resourceResolver().create(providerRoot, UUID.randomUUID().toString(), + ImmutableMap.<String, Object>of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED)); + } + catch (PersistenceException ex) { + throw new RuntimeException(ex); + } + } + return this.testRoot; + } + +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
