Repository: gora Updated Branches: refs/heads/master 228991175 -> 72ce897f9
GORA-437 implement couchdb datastore ,added a new store for gora as gora-couchdb ,added javadocs ,bugfixed Project: http://git-wip-us.apache.org/repos/asf/gora/repo Commit: http://git-wip-us.apache.org/repos/asf/gora/commit/d6c820b2 Tree: http://git-wip-us.apache.org/repos/asf/gora/tree/d6c820b2 Diff: http://git-wip-us.apache.org/repos/asf/gora/diff/d6c820b2 Branch: refs/heads/master Commit: d6c820b227ee1eea298126414e2e133096f7698a Parents: 4192c87 Author: cihad guzel <[email protected]> Authored: Thu May 19 10:16:34 2016 +0300 Committer: cihad guzel <[email protected]> Committed: Sun Aug 21 22:56:02 2016 +0300 ---------------------------------------------------------------------- gora-couchdb/pom.xml | 229 +++++++ .../apache/gora/couchdb/query/CouchDBQuery.java | 43 ++ .../gora/couchdb/query/CouchDBResult.java | 90 +++ .../gora/couchdb/store/CouchDBMapping.java | 56 ++ .../couchdb/store/CouchDBMappingBuilder.java | 106 ++++ .../gora/couchdb/store/CouchDBParameters.java | 82 +++ .../apache/gora/couchdb/store/CouchDBStore.java | 628 +++++++++++++++++++ .../util/CouchDBObjectMapperFactory.java | 69 ++ .../src/test/conf/gora-couchdb-mapping.xml | 47 ++ gora-couchdb/src/test/conf/gora.properties | 19 + .../gora/couchdb/GoraCouchDBTestDriver.java | 78 +++ .../org/apache/gora/couchdb/package-info.java | 21 + pom.xml | 7 + 13 files changed, 1475 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/pom.xml ---------------------------------------------------------------------- diff --git a/gora-couchdb/pom.xml b/gora-couchdb/pom.xml new file mode 100644 index 0000000..69307a6 --- /dev/null +++ b/gora-couchdb/pom.xml @@ -0,0 +1,229 @@ +<?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.gora</groupId> + <artifactId>gora</artifactId> + <version>0.7-SNAPSHOT</version> + <relativePath>../</relativePath> + </parent> + <artifactId>gora-couchdb</artifactId> + <packaging>bundle</packaging> + + <name>Apache Gora :: CouchDB</name> + <url>http://gora.apache.org</url> + <description>The Apache Gora open source framework provides an in-memory data model and + persistence for big data. Gora supports persisting to column stores, key value stores, + document stores and RDBMSs, and analyzing the data with extensive Apache Hadoop MapReduce + support.</description> + <inceptionYear>2010</inceptionYear> + <organization> + <name>The Apache Software Foundation</name> + <url>http://www.apache.org/</url> + </organization> + <issueManagement> + <system>JIRA</system> + <url>https://issues.apache.org/jira/browse/GORA</url> + </issueManagement> + <ciManagement> + <system>Jenkins</system> + <url>https://builds.apache.org/job/Gora-trunk/</url> + </ciManagement> + + <properties> + <osgi.import>*</osgi.import> + <osgi.export>org.apache.gora.couchdb*;version="${project.version}";-noimport:=true</osgi.export> + </properties> + + <profiles> + <profile> + <id>couchdb-with-test</id> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.4.2</version> + <configuration> + <skipTests>false</skipTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> + + <build> + <directory>target</directory> + <outputDirectory>target/classes</outputDirectory> + <finalName>${project.artifactId}-${project.version}</finalName> + <testOutputDirectory>target/test-classes</testOutputDirectory> + <testSourceDirectory>src/test/java</testSourceDirectory> + <sourceDirectory>src/main/java</sourceDirectory> + <testResources> + <testResource> + <directory>${project.basedir}/src/test/conf</directory> + <includes> + <include>**/*</include> + </includes> + </testResource> + </testResources> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.4.2</version> + <configuration> + <skipTests>true</skipTests> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>${build-helper-maven-plugin.version}</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>src/examples/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <repositories> + <repository> + <id>clojars.org</id> + <url>http://clojars.org/repo</url> + </repository> + </repositories> + + <dependencies> + <!-- Gora Internal Dependencies --> + <dependency> + <groupId>org.apache.gora</groupId> + <artifactId>gora-core</artifactId> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>org.apache.gora</groupId> + <artifactId>gora-core</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + + + <dependency> + <groupId>org.apache.gora</groupId> + <artifactId>gora-core</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + + <!-- END of Gora Internal Dependencies --> + + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-client</artifactId> + <scope>compile</scope> + <optional>true</optional> + </dependency> + + <!-- Apache CouchDB java client --> + <dependency> + <groupId>org.ektorp</groupId> + <artifactId>org.ektorp</artifactId> + <version>1.4.2</version> + </dependency> + + + <dependency> + <groupId>org.apache.avro</groupId> + <artifactId>avro</artifactId> + <scope>compile</scope> + </dependency> + + <!-- Misc Dependencies --> + <dependency> + <groupId>org.jdom</groupId> + <artifactId>jdom</artifactId> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <scope>compile</scope> + </dependency> + + <!-- Logging Dependencies --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>runtime</scope> + <exclusions> + <exclusion> + <groupId>javax.jms</groupId> + <artifactId>jms</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- END of Logging Dependencies --> + + <!-- Testing Dependencies --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-minicluster</artifactId> + <scope>test</scope> + </dependency> + + <!-- END of Testing Dependencies --> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBQuery.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBQuery.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBQuery.java new file mode 100644 index 0000000..69319c3 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBQuery.java @@ -0,0 +1,43 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.query; + +import org.apache.gora.couchdb.store.CouchDBStore; +import org.apache.gora.persistency.impl.PersistentBase; +import org.apache.gora.query.Query; +import org.apache.gora.query.impl.QueryBase; +import org.apache.gora.store.DataStore; + +/** + * CouchDB specific implementation of the {@link Query} interface. + */ +public class CouchDBQuery<K, T extends PersistentBase> extends QueryBase<K, T> { + + final CouchDBStore store; + + /** + * Constructor for the query + * + * @param dataStore Data store used + * + */ + public CouchDBQuery(DataStore<K, T> dataStore) { + super(dataStore); + store = (CouchDBStore) dataStore; + } +} http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBResult.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBResult.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBResult.java new file mode 100644 index 0000000..7d43682 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/query/CouchDBResult.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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.query; + +import org.apache.gora.couchdb.store.CouchDBStore; +import org.apache.gora.persistency.Persistent; +import org.apache.gora.query.Query; +import org.apache.gora.query.impl.ResultBase; +import org.apache.gora.store.DataStore; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * CouchDB specific implementation of the {@link org.apache.gora.query.Result} + * interface. + */ +public class CouchDBResult<K, T extends Persistent> extends ResultBase<K, T> { + + /** + * Result set containing query results + */ + private List<Map> result; + + protected CouchDBStore dataStore; + int position = 0; + + /** + * Constructor for the result set + * + * @param dataStore Data store used + * @param query Query used + * @param result Result obtained from querying + */ + public CouchDBResult(DataStore<K, T> dataStore, Query<K, T> query, List<Map> result) { + super(dataStore, query); + this.result = result; + this.dataStore = (CouchDBStore) dataStore; + } + + /** + * Gets the next item + */ + @Override + protected boolean nextInner() throws IOException { + if (result == null || result.size() <= 0 || position >= result.size()) { + return false; + } + key = (K) result.get(position).get("_id"); + persistent = (T) dataStore.newInstance(result.get(position++), query.getFields()); + return persistent != null; + } + + /** + * Gets the items reading progress + */ + @Override + public float getProgress() throws IOException, InterruptedException { + if (result != null && result.size() > 0) { + return (float) position / (float) result.size(); + } else { + return 0; + } + } + + /** + * Result set containing query results + * + * @return Result set containing query results + */ + public List<Map> getResultData() { + return result; + } +} http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMapping.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMapping.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMapping.java new file mode 100644 index 0000000..502cb24 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMapping.java @@ -0,0 +1,56 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.store; + +import org.jdom.Element; + +import java.util.List; + +/** + * Mapping definitions for CouchDB. + */ +public class CouchDBMapping { + + /** + * Name of the database this mapping is linked to + */ + public String databaseName; + + /** + * CouchDB document description + */ + public List<Element> fields; + + /** + * Name of the database this mapping is linked to + * + * @return name as String + */ + public String getDatabaseName() { + return databaseName; + } + + /** + * Setter for the name of the database + * + * @param databaseName name of the database + */ + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + +} http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMappingBuilder.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMappingBuilder.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMappingBuilder.java new file mode 100644 index 0000000..715add6 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBMappingBuilder.java @@ -0,0 +1,106 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.store; + +import org.apache.gora.persistency.impl.PersistentBase; +import org.jdom.Element; +import org.jdom.input.SAXBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * A builder for creating the mapper. + */ +public class CouchDBMappingBuilder<K, T extends PersistentBase> { + + private static final Logger LOG = LoggerFactory.getLogger(CouchDBMappingBuilder.class); + + // Class description + private static final String TAG_CLASS = "class"; + private static final String TAG_FIELD = "field"; + private static final String ATT_KEYCLASS = "keyClass"; + private static final String ATT_DOCUMENT = "document"; + + // Document description + private static final String ATT_NAME = "name"; + + /** + * Mapping instance being built + */ + private final CouchDBMapping mapping; + + private final CouchDBStore<K, T> dataStore; + + /** + * Constructor for builder to create the mapper. + * + * @param store + */ + public CouchDBMappingBuilder(final CouchDBStore<K, T> store) { + this.dataStore = store; + this.mapping = new CouchDBMapping(); + } + + /** + * Return the built mapping if it is in a legal state + */ + public CouchDBMapping build() { + if (mapping.getDatabaseName() == null) { + LOG.error("A collection is not specified"); + throw new IllegalStateException("A collection is not specified"); + } + return mapping; + } + + /** + * Load the {@link org.apache.gora.couchdb.store.CouchDBMapping} from a file + * passed in parameter. + * + * @param filename path to the file holding the mapping + * @throws java.io.IOException + */ + protected void readMapping(String filename) throws IOException { + try { + final Class<T> persistentClass = dataStore.getPersistentClass(); + final Class<K> keyClass = dataStore.getKeyClass(); + + final SAXBuilder saxBuilder = new SAXBuilder(); + final InputStream is = getClass().getClassLoader().getResourceAsStream(filename); + + final Element root = saxBuilder.build(is).getRootElement(); + final List<Element> classElements = root.getChildren(TAG_CLASS); + + for (Element classElement : classElements) { + if (classElement.getAttributeValue(ATT_NAME).equals(persistentClass.getName()) && + classElement.getAttributeValue(ATT_KEYCLASS).equals(keyClass.getName())) { + mapping + .setDatabaseName(dataStore.getSchemaName(classElement.getAttributeValue(ATT_DOCUMENT), persistentClass)); + mapping.fields = classElement.getChildren(TAG_FIELD); + break; + } + } + } catch (Exception ex) { + CouchDBStore.LOG.error(ex.getMessage(), ex); + throw new IOException(ex); + } + } +} http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBParameters.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBParameters.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBParameters.java new file mode 100644 index 0000000..d73f430 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBParameters.java @@ -0,0 +1,82 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.store; + +import java.util.Properties; + +/** + * Configuration properties + */ +public class CouchDBParameters { + + /** + * Property pointing to the host where the server is running + */ + public static final String PROP_COUCHDB_SERVER = "gora.datastore.couchdb.server"; + + /** + * Property pointing to the port where the server is running + */ + public static final String PROP_COUCHDB_PORT = "gora.datastore.couchdb.port"; + + private final String server; + private final String port; + + /** + * + * Initializing for configuration properties + * + * @param server server domain or ip for couchDB connection + * @param port port for couchDB connection + */ + private CouchDBParameters(String server, String port) { + this.server = server; + this.port = port; + } + + /** + * Get server domain + * + * @return + */ + public String getServer() { + return server; + } + + /** + * Get server port + * + * @return + */ + public int getPort() { + return Integer.parseInt(port); + } + + /** + * load configuration values + * + * @param properties + * @return + */ + public static CouchDBParameters load(Properties properties) { + String couchDBServer = properties.getProperty(PROP_COUCHDB_SERVER); + String couchDBPort = properties.getProperty(PROP_COUCHDB_PORT); + + return new CouchDBParameters(couchDBServer, couchDBPort); + } +} http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBStore.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBStore.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBStore.java new file mode 100644 index 0000000..5ca8af7 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/store/CouchDBStore.java @@ -0,0 +1,628 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.store; + +import com.google.common.primitives.Ints; +import org.apache.avro.Schema; +import org.apache.avro.Schema.Field; +import org.apache.avro.util.Utf8; +import org.apache.commons.lang.StringUtils; +import org.apache.gora.couchdb.query.CouchDBQuery; +import org.apache.gora.couchdb.query.CouchDBResult; +import org.apache.gora.couchdb.util.CouchDBObjectMapperFactory; +import org.apache.gora.persistency.impl.BeanFactoryImpl; +import org.apache.gora.persistency.impl.DirtyListWrapper; +import org.apache.gora.persistency.impl.DirtyMapWrapper; +import org.apache.gora.persistency.impl.PersistentBase; +import org.apache.gora.query.PartitionQuery; +import org.apache.gora.query.Query; +import org.apache.gora.query.Result; +import org.apache.gora.query.impl.PartitionQueryImpl; +import org.apache.gora.store.DataStoreFactory; +import org.apache.gora.store.impl.DataStoreBase; +import org.apache.gora.util.AvroUtils; +import org.apache.gora.util.ClassLoadingUtils; +import org.ektorp.CouchDbConnector; +import org.ektorp.CouchDbInstance; +import org.ektorp.ViewQuery; +import org.ektorp.http.HttpClient; +import org.ektorp.http.StdHttpClient; +import org.ektorp.impl.ObjectMapperFactory; +import org.ektorp.impl.StdCouchDbConnector; +import org.ektorp.impl.StdCouchDbInstance; +import org.ektorp.support.CouchDbDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Implementation of a CouchDB data store to be used by gora. + * + * @param <K> class to be used for the key + * @param <T> class to be persisted within the store + */ +public class CouchDBStore<K, T extends PersistentBase> extends DataStoreBase<K, T> { + + /** + * Logging implementation + */ + protected static final Logger LOG = LoggerFactory.getLogger(CouchDBStore.class); + + /** + * The default file name value to be used for obtaining the CouchDB object field mapping's + */ + public static final String DEFAULT_MAPPING_FILE = "gora-couchdb-mapping.xml"; + + /** + * for bulk document operations + */ + private final List<Object> bulkDocs = new ArrayList<>(); + + /** + * Mapping definition for CouchDB + */ + private CouchDBMapping mapping; + + /** + * The standard implementation of the CouchDbInstance interface. This interface provides methods for + * managing databases on the connected CouchDb instance. + * StdCouchDbInstance is thread-safe. + */ + private CouchDbInstance dbInstance; + + /** + * The standard implementation of the CouchDbConnector interface. This interface provides methods for + * manipulating documents within a specific database. + * StdCouchDbConnector is thread-safe. + */ + private CouchDbConnector db; + + /** + * Initialize the data store by reading the credentials, setting the client's properties up and + * reading the mapping file. Initialize is called when then the call to + * {@link org.apache.gora.store.DataStoreFactory#createDataStore} is made. + * + * @param keyClass + * @param persistentClass + * @param properties + */ + @Override + public void initialize(Class<K> keyClass, Class<T> persistentClass, Properties properties) { + LOG.debug("Initializing CouchDB store"); + super.initialize(keyClass, persistentClass, properties); + + final CouchDBParameters params = CouchDBParameters.load(properties); + + try { + final String mappingFile = DataStoreFactory.getMappingFile(properties, this, DEFAULT_MAPPING_FILE); + final HttpClient httpClient = new StdHttpClient.Builder() + .url("http://" + params.getServer() + ":" + params.getPort()) + .build(); + + dbInstance = new StdCouchDbInstance(httpClient); + + final CouchDBMappingBuilder<K, T> builder = new CouchDBMappingBuilder<>(this); + LOG.debug("Initializing CouchDB store with mapping {}.", new Object[] { mappingFile }); + builder.readMapping(mappingFile); + mapping = builder.build(); + + final ObjectMapperFactory myObjectMapperFactory = new CouchDBObjectMapperFactory(); + myObjectMapperFactory.createObjectMapper().addMixInAnnotations(persistentClass, CouchDbDocument.class); + + db = new StdCouchDbConnector(mapping.getDatabaseName(), dbInstance, myObjectMapperFactory); + db.createDatabaseIfNotExists(); + + } catch (IOException e) { + LOG.error("Error while initializing CouchDB store: {}", new Object[] { e.getMessage() }); + throw new RuntimeException(e); + } + } + + /** + * In CouchDB, Schemas are referred to as database name. + * + * @return databasename + */ + @Override + public String getSchemaName() { + return mapping.getDatabaseName(); + } + + /** + * In CouchDB, Schemas are referred to as database name. + * + * @param mappingSchemaName the name of the schema as read from the mapping file + * @param persistentClass persistent class + * @return database name + */ + @Override + public String getSchemaName(final String mappingSchemaName, final Class<?> persistentClass) { + return super.getSchemaName(mappingSchemaName, persistentClass); + } + + /** + * Create a new database in CouchDB if necessary. + */ + @Override + public void createSchema() { + if (schemaExists()) { + return; + } + dbInstance.createDatabase(mapping.getDatabaseName()); + } + + /** + * Drop the database. + */ + @Override + public void deleteSchema() { + if (schemaExists()) { + dbInstance.deleteDatabase(mapping.getDatabaseName()); + } + } + + /** + * Check if the database already exists or should be created. + */ + @Override + public boolean schemaExists() { + return dbInstance.checkIfDbExists(mapping.getDatabaseName()); + } + + /** + * Retrieve an entry from the store with only selected fields. + * + * @param key identifier of the document in the database + * @param fields list of fields to be loaded from the database + */ + @Override + public T get(final K key, final String[] fields) { + + final Map result; + try { + result = db.get(Map.class, key.toString()); + return newInstance(result, getFieldsToQuery(fields)); + } catch (Exception e) { + LOG.info(e.getMessage(), e); + return null; + } + } + + /** + * Persist an object into the store. + * + * @param key identifier of the object in the store + * @param obj the object to be inserted + */ + @Override + public void put(K key, T obj) { + final Map<String, Object> buffer = Collections.synchronizedMap(new LinkedHashMap<String, Object>()); + buffer.put("_id", key); + + Schema schema = obj.getSchema(); + + List<Field> fields = schema.getFields(); + for (int i = 0; i < fields.size(); i++) { + if (!obj.isDirty(i)) { + continue; + } + Field field = fields.get(i); + Object fieldValue = obj.get(field.pos()); + + Schema fieldSchema = field.schema(); + + // check if field has a nested structure (array, map, record or union) + fieldValue = toDBObject(fieldSchema, fieldValue); + buffer.put(field.name(), fieldValue); + } + bulkDocs.add(buffer); + + } + + private Map<String, Object> mapToCouchDB(final Object fieldValue) { + final Map<String, Object> newMap = new LinkedHashMap<>(); + final Map<?, ?> fieldMap = (Map<?, ?>) fieldValue; + if (fieldValue == null) { + return null; + } + for (Object key : fieldMap.keySet()) { + newMap.put(key.toString(), fieldMap.get(key).toString()); + } + return newMap; + } + + private List<Object> listToCouchDB(final Schema fieldSchema, final Object fieldValue) { + final List<Object> list = new LinkedList<>(); + for (Object obj : (List<Object>) fieldValue) { + list.add(toDBObject(fieldSchema.getElementType(), obj)); + } + return list; + } + + private Map<String, Object> recordToCouchDB(final Schema fieldSchema, final Object fieldValue) { + final PersistentBase persistent = (PersistentBase) fieldValue; + final Map<String, Object> newMap = new LinkedHashMap<>(); + + if (persistent != null) { + for (Field member : fieldSchema.getFields()) { + Schema memberSchema = member.schema(); + Object memberValue = persistent.get(member.pos()); + newMap.put(member.name(), toDBObject(memberSchema, memberValue)); + } + return newMap; + } + return null; + } + + private String bytesToCouchDB(final Object fieldValue) { + return new String(((ByteBuffer) fieldValue).array(), StandardCharsets.UTF_8); + } + + private Object unionToCouchDB(final Schema fieldSchema, final Object fieldValue) { + Schema.Type type0 = fieldSchema.getTypes().get(0).getType(); + Schema.Type type1 = fieldSchema.getTypes().get(1).getType(); + + // Check if types are different and there's a "null", like ["null","type"] + // or ["type","null"] + if (!type0.equals(type1) + && (type0.equals(Schema.Type.NULL) || type1.equals(Schema.Type.NULL))) { + Schema innerSchema = fieldSchema.getTypes().get(1); + LOG.debug("Transform value to DBObject (UNION), schemaType:{}, type1:{}", + new Object[] { innerSchema.getType(), type1 }); + + // Deserialize as if schema was ["type"] + return toDBObject(innerSchema, fieldValue); + } else { + throw new IllegalStateException( + "CouchDBStore doesn't support 3 types union field yet. Please update your mapping"); + } + } + + private Object toDBObject(final Schema fieldSchema, final Object fieldValue) { + + final Object result; + + switch (fieldSchema.getType()) { + case MAP: + result = mapToCouchDB(fieldValue); + break; + case ARRAY: + result = listToCouchDB(fieldSchema, fieldValue); + break; + case RECORD: + result = recordToCouchDB(fieldSchema, fieldValue); + break; + case BYTES: + result = bytesToCouchDB(fieldValue); + break; + case ENUM: + case STRING: + result = fieldValue.toString(); + break; + case UNION: + result = unionToCouchDB(fieldSchema, fieldValue); + break; + default: + result = fieldValue; + break; + } + return result; + } + + /** + * Deletes the object with the given key + * + * @param key the key of the object + * @return whether the object was successfully deleted + */ + @Override + public boolean delete(K key) { + if (key == null) { + deleteSchema(); + createSchema(); + return true; + } + final String keyString = key.toString(); + final Map<String, Object> referenceData = db.get(Map.class, keyString); + return StringUtils.isNotEmpty(db.delete(keyString, referenceData.get("_rev").toString())); + } + + /** + * Deletes all the objects matching the query. + * See also the note on <a href="#visibility">visibility</a>. + * + * @param query matching records to this query will be deleted + * @return number of deleted records + */ + @Override + public long deleteByQuery(Query<K, T> query) { + + final K key = query.getKey(); + final K startKey = query.getStartKey(); + final K endKey = query.getEndKey(); + + if (key == null && startKey == null && endKey == null) { + deleteSchema(); + createSchema(); + return -1; + } else { + final ViewQuery viewQuery = new ViewQuery() + .allDocs() + .includeDocs(true) + .key(key) + .startKey(startKey) + .endKey(endKey); + + final List<Map> result = db.queryView(viewQuery, Map.class); + final Map<String, List<String>> revisionsToPurge = new HashMap<>(); + + for (Map map : result) { + final List<String> revisions = new ArrayList<>(); + String keyString = map.get("_id").toString(); + String rev = map.get("_rev").toString(); + revisions.add(rev); + revisionsToPurge.put(keyString, revisions); + } + return db.purge(revisionsToPurge).getPurged().size(); + } + } + + /** + * Create a new {@link Query} to query the datastore. + */ + @Override + public Query<K, T> newQuery() { + CouchDBQuery<K, T> query = new CouchDBQuery<>(this); + query.setFields(getFieldsToQuery(null)); + return query; + } + + /** + * Execute the query and return the result. + */ + @Override + public Result<K, T> execute(Query<K, T> query) { + query.setFields(getFieldsToQuery(query.getFields())); + final ViewQuery viewQuery = new ViewQuery() + .allDocs() + .includeDocs(true) + .startKey(query.getStartKey()) + .endKey(query.getEndKey()) + .limit(Ints.checkedCast(query.getLimit())); //FIXME GORA have long value but ektorp client use integer + + CouchDBResult<K, T> couchDBResult = new CouchDBResult<>(this, query, db.queryView(viewQuery, Map.class)); + + return couchDBResult; + } + + @Override + public List<PartitionQuery<K, T>> getPartitions(Query<K, T> query) throws IOException { + + final List<PartitionQuery<K, T>> list = new ArrayList<>(); + final PartitionQueryImpl<K, T> pqi = new PartitionQueryImpl<>(query); + + pqi.setConf(getConf()); + list.add(pqi); + return list; + } + + /** + * Creates a new Persistent instance with the values in 'result' for the fields listed. + * + * @param result result from the query to the database + * @param fields the list of fields to be mapped to the persistence class instance + * @return a persistence class instance which content was deserialized + * @throws IOException + */ + public T newInstance(Map<String, Object> result, String[] fields) throws IOException { + if (result == null) + return null; + + T persistent = newPersistent(); + + // Populate each field + for (String fieldName : fields) { + if (result.get(fieldName) == null) { + continue; + } + final Field field = fieldMap.get(fieldName); + final Schema fieldSchema = field.schema(); + + LOG.debug("Load from DBObject (MAIN), field:{}, schemaType:{}, docField:{}", + new Object[] { field.name(), fieldSchema.getType(), fieldName }); + + final Object resultObj = fromDBObject(fieldSchema, field, fieldName, result); + persistent.put(field.pos(), resultObj); + persistent.setDirty(field.pos()); + } + + persistent.clearDirty(); + return persistent; + + } + + private Object fromCouchDBRecord(final Schema fieldSchema, final String docf, final Object value) { + + final Object innerValue = ((Map) value).get(docf); + if (innerValue == null) { + return null; + } + + Class<?> clazz = null; + try { + clazz = ClassLoadingUtils.loadClass(fieldSchema.getFullName()); + } catch (ClassNotFoundException e) { + LOG.debug(e.getMessage()); + } + + final PersistentBase record = (PersistentBase) new BeanFactoryImpl(keyClass, clazz).newPersistent(); + + for (Field recField : fieldSchema.getFields()) { + Schema innerSchema = recField.schema(); + + record.put(recField.pos(), fromDBObject(innerSchema, recField, recField.name(), innerValue)); + } + return record; + } + + private Object fromCouchDBMap(final Schema fieldSchema, final Field field, final String docf, final Object value) { + + final Map<String, Object> map = (Map<String, Object>) ((Map<String, Object>) value).get(docf); + final Map<Utf8, Object> rmap = new HashMap<>(); + + if (map == null) { + return new DirtyMapWrapper(rmap); + } + + for (Map.Entry<String, Object> e : map.entrySet()) { + Schema innerSchema = fieldSchema.getValueType(); + ; + Object o = fromDBObject(innerSchema, field, e.getKey(), e.getValue()); + rmap.put(new Utf8(e.getKey()), o); + } + return new DirtyMapWrapper<>(rmap); + } + + private Object fromCouchDBUnion(final Schema fieldSchema, final Field field, final String docf, final Object value) { + + Object result;// schema [type0, type1] + Schema.Type type0 = fieldSchema.getTypes().get(0).getType(); + Schema.Type type1 = fieldSchema.getTypes().get(1).getType(); + + // Check if types are different and there's a "null", like ["null","type"] + // or ["type","null"] + if (!type0.equals(type1) + && (type0.equals(Schema.Type.NULL) || type1.equals(Schema.Type.NULL))) { + Schema innerSchema = fieldSchema.getTypes().get(1); + LOG.debug( + "Load from DBObject (UNION), schemaType:{}, docField:{}, storeType:{}", + new Object[] { innerSchema.getType(), docf }); + // Deserialize as if schema was ["type"] + result = fromDBObject(innerSchema, field, docf, value); + } else { + throw new IllegalStateException( + "CouchDBStore doesn't support 3 types union field yet. Please update your mapping"); + } + return result; + } + + private Object fromCouchDBList(final Schema fieldSchema, final Field field, final String docf, final Object value) { + final List<Object> list = (List<Object>) ((Map<String, Object>) value).get(docf); + final List<Object> rlist = new ArrayList<>(); + + if (list == null) { + return new DirtyListWrapper(rlist); + } + + for (Object item : list) { + + Object o = fromDBObject(fieldSchema.getElementType(), field, "item", item); + rlist.add(o); + } + return new DirtyListWrapper<>(rlist); + } + + private Object fromCouchDBEnum(final Schema fieldSchema, final String docf, final Object value) { + final Object result; + if (value instanceof Map) { + result = AvroUtils.getEnumValue(fieldSchema, (String) ((Map) value).get(docf)); + } else { + result = AvroUtils.getEnumValue(fieldSchema, (String) value); + } + return result; + } + + private Object fromCouchDBBytes(final String docf, final Object value) { + final byte[] array; + if (value instanceof Map) { + array = ((String) ((Map) value).get(docf)).getBytes(StandardCharsets.UTF_8); + } else { + array = ((String) value).getBytes(StandardCharsets.UTF_8); + } + return ByteBuffer.wrap(array); + } + + private Object fromCouchDBString(final String docf, final Object value) { + final Object result; + + if (value instanceof Map) { + result = new Utf8((String) ((Map) value).get(docf)); + } else { + result = new Utf8((String) value); + } + + return result; + } + + private Object fromDBObject(final Schema fieldSchema, final Field field, final String docf, final Object value) { + if (value == null) { + return null; + } + + final Object result; + + switch (fieldSchema.getType()) { + case MAP: + result = fromCouchDBMap(fieldSchema, field, docf, value); + break; + case ARRAY: + result = fromCouchDBList(fieldSchema, field, docf, value); + break; + case RECORD: + result = fromCouchDBRecord(fieldSchema, docf, value); + break; + case UNION: + result = fromCouchDBUnion(fieldSchema, field, docf, value); + break; + case ENUM: + result = fromCouchDBEnum(fieldSchema, docf, value); + break; + case BYTES: + result = fromCouchDBBytes(docf, value); + break; + case STRING: + result = fromCouchDBString(docf, value); + break; + case LONG: + case DOUBLE: + case INT: + result = ((Map) value).get(docf); + break; + default: + result = value; + } + return result; + } + + @Override + public void flush() { + db.executeBulk(bulkDocs); + bulkDocs.clear(); + db.flushBulkBuffer(); + } + + @Override + public void close() { + flush(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/main/java/org/apache/gora/couchdb/util/CouchDBObjectMapperFactory.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/main/java/org/apache/gora/couchdb/util/CouchDBObjectMapperFactory.java b/gora-couchdb/src/main/java/org/apache/gora/couchdb/util/CouchDBObjectMapperFactory.java new file mode 100644 index 0000000..1e447d9 --- /dev/null +++ b/gora-couchdb/src/main/java/org/apache/gora/couchdb/util/CouchDBObjectMapperFactory.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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb.util; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.ektorp.CouchDbConnector; +import org.ektorp.impl.ObjectMapperFactory; +import org.ektorp.impl.jackson.EktorpJacksonModule; + +/** + * + */ +public class CouchDBObjectMapperFactory implements ObjectMapperFactory { + + private ObjectMapper instance; + private boolean writeDatesAsTimestamps = false; + + /** + * Create a object mapper instance + * @return + */ + public synchronized ObjectMapper createObjectMapper() { + if (instance == null) { + instance = new ObjectMapper(); + applyDefaultConfiguration(instance); + } + return instance; + } + + /** + * Create a object mapper object via couchdb connector + * + * @param connector + * @return + */ + public synchronized ObjectMapper createObjectMapper(CouchDbConnector connector) { + this.createObjectMapper(); + instance.registerModule(new EktorpJacksonModule(connector, instance)); + return instance; + } + + /** + * Apply default configuration + * + * @param om a object mapper object + */ + private void applyDefaultConfiguration(ObjectMapper om) { + om.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, this.writeDatesAsTimestamps); + om.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.NON_NULL); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/test/conf/gora-couchdb-mapping.xml ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/test/conf/gora-couchdb-mapping.xml b/gora-couchdb/src/test/conf/gora-couchdb-mapping.xml new file mode 100644 index 0000000..e2e7672 --- /dev/null +++ b/gora-couchdb/src/test/conf/gora-couchdb-mapping.xml @@ -0,0 +1,47 @@ +<?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. +--> + +<gora-otd> + + <class name="org.apache.gora.examples.generated.Employee" keyClass="java.lang.String" table="Employee"> + <field name="name"/> + <field name="dateOfBirth"/> + <field name="ssn"/> + <field name="salary"/> + <field name="boss"/> + <field name="webpage"/> + </class> + + <class name="org.apache.gora.examples.generated.WebPage" keyClass="java.lang.String" table="WebPage"> + <field name="url"/> + <field name="content"/> + <field name="parsedContent"/> + <field name="outlinks"/> + <field name="headers"/> + <field name="metadata"/> + <field name="byteData"/> + <field name="stringData"/> + </class> + + + <class name="org.apache.gora.examples.generated.TokenDatum" keyClass="java.lang.String"> + <field name="count"/> + </class> + +</gora-otd> http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/test/conf/gora.properties ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/test/conf/gora.properties b/gora-couchdb/src/test/conf/gora.properties new file mode 100644 index 0000000..4bcf6c7 --- /dev/null +++ b/gora-couchdb/src/test/conf/gora.properties @@ -0,0 +1,19 @@ +# 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. + +gora.datastore.default=org.apache.gora.couchdb.store.CouchDBStore +gora.datastore.couchdb.server=localhost +gora.datastore.couchdb.port=5984 +gora.datastore.mapping.file=gora-couchdb-mapping.xml \ No newline at end of file http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/test/java/org/apache/gora/couchdb/GoraCouchDBTestDriver.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/test/java/org/apache/gora/couchdb/GoraCouchDBTestDriver.java b/gora-couchdb/src/test/java/org/apache/gora/couchdb/GoraCouchDBTestDriver.java new file mode 100644 index 0000000..bb48c83 --- /dev/null +++ b/gora-couchdb/src/test/java/org/apache/gora/couchdb/GoraCouchDBTestDriver.java @@ -0,0 +1,78 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.gora.couchdb; + +import org.apache.gora.GoraTestDriver; +import org.apache.gora.couchdb.store.CouchDBParameters; +import org.apache.gora.couchdb.store.CouchDBStore; +import org.apache.gora.persistency.Persistent; +import org.apache.gora.store.DataStore; +import org.apache.gora.store.DataStoreFactory; +import org.apache.gora.util.GoraException; +import org.testcontainers.containers.GenericContainer; + +import java.util.Properties; + +/** + * Helper class for third party tests using gora-couchdb backend. + * @see GoraTestDriver for test specifics. + * This driver is the base for all test cases that require an CouchDB server. + * In this case we use docker container. A docker container is run before tests + * and it is stopped after tests. + * + */ +public class GoraCouchDBTestDriver extends GoraTestDriver { + + private final GenericContainer couchdbContainer; + private Properties properties = DataStoreFactory.createProps(); + + /** + * Default constructor + */ + public GoraCouchDBTestDriver(GenericContainer couchdbContainer) { + super(CouchDBStore.class); + this.couchdbContainer = couchdbContainer; + } + + @Override + public void setUpClass() throws Exception { + properties.put(CouchDBParameters.PROP_COUCHDB_PORT, couchdbContainer.getMappedPort(5984).toString()); + } + + /** + * Instantiate a new {@link DataStore}. Uses 'null' schema. + * + * @param keyClass The key class. + * @param persistentClass The value class. + * @return A new store instance. + * @throws GoraException + */ + @Override + public <K, T extends Persistent> DataStore<K, T> createDataStore(Class<K> keyClass, Class<T> persistentClass) + throws GoraException { + + final DataStore<K, T> dataStore = DataStoreFactory + .createDataStore((Class<? extends DataStore<K, T>>) dataStoreClass, keyClass, persistentClass, conf, + properties); + dataStores.add(dataStore); + log.info("Datastore for {} was added.", persistentClass); + return dataStore; + } + +} http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/gora-couchdb/src/test/java/org/apache/gora/couchdb/package-info.java ---------------------------------------------------------------------- diff --git a/gora-couchdb/src/test/java/org/apache/gora/couchdb/package-info.java b/gora-couchdb/src/test/java/org/apache/gora/couchdb/package-info.java new file mode 100644 index 0000000..ae4cd6c --- /dev/null +++ b/gora-couchdb/src/test/java/org/apache/gora/couchdb/package-info.java @@ -0,0 +1,21 @@ +/** + * 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. + */ +/** + * Tests for <code>gora-couchdb</code> including + * the test driver for {@link org.apache.gora.couchdb.store.TestCouchDBStore} + */ +package org.apache.gora.couchdb; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/gora/blob/d6c820b2/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 5221fd1..e705894 100644 --- a/pom.xml +++ b/pom.xml @@ -666,6 +666,7 @@ <module>gora-jcache</module> <!-- module>gora-lucene</module --> <module>gora-dynamodb</module> + <module>gora-couchdb</module> <!--module>gora-sql</module --> <module>gora-maven-plugin</module> <module>gora-mongodb</module> @@ -830,6 +831,12 @@ <dependency> <groupId>org.apache.gora</groupId> + <artifactId>gora-couchdb</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.gora</groupId> <artifactId>gora-jcache</artifactId> <version>${project.version}</version> </dependency>
