This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.nosql.couchbase-resourceprovider-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-nosql-couchbase-resourceprovider.git
commit 9ae2be2d2ba1003e3801eb074822494213692cba Author: Stefan Seifert <sseif...@apache.org> AuthorDate: Wed May 20 14:11:43 2015 +0000 SLING-4381 couchbase resource provider git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/nosql/couchbase-resourceprovider@1680578 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 119 +++++++++++++++++ src/main/couchbase-views/ancestorPath.js | 22 ++++ src/main/couchbase-views/ancestorPathTester.html | 66 ++++++++++ src/main/couchbase-views/parentPath.js | 19 +++ src/main/couchbase-views/parentPathTester.html | 64 +++++++++ .../impl/CouchbaseNoSqlAdapter.java | 146 +++++++++++++++++++++ .../CouchbaseNoSqlResourceProviderFactory.java | 118 +++++++++++++++++ .../resourceprovider/impl/MapConverter.java | 79 +++++++++++ .../resourceprovider/impl/MapConverterTest.java | 79 +++++++++++ .../CouchbaseNoSqlResourceProviderIT.java | 76 +++++++++++ ...chbaseNoSqlResourceProviderTransactionalIT.java | 76 +++++++++++ 11 files changed, 864 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8df9d96 --- /dev/null +++ b/pom.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>22</version> + <relativePath/> + </parent> + + <artifactId>org.apache.sling.nosql.couchbase-resourceprovider</artifactId> + <version>0.5.0-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>Apache Sling NoSQL Couchbase Resource Provider</name> + <description>Resource Provider with Couchbase Persistence for Apache Sling.</description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/contrib/nosql/couchbase-resourceprovider</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/contrib/nosql/couchbase-resourceprovider</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/contrib/nosql/couchbase-resourceprovider</url> + </scm> + + <properties> + <sling.java.version>7</sling.java.version> + </properties> + + <dependencies> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.nosql.couchbase-client</artifactId> + <version>0.5.0-SNAPSHOT</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.nosql.generic</artifactId> + <version>0.5.0-SNAPSHOT</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.2</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.3.2</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.testing.sling-mock</artifactId> + <version>1.2.1-SNAPSHOT</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.nosql.generic</artifactId> + <classifier>tests</classifier> + <version>0.5.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>javax.jcr</groupId> + <artifactId>jcr</artifactId> + <version>2.0</version> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <plugins> + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + </plugin> + + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + + </plugins> + </build> + +</project> diff --git a/src/main/couchbase-views/ancestorPath.js b/src/main/couchbase-views/ancestorPath.js new file mode 100644 index 0000000..6302a19 --- /dev/null +++ b/src/main/couchbase-views/ancestorPath.js @@ -0,0 +1,22 @@ +/* + * Emits for each document the all parent paths - allowing to fetch children and their decendants by path. + * Includes the path of the item itself. + */ +function(doc, meta) { + + // handle only sling resource documents with a valid path + if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) { + return; + } + var pathParts = doc.path.split("/"); + if (pathParts.length < 3) { + return; + } + + while (pathParts.length >= 2) { + // remove last element to get parent path + var parentPath = pathParts.join("/"); + emit(parentPath, null); + pathParts.pop(); + } +} diff --git a/src/main/couchbase-views/ancestorPathTester.html b/src/main/couchbase-views/ancestorPathTester.html new file mode 100644 index 0000000..d07e5d9 --- /dev/null +++ b/src/main/couchbase-views/ancestorPathTester.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> + <head> + <title>Couchbase View Tester</title> + <style>body { font-family: Courier }</style> + </head> + <body> + +<script> + +var emit = function(key, value) { + document.write("[" + key + "]" + "<br/>"); +} + +var testFunction = function(doc, meta) { + + // handle only sling resource documents with a valid path + if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) { + return; + } + var pathParts = doc.path.split("/"); + if (pathParts.length < 3) { + return; + } + + while (pathParts.length >= 2) { + // remove last element to get parent path + var parentPath = pathParts.join("/"); + emit(parentPath, null); + pathParts.pop(); + } +}; + +var testInput = [ + null, + "", + "abc", + "/", + "/content", + "/content/node1", + "/content/node1/node2", + "/content/node1/node2/node3", + "/content/node1/node2/node3/node4" +]; + +</script> + + <table border="1"> + <tr> + <th>Input</th> + <th>Output</th> + </tr> +<script> +for (var i=0; i < testInput.length; i++) { + document.write("<tr>") + document.write("<td>" + testInput[i] + "</td>") + document.write("<td>") + testFunction({path: testInput[i], data: {}}, {id: "sling-resource:doc" + i}); + document.write("</td>") + document.write("</tr>") +} +</script> + </table> + + </body> +</html> diff --git a/src/main/couchbase-views/parentPath.js b/src/main/couchbase-views/parentPath.js new file mode 100644 index 0000000..5277818 --- /dev/null +++ b/src/main/couchbase-views/parentPath.js @@ -0,0 +1,19 @@ +/* + * Emits for each document the direct parent path - allowing to fetch direct children by path. + */ +function(doc, meta) { + + // handle only sling resource documents with a valid path + if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) { + return; + } + var pathParts = doc.path.split("/"); + if (pathParts.length < 3) { + return; + } + + // remove last element to get parent path + pathParts.pop(); + var parentPath = pathParts.join("/"); + emit(parentPath, null); +} diff --git a/src/main/couchbase-views/parentPathTester.html b/src/main/couchbase-views/parentPathTester.html new file mode 100644 index 0000000..34e037f --- /dev/null +++ b/src/main/couchbase-views/parentPathTester.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> + <head> + <title>Couchbase View Tester</title> + <style>body { font-family: Courier }</style> + </head> + <body> + +<script> + +var emit = function(key, value) { + document.write("[" + key + "]" + "<br/>"); +} + +var testFunction = function(doc, meta) { + + // handle only sling resource documents with a valid path + if (!(meta.id.indexOf("sling-resource:")==0 && doc.path && doc.data)) { + return; + } + var pathParts = doc.path.split("/"); + if (pathParts.length < 3) { + return; + } + + // remove last element to get parent path + pathParts.pop(); + var parentPath = pathParts.join("/"); + emit(parentPath, null); +}; + +var testInput = [ + null, + "", + "abc", + "/", + "/content", + "/content/node1", + "/content/node1/node2", + "/content/node1/node2/node3", + "/content/node1/node2/node3/node4" +]; + +</script> + + <table border="1"> + <tr> + <th>Input</th> + <th>Output</th> + </tr> +<script> +for (var i=0; i < testInput.length; i++) { + document.write("<tr>") + document.write("<td>" + testInput[i] + "</td>") + document.write("<td>") + testFunction({path: testInput[i], data: {}}, {id: "sling-resource:doc" + i}); + document.write("</td>") + document.write("</tr>") +} +</script> + </table> + + </body> +</html> diff --git a/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.java b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.java new file mode 100644 index 0000000..14f4e72 --- /dev/null +++ b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlAdapter.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.nosql.couchbase.resourceprovider.impl; + +import java.util.Iterator; + +import org.apache.sling.nosql.couchbase.client.CouchbaseClient; +import org.apache.sling.nosql.couchbase.client.CouchbaseKey; +import org.apache.sling.nosql.generic.adapter.NoSqlAdapter; +import org.apache.sling.nosql.generic.adapter.NoSqlData; + +import com.couchbase.client.java.Bucket; +import com.couchbase.client.java.document.JsonDocument; +import com.couchbase.client.java.document.json.JsonObject; +import com.couchbase.client.java.error.DocumentAlreadyExistsException; +import com.couchbase.client.java.view.Stale; +import com.couchbase.client.java.view.ViewQuery; +import com.couchbase.client.java.view.ViewRow; + +class CouchbaseNoSqlAdapter implements NoSqlAdapter { + + private static final String PN_PATH = "path"; + private static final String PN_DATA = "data"; + + private static final String VIEW_DESIGN_DOCUMENT = "resourceIndex"; + private static final String VIEW_PARENT_PATH = "parentPath"; + private static final String VIEW_ANCESTOR_PATH = "ancestorPath"; + + private final CouchbaseClient couchbaseClient; + private final String cacheKeyPrefix; + + public CouchbaseNoSqlAdapter(CouchbaseClient couchbaseClient, String cacheKeyPrefix) { + this.couchbaseClient = couchbaseClient; + this.cacheKeyPrefix = cacheKeyPrefix; + } + + @Override + public boolean validPath(String path) { + return (couchbaseClient != null); + } + + @Override + public NoSqlData get(String path) { + Bucket bucket = couchbaseClient.getBucket(); + String cacheKey = CouchbaseKey.build(path, cacheKeyPrefix); + JsonDocument doc = bucket.get(cacheKey); + if (doc == null) { + return null; + } + else { + JsonObject data = doc.content().getObject(PN_DATA); + if (data == null) { + return null; + } + else { + return new NoSqlData(path, MapConverter.mapListToArray(data.toMap())); + } + } + } + + @Override + public Iterator<NoSqlData> getChildren(String parentPath) { + Bucket bucket = couchbaseClient.getBucket(); + // fetch all direct children of this path + final Iterator<ViewRow> results = bucket.query( + ViewQuery.from(VIEW_DESIGN_DOCUMENT, VIEW_PARENT_PATH).key(parentPath).stale(Stale.FALSE)).rows(); + return new Iterator<NoSqlData>() { + @Override + public boolean hasNext() { + return results.hasNext(); + } + + @Override + public NoSqlData next() { + JsonDocument doc = results.next().document(); + JsonObject envelope = doc.content(); + String path = envelope.getString(PN_PATH); + JsonObject data = envelope.getObject(PN_DATA); + return new NoSqlData(path, MapConverter.mapListToArray(data.toMap())); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public boolean store(NoSqlData data) { + Bucket bucket = couchbaseClient.getBucket(); + String cacheKey = CouchbaseKey.build(data.getPath(), cacheKeyPrefix); + + JsonObject envelope = JsonObject.create(); + envelope.put(PN_PATH, data.getPath()); + envelope.put(PN_DATA, JsonObject.from(MapConverter.mapArrayToList(data.getProperties()))); + + JsonDocument doc = JsonDocument.create(cacheKey, envelope); + try { + bucket.insert(doc); + return true; // created + } + catch (DocumentAlreadyExistsException ex) { + bucket.upsert(doc); + return false; // updated + } + } + + @Override + public boolean deleteRecursive(String path) { + Bucket bucket = couchbaseClient.getBucket(); + // fetch referenced item and all descendants + Iterator<ViewRow> results = bucket.query( + ViewQuery.from(VIEW_DESIGN_DOCUMENT, VIEW_ANCESTOR_PATH).key(path).stale(Stale.FALSE)).rows(); + boolean deletedAny = false; + while (results.hasNext()) { + ViewRow result = results.next(); + bucket.remove(result.document()); + deletedAny = true; + } + return deletedAny; + } + + @Override + public Iterator<NoSqlData> query(String query, String language) { + // not supported + return null; + } + +} diff --git a/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlResourceProviderFactory.java b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlResourceProviderFactory.java new file mode 100644 index 0000000..3f20e6c --- /dev/null +++ b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/CouchbaseNoSqlResourceProviderFactory.java @@ -0,0 +1,118 @@ +/* + * 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.couchbase.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.Deactivate; +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.couchbase.client.CouchbaseClient; +import org.apache.sling.nosql.generic.adapter.NoSqlAdapter; +import org.apache.sling.nosql.generic.resource.AbstractNoSqlResourceProviderFactory; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.event.EventAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link ResourceProviderFactory} implementation that uses couchbase as + * persistence. + */ +@Component(immediate = true) +@Service(value = ResourceProviderFactory.class) +public class CouchbaseNoSqlResourceProviderFactory extends AbstractNoSqlResourceProviderFactory { + + @Property(label = "Couchbase Client ID", description = "ID referencing the matching couchbase client configuration and bucket for caching.", value = CouchbaseNoSqlResourceProviderFactory.COUCHBASE_CLIENT_ID_DEFAULT) + static final String COUCHBASE_CLIENT_ID_PROPERTY = "couchbaseClientID"; + private static final String COUCHBASE_CLIENT_ID_DEFAULT = "caravan-resourceprovider-couchbase"; + + @Property(label = "Cache Key Prefix", description = "Prefix for caching keys.", value = CouchbaseNoSqlResourceProviderFactory.CACHE_KEY_PREFIX_DEFAULT) + static final String CACHE_KEY_PREFIX_PROPERTY = "cacheKeyPrefix"; + private static final String CACHE_KEY_PREFIX_DEFAULT = "sling-resource:"; + + @Property(label = "Root paths", description = "Root paths for resource provider.", cardinality = Integer.MAX_VALUE) + static final String PROVIDER_ROOTS_PROPERTY = ResourceProvider.ROOTS; + + @Reference + private EventAdmin eventAdmin; + + private String couchbaseClientId; + private ServiceReference couchbaseClientServiceReference; + private NoSqlAdapter noSqlAdapter; + + private static final Logger log = LoggerFactory.getLogger(CouchbaseNoSqlResourceProviderFactory.class); + + @Activate + private void activate(ComponentContext componentContext, Map<String, Object> config) { + CouchbaseClient couchbaseClient = null; + try { + couchbaseClientId = PropertiesUtil.toString(config.get(COUCHBASE_CLIENT_ID_PROPERTY), + COUCHBASE_CLIENT_ID_DEFAULT); + BundleContext bundleContext = componentContext.getBundleContext(); + ServiceReference[] serviceReferences = bundleContext.getServiceReferences( + CouchbaseClient.class.getName(), "(" + CouchbaseClient.CLIENT_ID_PROPERTY + "=" + couchbaseClientId + ")"); + if (serviceReferences.length == 1) { + couchbaseClientServiceReference = serviceReferences[0]; + couchbaseClient = (CouchbaseClient)bundleContext.getService(couchbaseClientServiceReference); + } + else if (serviceReferences.length > 1) { + log.error("Multiple couchbase clients registered for client id '{}', caching is disabled.", + couchbaseClientId); + } + else { + log.error("No couchbase clients registered for client id '{}', caching is disabled.", couchbaseClientId); + } + } + catch (InvalidSyntaxException ex) { + log.error("Invalid service filter, couchbase caching is disabled.", ex); + } + + String cacheKeyPrefix = PropertiesUtil + .toString(config.get(CACHE_KEY_PREFIX_PROPERTY), CACHE_KEY_PREFIX_DEFAULT); + noSqlAdapter = new CouchbaseNoSqlAdapter(couchbaseClient, cacheKeyPrefix); + } + + @Deactivate + private void deactivate(ComponentContext componentContext) { + if (couchbaseClientServiceReference != null) { + componentContext.getBundleContext().ungetService(couchbaseClientServiceReference); + } + } + + @Override + protected NoSqlAdapter getNoSqlAdapter() { + return noSqlAdapter; + } + + @Override + protected EventAdmin getEventAdmin() { + return eventAdmin; + } + +} diff --git a/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/MapConverter.java b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/MapConverter.java new file mode 100644 index 0000000..f1e4ac4 --- /dev/null +++ b/src/main/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/MapConverter.java @@ -0,0 +1,79 @@ +/* + * 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.couchbase.resourceprovider.impl; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * Transforms NoSqlData maps to a valid form for couchbase JSON document. + * All arrays have to be transformed to lists. + */ +final class MapConverter { + + private MapConverter() { + // static methods only + } + + static Map<String, Object> mapArrayToList(Map<String, Object> map) { + for (Map.Entry<String, Object> entry : map.entrySet()) { + if (entry.getValue().getClass().isArray()) { + Class componentType = entry.getValue().getClass().getComponentType(); + if (componentType == int.class) { + entry.setValue(Arrays.asList(ArrayUtils.toObject((int[]) entry.getValue()))); + } + else if (componentType == long.class) { + entry.setValue(Arrays.asList(ArrayUtils.toObject((long[]) entry.getValue()))); + } + else if (componentType == double.class) { + entry.setValue(Arrays.asList(ArrayUtils.toObject((double[]) entry.getValue()))); + } + else if (componentType == boolean.class) { + entry.setValue(Arrays.asList(ArrayUtils.toObject((boolean[]) entry.getValue()))); + } + else { + entry.setValue(Arrays.asList((Object[]) entry.getValue())); + } + } + } + return map; + } + + @SuppressWarnings("unchecked") + static Map<String, Object> mapListToArray(Map<String, Object> map) { + for (Map.Entry<String, Object> entry : map.entrySet()) { + if (entry.getValue() instanceof List) { + List list = (List) entry.getValue(); + if (list.size() == 0) { + entry.setValue(null); + } + else { + Class type = list.get(0).getClass(); + entry.setValue(list.toArray((Object[]) Array.newInstance(type, list.size()))); + } + } + } + return map; + } + +} diff --git a/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/MapConverterTest.java b/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/MapConverterTest.java new file mode 100644 index 0000000..1f8e1ee --- /dev/null +++ b/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/impl/MapConverterTest.java @@ -0,0 +1,79 @@ +/* + * 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.couchbase.resourceprovider.impl; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.util.Map; + +import org.apache.sling.nosql.couchbase.resourceprovider.impl.MapConverter; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +public class MapConverterTest { + + @Test + public void testMapArrayToList() throws Exception { + Map<String, Object> result = MapConverter.mapArrayToList(Maps.newHashMap(ImmutableMap.<String, Object>builder() + .put("prop1", "value1") + .put("prop2", 2) + .put("stringArray", new String[] { "value1", "value2" }) + .put("integerArray", new Integer[] { 1, 2, 3 }) + .put("integerArray2", new int[] { 1, 2, 3 }) + .put("longArray", new long[] { 1L, 2L }) + .put("doubleArray", new double[] { 1.1d, 1.2d }) + .put("booleanArray", new boolean[] { true, false }) + .build())); + + assertEquals("prop1", "value1", result.get("prop1")); + assertEquals("prop2", 2, result.get("prop2")); + assertEquals("stringArray", ImmutableList.of("value1", "value2"), result.get("stringArray")); + assertEquals("integerArray", ImmutableList.of(1, 2, 3), result.get("integerArray")); + assertEquals("integerArray2", ImmutableList.of(1, 2, 3), result.get("integerArray2")); + assertEquals("longArray", ImmutableList.of(1L, 2L), result.get("longArray")); + assertEquals("doubleArray", ImmutableList.of(1.1d, 1.2d), result.get("doubleArray")); + assertEquals("booleanArray", ImmutableList.of(true, false), result.get("booleanArray")); + } + + @Test + public void testMapListToArray() throws Exception { + Map<String, Object> result = MapConverter.mapListToArray(Maps.newHashMap(ImmutableMap.<String, Object>builder() + .put("prop1", "value1") + .put("prop2", 2) + .put("stringArray", ImmutableList.of("value1", "value2")) + .put("integerArray", ImmutableList.of(1, 2, 3)) + .put("longArray", ImmutableList.of(1L, 2L)) + .put("doubleArray", ImmutableList.of(1.1d, 1.2d)) + .put("booleanArray", ImmutableList.of(true, false)) + .build())); + + assertEquals("prop1", "value1", result.get("prop1")); + assertEquals("prop2", 2, result.get("prop2")); + assertArrayEquals("stringArray", new String[] { "value1", "value2" }, (String[]) result.get("stringArray")); + assertArrayEquals("integerArray", new Integer[] { 1, 2, 3 }, (Integer[]) result.get("integerArray")); + assertArrayEquals("longArray", new Long[] { 1L, 2L }, (Long[]) result.get("longArray")); + assertArrayEquals("doubleArray", new Double[] { 1.1d, 1.2d }, (Double[]) result.get("doubleArray")); + assertArrayEquals("booleanArray", new Boolean[] { true, false }, (Boolean[]) result.get("booleanArray")); + } + +} diff --git a/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/integration/CouchbaseNoSqlResourceProviderIT.java b/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/integration/CouchbaseNoSqlResourceProviderIT.java new file mode 100644 index 0000000..ed64041 --- /dev/null +++ b/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/integration/CouchbaseNoSqlResourceProviderIT.java @@ -0,0 +1,76 @@ +/* + * 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.couchbase.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.couchbase.client.CouchbaseClient; +import org.apache.sling.nosql.couchbase.client.impl.CouchbaseClientImpl; +import org.apache.sling.nosql.couchbase.resourceprovider.impl.CouchbaseNoSqlResourceProviderFactory; +import org.apache.sling.nosql.generic.resource.impl.AbstractNoSqlResourceProviderTest; + +import com.google.common.collect.ImmutableMap; + +/** + * Test basic ResourceResolver and ValueMap with different data types. + */ +public class CouchbaseNoSqlResourceProviderIT extends AbstractNoSqlResourceProviderTest { + + private Resource testRoot; + + @Override + protected void registerResourceProviderFactory() { + context.registerInjectActivateService( + new CouchbaseClientImpl(), + ImmutableMap.<String, Object> builder() + .put(CouchbaseClient.CLIENT_ID_PROPERTY, "caravan-resourceprovider-couchbase") + .put("couchbaseHosts", System.getProperty("couchbaseHosts", "localhost:8091")) + .put("bucketName", System.getProperty("bucketName", "resource-test")).build()); + + context.registerInjectActivateService(new CouchbaseNoSqlResourceProviderFactory(), ImmutableMap + .<String, Object> builder().put(ResourceProvider.ROOTS, "/test").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/couchbase/resourceprovider/integration/CouchbaseNoSqlResourceProviderTransactionalIT.java b/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/integration/CouchbaseNoSqlResourceProviderTransactionalIT.java new file mode 100644 index 0000000..3c62e9d --- /dev/null +++ b/src/test/java/org/apache/sling/nosql/couchbase/resourceprovider/integration/CouchbaseNoSqlResourceProviderTransactionalIT.java @@ -0,0 +1,76 @@ +/* + * 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.couchbase.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.couchbase.client.CouchbaseClient; +import org.apache.sling.nosql.couchbase.client.impl.CouchbaseClientImpl; +import org.apache.sling.nosql.couchbase.resourceprovider.impl.CouchbaseNoSqlResourceProviderFactory; +import org.apache.sling.nosql.generic.resource.impl.AbstractNoSqlResourceProviderTransactionalTest; + +import com.google.common.collect.ImmutableMap; + +/** + * Test basic ResourceResolver and ValueMap with different data types. + */ +public class CouchbaseNoSqlResourceProviderTransactionalIT extends AbstractNoSqlResourceProviderTransactionalTest { + + private Resource testRoot; + + @Override + protected void registerResourceProviderFactory() { + context.registerInjectActivateService( + new CouchbaseClientImpl(), + ImmutableMap.<String, Object> builder() + .put(CouchbaseClient.CLIENT_ID_PROPERTY, "caravan-resourceprovider-couchbase") + .put("couchbaseHosts", System.getProperty("couchbaseHosts", "localhost:8091")) + .put("bucketName", System.getProperty("bucketName", "resource-test")).build()); + + context.registerInjectActivateService(new CouchbaseNoSqlResourceProviderFactory(), ImmutableMap + .<String, Object> builder().put(ResourceProvider.ROOTS, "/test").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 "commits@sling.apache.org" <commits@sling.apache.org>.