Author: rhauch
Date: 2008-11-19 14:34:05 -0500 (Wed, 19 Nov 2008)
New Revision: 641
Added:
trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java
trunk/extensions/dna-connector-store-jpa/
trunk/extensions/dna-connector-store-jpa/pom.xml
trunk/extensions/dna-connector-store-jpa/src/
trunk/extensions/dna-connector-store-jpa/src/main/
trunk/extensions/dna-connector-store-jpa/src/main/java/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java
trunk/extensions/dna-connector-store-jpa/src/main/resources/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
trunk/extensions/dna-connector-store-jpa/src/test/
trunk/extensions/dna-connector-store-jpa/src/test/java/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java
trunk/extensions/dna-connector-store-jpa/src/test/resources/
trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java
trunk/pom.xml
Log:
DNA-40 Persistant storage for information not stored in other repository sources
Started work on this new connector, which uses Java Persistence Architecture (JPA) to work
with EJB3 entities stored in a relational database, using Hibernate for the JPA
implementation. (Note that some Hibernate extensions are also used.)
The connector is capable of using one of several models (or schemas). The initial model is
called Basic, but the connector supports having multiple models (which can be exist in
separate packages) and allowing the user to choose via a JavaBean property in the
JpaSource object. (Note that the kind of model used in a particular database instance is
recorded in the database, and may not be changed. In this case, the JpaSource's model
selection is changed to reflect what is in the database. Changing a database's model
would require migration, which is not currently supported, but could be provided by a copy
utility that operates on a graph by generically copying the content from one source to
another.)
Each model is also responsible for creating the RequestProcessor implementation, so the
JpaSource and JpaConnection implementations can work with any model.
The Basic model stores for each node a single record containing the node ID (which is a
UUID string) and a blob for the serialized properties. Large The Basic model stores for
each node a single record containing the node ID (which is a UUID string) and a blob for
the serialized properties. Large The Basic model stores for each node a single record
containing the node ID (which is a UUID string) and a blob for the serialized properties.
Large The Basic model stores for each node a single record containing the node ID (which
is a UUID string) and a blob for the serialized properties. Large The Basic model stores
for each node a single record containing the node ID (which is a UUID string) and a blob
for the serialized properties. Large The Basic model stores for each node a single record
containing the node ID (which is a UUID string) andxistsThe Basic model stores for each
node a single record containing the node ID (which is a UUID string) and a blob for the
serialized prop!
erties. Large Th current implementation is probably close to 75% complete. The Basic
entities are mostly completed (minor changes are anticipated), and all of the source
infrastructure has been completed. Most of the outstanding work is in the
BasicRequestProcessor implementation.
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java
(rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,104 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.common.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author Randall Hauch
+ */
+public class SecureHash {
+
+ /**
+ * Commonly-used hashing algorithms.
+ */
+ public enum Algorithm {
+ MD2("MD2", "The MD2 message digest algorithm as defined in RFC
1319"),
+ MD5("MD5", "The MD5 message digest algorithm as defined in RFC
1321"),
+ SHA_1("SHA-1", "The Secure Hash Algorithm, as defined in Secure
Hash Standard, NIST FIPS 180-1"),
+ SHA_256(
+ "SHA-256",
+ "New hash algorithms for which the draft Federal Information
Processing Standard 180-2, "
+ + "Secure Hash Standard (SHS) is now available. SHA-256 is a
256-bit hash function intended to provide 128 bits of "
+ + "security against collision attacks."),
+ SHA_384(
+ "SHA-384",
+ "New hash algorithms for which the draft Federal Information
Processing Standard 180-2, "
+ + "Secure Hash Standard (SHS) is now available. A 384-bit hash may
be obtained by truncating the SHA-512 output."),
+ SHA_512(
+ "SHA-512",
+ "New hash algorithms for which the draft Federal Information
Processing Standard 180-2, "
+ + "Secure Hash Standard (SHS) is now available. SHA-512 is a
512-bit hash function intended to provide 256 bits of security.");
+ private String name;
+ private String description;
+
+ private Algorithm( String name,
+ String description ) {
+ this.name = name;
+ this.description = description;
+ }
+
+ public String digestName() {
+ return this.name;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ @Override
+ public String toString() {
+ return digestName();
+ }
+ }
+
+ /**
+ * Get the hash of the supplied content, using the supplied digest algorithm.
+ *
+ * @param algorithm the hashing function algorithm that should be used
+ * @param content the content to be hashed; may not be null
+ * @return the hash of the contents as a byte array
+ * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
+ * @throws IllegalArgumentException if the algorithm is null
+ */
+ public static byte[] getHash( Algorithm algorithm,
+ byte[] content ) throws NoSuchAlgorithmException {
+ CheckArg.isNotNull(algorithm, "algorithm");
+ return getHash(algorithm.digestName(), content);
+ }
+
+ /**
+ * Get the hash of the supplied content, using the digest identified by the supplied
name.
+ *
+ * @param digestName the name of the hashing function (or {@link MessageDigest
message digest}) that should be used
+ * @param content the content to be hashed; may not be null
+ * @return the hash of the contents as a byte array
+ * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
+ */
+ public static byte[] getHash( String digestName,
+ byte[] content ) throws NoSuchAlgorithmException {
+ MessageDigest digest = MessageDigest.getInstance(digestName);
+ assert digest != null;
+ return digest.digest(content);
+ }
+}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-11-18 13:00:30
UTC (rev 640)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-11-19 19:34:05
UTC (rev 641)
@@ -494,4 +494,21 @@
return this;
}
+ /**
+ * Create a copy of this location that adds the supplied UUID as an identification
property. The new identification property
+ * will replace any existing identification property with the same name on the
original.
+ *
+ * @param uuid the new UUID, which may be null
+ * @return the new location, or this location if the new identification property is
null or empty
+ */
+ public Location with( UUID uuid ) {
+ if (uuid == null) return this;
+ Property newProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
+ if (this.hasIdProperties()) {
+ Property existing = this.getIdProperty(DnaLexicon.UUID);
+ if (existing != null && existing.equals(newProperty)) return this;
+ }
+ return new Location(this, newProperty);
+ }
+
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java 2008-11-18
13:00:30 UTC (rev 640)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -23,7 +23,6 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -31,6 +30,7 @@
import org.jboss.dna.common.util.Base64;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.common.util.SecureHash;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.properties.Binary;
import org.jboss.dna.graph.properties.ValueComparators;
@@ -44,7 +44,7 @@
public class InMemoryBinary implements Binary {
protected static final Set<String> ALGORITHMS_NOT_FOUND_AND_LOGGED = new
CopyOnWriteArraySet<String>();
- private static final String SHA1DIGEST_NAME = "SHA-1";
+ private static final SecureHash.Algorithm ALGORITHM = SecureHash.Algorithm.SHA_1;
private static final byte[] NO_HASH = new byte[] {};
/**
@@ -75,12 +75,12 @@
*/
public byte[] getHash() {
if (sha1hash == null) {
- // Omnipotent, so doesn't matter if we recompute in concurrent threads
...
+ // Idempotent, so doesn't matter if we recompute in concurrent threads
...
try {
- sha1hash = getHash(SHA1DIGEST_NAME);
+ sha1hash = SecureHash.getHash(ALGORITHM, bytes);
} catch (NoSuchAlgorithmException e) {
- if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(SHA1DIGEST_NAME)) {
- Logger.getLogger(getClass()).error(e,
GraphI18n.messageDigestNotFound, SHA1DIGEST_NAME);
+ if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(ALGORITHM.digestName())) {
+ Logger.getLogger(getClass()).error(e,
GraphI18n.messageDigestNotFound, ALGORITHM.digestName());
}
sha1hash = NO_HASH;
}
@@ -89,19 +89,6 @@
}
/**
- * Get the hash of the contents, using the digest identified by the supplied name.
- *
- * @param digestName the name of the hashing function (or {@link MessageDigest
message digest}) that should be used
- * @return the hash of the contents as a byte array
- * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
- */
- protected byte[] getHash( String digestName ) throws NoSuchAlgorithmException {
- MessageDigest digest = MessageDigest.getInstance(digestName);
- assert digest != null;
- return digest.digest(bytes);
- }
-
- /**
* {@inheritDoc}
*/
public byte[] getBytes() {
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-18
13:00:30 UTC (rev 640)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -55,7 +55,7 @@
* @param at the location of the node to be read
* @param properties the properties of the new node, which should not include the
location's
* {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null
+ * @throws IllegalArgumentException if the location is null or has no {@link
Location#getPath() path}
*/
public CreateNodeRequest( Location at,
Property... properties ) {
@@ -68,7 +68,7 @@
* @param at the location of the node to be read
* @param properties the properties of the new node, which should not include the
location's
* {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null
+ * @throws IllegalArgumentException if the location is null or has no {@link
Location#getPath() path}
*/
public CreateNodeRequest( Location at,
Iterable<Property> properties ) {
@@ -81,7 +81,7 @@
* @param at the location of the node to be read
* @param properties the properties of the new node, which should not include the
location's
* {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null
+ * @throws IllegalArgumentException if the location is null or has no {@link
Location#getPath() path}
*/
public CreateNodeRequest( Location at,
Iterator<Property> properties ) {
@@ -96,13 +96,15 @@
* {@link Location#getIdProperties() identification properties}
* @param conflictBehavior the expected behavior if an equivalently-named child
already exists at the <code>into</code>
* location
- * @throws IllegalArgumentException if the location or the conflict behavior is null
+ * @throws IllegalArgumentException if the location or the conflict behavior is null,
or if the location does not have a
+ * {@link Location#getPath() path}
*/
public CreateNodeRequest( Location at,
NodeConflictBehavior conflictBehavior,
Property... properties ) {
CheckArg.isNotNull(at, "at");
CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
+ CheckArg.isNotNull(at.getPath(), "at.getPath()");
this.at = at;
this.conflictBehavior = conflictBehavior;
int number = properties.length + (at.hasIdProperties() ?
at.getIdProperties().size() : 0);
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java 2008-11-18
13:00:30 UTC (rev 640)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -21,16 +21,15 @@
*/
package org.jboss.dna.graph.properties.basic;
+import static org.hamcrest.core.Is.is;
import static org.jboss.dna.graph.properties.basic.BinaryContains.hasContent;
import static org.jboss.dna.graph.properties.basic.BinaryContains.hasNoContent;
-import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.io.InputStream;
import org.jboss.dna.common.util.IoUtil;
import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.graph.properties.Binary;
-import org.jboss.dna.graph.properties.basic.InMemoryBinary;
import org.junit.Before;
import org.junit.Test;
Property changes on: trunk/extensions/dna-connector-store-jpa
___________________________________________________________________
Name: svn:ignore
+ target
Added: trunk/extensions/dna-connector-store-jpa/pom.xml
===================================================================
--- trunk/extensions/dna-connector-store-jpa/pom.xml (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/pom.xml 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna</artifactId>
+ <version>0.4-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <!-- The groupId and version values are inherited from parent -->
+ <artifactId>dna-connector-store-jpa</artifactId>
+ <packaging>jar</packaging>
+ <name>JBoss DNA Connector to JPA Persistence Store</name>
+ <description>JBoss DNA Connector that persists graph content using
JPA.</description>
+ <
url>http://labs.jboss.org/dna</url>
+ <!--
+ Define the dependencies. Note that all version and scopes default to those defined in
the dependencyManagement section of the
+ parent pom.
+ -->
+ <dependencies>
+ <!--
+ JBoss DNA
+ -->
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ </dependency>
+ <!--
+ JPA via Hibernate Entity Manager
+ -->
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>3.4.0.GA</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-c3p0</artifactId>
+ <version>3.3.1.GA</version>
+ </dependency>
+ <!--
+ HSQLDB
+ -->
+ <dependency>
+ <groupId>hsqldb</groupId>
+ <artifactId>hsqldb</artifactId>
+ <version>1.8.0.2</version>
+ </dependency>
+ <!--
+ Testing (note the scope)
+ -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for
testing)
+ -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <!--
+ Java Concurrency in Practice annotations
+ -->
+ <dependency>
+ <groupId>net.jcip</groupId>
+ <artifactId>jcip-annotations</artifactId>
+ </dependency>
+ </dependencies>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
\ No newline at end of file
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,141 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import javax.persistence.EntityManager;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+import org.jboss.dna.graph.connectors.RepositorySourceListener;
+import org.jboss.dna.graph.requests.Request;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnection implements RepositoryConnection {
+
+ private final String name;
+ private final CachePolicy cachePolicy;
+ private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new
CopyOnWriteArrayList<RepositorySourceListener>();
+ private final EntityManager entityManager;
+ private final Model model;
+ private final UUID rootNodeUuid;
+ private final long largeValueMinimumSizeInBytes;
+
+ /*package*/JpaConnection( String sourceName,
+ CachePolicy cachePolicy,
+ EntityManager entityManager,
+ Model model,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes ) {
+ assert sourceName != null;
+ assert entityManager != null;
+ assert model != null;
+ assert rootNodeUuid != null;
+ this.name = sourceName;
+ this.cachePolicy = cachePolicy; // may be null
+ this.entityManager = entityManager;
+ this.model = model;
+ this.rootNodeUuid = rootNodeUuid;
+ this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connectors.RepositoryConnection#setListener(org.jboss.dna.graph.connectors.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ if (listener != null) {
+ listeners.addIfAbsent(listener);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return cachePolicy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#ping(long,
java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return entityManager.isOpen();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connectors.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.requests.Request)
+ */
+ public void execute( ExecutionContext context,
+ Request request ) throws RepositorySourceException {
+ long size = largeValueMinimumSizeInBytes;
+ RequestProcessor proc = model.createRequestProcessor(name, context,
entityManager, rootNodeUuid, size);
+ try {
+ proc.process(request);
+ } finally {
+ proc.close();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#close()
+ */
+ public void close() {
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ */
+public final class JpaConnectorI18n {
+
+ public static I18n connectorName;
+ public static I18n nodeDoesNotExist;
+ public static I18n propertyIsRequired;
+ public static I18n errorFindingDataSourceInJndi;
+ public static I18n repositorySourceMustHaveName;
+ public static I18n unknownModelName;
+ public static I18n errorSettingContextClassLoader;
+ public static I18n existingStoreSpecifiesUnknownModel;
+ public static I18n unableToReadLargeValue;
+
+ public static I18n basicModelDescription;
+
+ static {
+ try {
+ I18n.initialize(JpaConnectorI18n.class);
+ } catch (final Exception err) {
+ System.err.println(err);
+ }
+ }
+
+ public static Set<Locale> getLocalizationProblemLocales() {
+ return I18n.getLocalizationProblemLocales(JpaConnectorI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems() {
+ return I18n.getLocalizationProblems(JpaConnectorI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems( Locale locale ) {
+ return I18n.getLocalizationProblems(JpaConnectorI18n.class, locale);
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,884 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.ThreadSafe;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.connector.store.jpa.models.basic.BasicModel;
+import org.jboss.dna.connector.store.jpa.util.StoreOptionEntity;
+import org.jboss.dna.connector.store.jpa.util.StoreOptions;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositoryContext;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.connectors.RepositorySourceCapabilities;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaSource implements RepositorySource, ObjectFactory {
+
+ /**
+ * This source is capable of using different database schemas
+ *
+ * @author Randall Hauch
+ */
+ public static class Models {
+ public static final Model BASIC = new BasicModel();
+ private static final Model[] ALL_ARRAY = new Model[] {BASIC};
+ private static final List<Model> MODIFIABLE_MODELS = new
ArrayList<Model>(Arrays.asList(ALL_ARRAY));
+ public static final Collection<Model> ALL =
Collections.unmodifiableCollection(MODIFIABLE_MODELS);
+ public static final Model DEFAULT = BASIC;
+
+ public static boolean addModel( Model model ) {
+ CheckArg.isNotNull(model, "modelName");
+ for (Model existing : MODIFIABLE_MODELS) {
+ if (existing.getName().equals(model.getName())) return false;
+ }
+ return MODIFIABLE_MODELS.add(model);
+ }
+
+ public static Model getModel( String name ) {
+ CheckArg.isNotEmpty(name, "name");
+ name = name.trim();
+ for (Model existing : ALL) {
+ if (existing.getName().equals(name)) return existing;
+ }
+ return null;
+ }
+ }
+
+ protected static final String SOURCE_NAME = "sourceName";
+ protected static final String ROOT_NODE_UUID = "rootNodeUuid";
+ protected static final String DATA_SOURCE_JNDI_NAME =
"dataSourceJndiName";
+ protected static final String DIALECT = "dialect";
+ protected static final String USERNAME = "username";
+ protected static final String PASSWORD = "password";
+ protected static final String URL = "url";
+ protected static final String DRIVER_CLASS_NAME = "driverClassName";
+ protected static final String DRIVER_CLASSLOADER_NAME =
"driverClassloaderName";
+ protected static final String MAXIMUM_CONNECTIONS_IN_POOL =
"maximumConnectionsInPool";
+ protected static final String MINIMUM_CONNECTIONS_IN_POOL =
"minimumConnectionsInPool";
+ protected static final String MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS =
"maximumConnectionIdleTimeInSeconds";
+ protected static final String MAXIMUM_SIZE_OF_STATEMENT_CACHE =
"maximumSizeOfStatementCache";
+ protected static final String NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED =
"numberOfConnectionsToBeAcquiredAsNeeded";
+ protected static final String IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS =
"idleTimeInSecondsBeforeTestingConnections";
+ protected static final String CACHE_TIME_TO_LIVE_IN_MILLISECONDS =
"cacheTimeToLiveInMilliseconds";
+ protected static final String RETRY_LIMIT = "retryLimit";
+ protected static final String MODEL_NAME = "modelName";
+ protected static final String LARGE_VALUE_SIZE_IN_BYTES =
"largeValueSizeInBytes";
+
+ /**
+ * This source supports events.
+ */
+ protected static final boolean SUPPORTS_EVENTS = true;
+ /**
+ * This source supports same-name-siblings.
+ */
+ protected static final boolean SUPPORTS_SAME_NAME_SIBLINGS = true;
+ /**
+ * This source supports udpates by default, but each instance may be configured to
{@link #setSupportsUpdates(boolean) be
+ * read-only or updateable}.
+ */
+ public static final boolean DEFAULT_SUPPORTS_UPDATES = true;
+
+ /**
+ * The default UUID that is used for root nodes in a store.
+ */
+ public static final String DEFAULT_ROOT_NODE_UUID =
"1497b6fe-8c7e-4bbb-aaa2-24f3d4942668";
+
+ private static final int DEFAULT_RETRY_LIMIT = 0;
+ private static final int DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS = 60 * 5; // 5
minutes
+ private static final int DEFAULT_MAXIMUM_FETCH_DEPTH = 3;
+ private static final int DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL = 5;
+ private static final int DEFAULT_MINIMUM_CONNECTIONS_IN_POOL = 0;
+ private static final int DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS = 60 * 10;
// 10 minutes
+ private static final int DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE = 100;
+ private static final int DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED = 1;
+ private static final int DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS = 60
* 3; // 3 minutes
+ private static final int DEFAULT_LARGE_VALUE_SIZE_IN_BYTES = 2 ^ 10; // 1 kilobyte
+
+ /**
+ * The first serialized version of this source.
+ */
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private String dataSourceJndiName;
+ private String dialect;
+ private String username;
+ private String password;
+ private String url;
+ private String driverClassName;
+ private String driverClassloaderName;
+ private String rootNodeUuid = DEFAULT_ROOT_NODE_UUID;
+ private int maximumConnectionsInPool = DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL;
+ private int minimumConnectionsInPool = DEFAULT_MINIMUM_CONNECTIONS_IN_POOL;
+ private int maximumConnectionIdleTimeInSeconds =
DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS;
+ private int maximumSizeOfStatementCache =
DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE;
+ private int numberOfConnectionsToAcquireAsNeeded =
DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED;
+ private int idleTimeInSecondsBeforeTestingConnections =
DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS;
+ private int retryLimit = DEFAULT_RETRY_LIMIT;
+ private int cacheTimeToLiveInMilliseconds = DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS *
1000;
+ private long largeValueSizeInBytes = DEFAULT_LARGE_VALUE_SIZE_IN_BYTES;
+ private final Capabilities capabilities = new Capabilities();
+ private transient Model model;
+ private String modelName;
+ private transient DataSource dataSource;
+ private transient EntityManagerFactory entityManagerFactory;
+ private transient CachePolicy cachePolicy;
+ private transient RepositoryContext repositoryContext;
+ private transient UUID rootUuid = UUID.fromString(rootNodeUuid);
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public void setName( String name ) {
+ if (name != null) {
+ name = name.trim();
+ if (name.length() == 0) name = null;
+ }
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getCapabilities()
+ */
+ public RepositorySourceCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ /**
+ * Get whether this source supports updates.
+ *
+ * @return true if this source supports updates, or false if this source only
supports reading content.
+ */
+ public boolean getSupportsUpdates() {
+ return capabilities.supportsUpdates();
+ }
+
+ /**
+ * Set whether this source supports updates.
+ *
+ * @param supportsUpdates true if this source supports updating content, or false if
this source only supports reading
+ * content.
+ */
+ public synchronized void setSupportsUpdates( boolean supportsUpdates ) {
+ capabilities.setSupportsUpdates(supportsUpdates);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getRetryLimit()
+ */
+ public int getRetryLimit() {
+ return retryLimit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#setRetryLimit(int)
+ */
+ public synchronized void setRetryLimit( int limit ) {
+ if (limit < 0) limit = 0;
+ this.retryLimit = limit;
+ }
+
+ /**
+ * Get the time in milliseconds that content returned from this source may used while
in the cache.
+ *
+ * @return the time to live, in milliseconds, or 0 if the time to live is not
specified by this source
+ */
+ public int getCacheTimeToLiveInMilliseconds() {
+ return cacheTimeToLiveInMilliseconds;
+ }
+
+ /**
+ * Set the time in milliseconds that content returned from this source may used while
in the cache.
+ *
+ * @param cacheTimeToLive the time to live, in milliseconds; 0 if the time to live is
not specified by this source; or a
+ * negative number for the default value
+ */
+ public synchronized void setCacheTimeToLiveInMilliseconds( int cacheTimeToLive ) {
+ if (cacheTimeToLive < 0) cacheTimeToLive =
DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS;
+ this.cacheTimeToLiveInMilliseconds = cacheTimeToLive;
+ this.cachePolicy = cacheTimeToLiveInMilliseconds > 0 ? new
JpaCachePolicy(cacheTimeToLiveInMilliseconds) : null;
+ }
+
+ /**
+ * @return rootNodeUuid
+ */
+ public String getRootNodeUuid() {
+ return rootNodeUuid;
+ }
+
+ /**
+ * @param rootNodeUuid Sets rootNodeUuid to the specified value.
+ * @throws IllegalArgumentException if the string value cannot be converted to UUID
+ */
+ public void setRootNodeUuid( String rootNodeUuid ) {
+ if (rootNodeUuid != null && rootNodeUuid.trim().length() == 0)
rootNodeUuid = DEFAULT_ROOT_NODE_UUID;
+ this.rootUuid = UUID.fromString(rootNodeUuid);
+ this.rootNodeUuid = rootNodeUuid;
+ }
+
+ /**
+ * @return dialect
+ */
+ public String getDialect() {
+ return dialect;
+ }
+
+ /**
+ * @param dialect Sets dialect to the specified value.
+ */
+ public synchronized void setDialect( String dialect ) {
+ if (dialect != null && dialect.trim().length() == 0) dialect = null;
+ this.dialect = dialect;
+ }
+
+ /**
+ * @return dataSourceJndiName
+ */
+ public String getDataSourceJndiName() {
+ return dataSourceJndiName;
+ }
+
+ /**
+ * @param dataSourceJndiName Sets dataSourceJndiName to the specified value.
+ */
+ public void setDataSourceJndiName( String dataSourceJndiName ) {
+ if (dataSourceJndiName != null && dataSourceJndiName.trim().length() ==
0) dataSourceJndiName = null;
+ this.dataSourceJndiName = dataSourceJndiName;
+ }
+
+ /**
+ * @return driverClassName
+ */
+ public String getDriverClassName() {
+ return driverClassName;
+ }
+
+ /**
+ * @param driverClassName Sets driverClassName to the specified value.
+ */
+ public synchronized void setDriverClassName( String driverClassName ) {
+ if (driverClassName != null && driverClassName.trim().length() == 0)
driverClassName = null;
+ this.driverClassName = driverClassName;
+ }
+
+ /**
+ * @return driverClassloaderName
+ */
+ public String getDriverClassloaderName() {
+ return driverClassloaderName;
+ }
+
+ /**
+ * @param driverClassloaderName Sets driverClassloaderName to the specified value.
+ */
+ public void setDriverClassloaderName( String driverClassloaderName ) {
+ if (driverClassloaderName != null &&
driverClassloaderName.trim().length() == 0) driverClassloaderName = null;
+ this.driverClassloaderName = driverClassloaderName;
+ }
+
+ /**
+ * @return username
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * @param username Sets username to the specified value.
+ */
+ public synchronized void setUsername( String username ) {
+ this.username = username;
+ }
+
+ /**
+ * @return password
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * @param password Sets password to the specified value.
+ */
+ public synchronized void setPassword( String password ) {
+ this.password = password;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url Sets url to the specified value.
+ */
+ public synchronized void setUrl( String url ) {
+ if (url != null && url.trim().length() == 0) url = null;
+ this.url = url;
+ }
+
+ /**
+ * @return maximumConnectionsInPool
+ */
+ public int getMaximumConnectionsInPool() {
+ return maximumConnectionsInPool;
+ }
+
+ /**
+ * @param maximumConnectionsInPool Sets maximumConnectionsInPool to the specified
value.
+ */
+ public synchronized void setMaximumConnectionsInPool( int maximumConnectionsInPool )
{
+ if (maximumConnectionsInPool < 0) maximumConnectionsInPool =
DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL;
+ this.maximumConnectionsInPool = maximumConnectionsInPool;
+ }
+
+ /**
+ * @return minimumConnectionsInPool
+ */
+ public int getMinimumConnectionsInPool() {
+ return minimumConnectionsInPool;
+ }
+
+ /**
+ * @param minimumConnectionsInPool Sets minimumConnectionsInPool to the specified
value.
+ */
+ public synchronized void setMinimumConnectionsInPool( int minimumConnectionsInPool )
{
+ if (minimumConnectionsInPool < 0) minimumConnectionsInPool =
DEFAULT_MINIMUM_CONNECTIONS_IN_POOL;
+ this.minimumConnectionsInPool = minimumConnectionsInPool;
+ }
+
+ /**
+ * @return maximumConnectionIdleTimeInSeconds
+ */
+ public int getMaximumConnectionIdleTimeInSeconds() {
+ return maximumConnectionIdleTimeInSeconds;
+ }
+
+ /**
+ * @param maximumConnectionIdleTimeInSeconds Sets maximumConnectionIdleTimeInSeconds
to the specified value.
+ */
+ public synchronized void setMaximumConnectionIdleTimeInSeconds( int
maximumConnectionIdleTimeInSeconds ) {
+ if (maximumConnectionIdleTimeInSeconds < 0) maximumConnectionIdleTimeInSeconds
= DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS;
+ this.maximumConnectionIdleTimeInSeconds = maximumConnectionIdleTimeInSeconds;
+ }
+
+ /**
+ * @return maximumSizeOfStatementCache
+ */
+ public int getMaximumSizeOfStatementCache() {
+ return maximumSizeOfStatementCache;
+ }
+
+ /**
+ * @param maximumSizeOfStatementCache Sets maximumSizeOfStatementCache to the
specified value.
+ */
+ public synchronized void setMaximumSizeOfStatementCache( int
maximumSizeOfStatementCache ) {
+ if (maximumSizeOfStatementCache < 0) maximumSizeOfStatementCache =
DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE;
+ this.maximumSizeOfStatementCache = maximumSizeOfStatementCache;
+ }
+
+ /**
+ * @return numberOfConnectionsToAcquireAsNeeded
+ */
+ public int getNumberOfConnectionsToAcquireAsNeeded() {
+ return numberOfConnectionsToAcquireAsNeeded;
+ }
+
+ /**
+ * @param numberOfConnectionsToAcquireAsNeeded Sets
numberOfConnectionsToAcquireAsNeeded to the specified value.
+ */
+ public synchronized void setNumberOfConnectionsToAcquireAsNeeded( int
numberOfConnectionsToAcquireAsNeeded ) {
+ if (numberOfConnectionsToAcquireAsNeeded < 0)
numberOfConnectionsToAcquireAsNeeded =
DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED;
+ this.numberOfConnectionsToAcquireAsNeeded =
numberOfConnectionsToAcquireAsNeeded;
+ }
+
+ /**
+ * @return idleTimeInSecondsBeforeTestingConnections
+ */
+ public int getIdleTimeInSecondsBeforeTestingConnections() {
+ return idleTimeInSecondsBeforeTestingConnections;
+ }
+
+ /**
+ * @param idleTimeInSecondsBeforeTestingConnections Sets
idleTimeInSecondsBeforeTestingConnections to the specified value.
+ */
+ public synchronized void setIdleTimeInSecondsBeforeTestingConnections( int
idleTimeInSecondsBeforeTestingConnections ) {
+ if (idleTimeInSecondsBeforeTestingConnections < 0)
idleTimeInSecondsBeforeTestingConnections =
DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS;
+ this.idleTimeInSecondsBeforeTestingConnections =
idleTimeInSecondsBeforeTestingConnections;
+ }
+
+ /**
+ * Get the {@link DataSource} object that this source is to use.
+ *
+ * @return the data source; may be null if no data source has been set or found in
JNDI
+ * @see #setDataSource(DataSource)
+ * @see #setDataSourceJndiName(String)
+ */
+ /*package*/DataSource getDataSource() {
+ return dataSource;
+ }
+
+ /**
+ * Set the {@link DataSource} instance that this source should use.
+ *
+ * @param dataSource the data source; may be null
+ * @see #getDataSource()
+ * @see #setDataSourceJndiName(String)
+ */
+ /*package*/synchronized void setDataSource( DataSource dataSource ) {
+ this.dataSource = dataSource;
+ }
+
+ /**
+ * Get the model that will be used. This may be null if not yet connected, but after
connections will reflect the type of
+ * model that is being used in the store.
+ *
+ * @return the name of the model
+ */
+ public String getModel() {
+ return modelName;
+ }
+
+ /**
+ * Set the model that should be used for this store. If the store already has a
model, specifying a different value has no
+ * effect, since the store's model will not be changed. After connection, this
value will reflect the actual store value.
+ *
+ * @param modelName the name of the model that should be used for new stores, or null
if the default should be used
+ */
+ public synchronized void setModel( String modelName ) {
+ if (modelName != null) {
+ modelName = modelName.trim();
+ if (modelName.length() == 0) modelName = null;
+ }
+ if (modelName == null) {
+ model = null;
+ return;
+ }
+ Model model = Models.getModel(modelName);
+ if (model == null) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (Model existing : Models.ALL) {
+ if (!first) {
+ first = false;
+ sb.append(", ");
+ }
+
sb.append('"').append(existing.getName()).append('"');
+ }
+ String modelNames = sb.toString();
+ throw new
IllegalArgumentException(JpaConnectorI18n.unknownModelName.text(model, modelNames));
+ }
+ this.model = model;
+ this.modelName = modelName;
+ }
+
+ /**
+ * @return largeValueSizeInBytes
+ */
+ public long getLargeValueSizeInBytes() {
+ return largeValueSizeInBytes;
+ }
+
+ /**
+ * @param largeValueSizeInBytes Sets largeValueSizeInBytes to the specified value.
+ */
+ public void setLargeValueSizeInBytes( long largeValueSizeInBytes ) {
+ if (largeValueSizeInBytes < 0) largeValueSizeInBytes =
DEFAULT_LARGE_VALUE_SIZE_IN_BYTES;
+ this.largeValueSizeInBytes = largeValueSizeInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connectors.RepositorySource#initialize(org.jboss.dna.graph.connectors.RepositoryContext)
+ */
+ public void initialize( RepositoryContext context ) throws RepositorySourceException
{
+ this.repositoryContext = context;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.Referenceable#getReference()
+ */
+ public Reference getReference() {
+ String className = getClass().getName();
+ String factoryClassName = this.getClass().getName();
+ Reference ref = new Reference(className, factoryClassName, null);
+
+ if (getName() != null) {
+ ref.add(new StringRefAddr(SOURCE_NAME, getName()));
+ }
+ if (getRootNodeUuid() != null) {
+ ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid()));
+ }
+ if (getDataSourceJndiName() != null) {
+ ref.add(new StringRefAddr(DATA_SOURCE_JNDI_NAME, getDataSourceJndiName()));
+ }
+ if (getDialect() != null) {
+ ref.add(new StringRefAddr(DIALECT, getDialect()));
+ }
+ if (getUsername() != null) {
+ ref.add(new StringRefAddr(USERNAME, getUsername()));
+ }
+ if (getPassword() != null) {
+ ref.add(new StringRefAddr(PASSWORD, getPassword()));
+ }
+ if (getUrl() != null) {
+ ref.add(new StringRefAddr(URL, getUrl()));
+ }
+ if (getDriverClassName() != null) {
+ ref.add(new StringRefAddr(DRIVER_CLASS_NAME, getDriverClassName()));
+ }
+ if (getDriverClassloaderName() != null) {
+ ref.add(new StringRefAddr(DRIVER_CLASSLOADER_NAME,
getDriverClassloaderName()));
+ }
+ ref.add(new StringRefAddr(MAXIMUM_CONNECTIONS_IN_POOL,
Integer.toString(getMaximumConnectionsInPool())));
+ ref.add(new StringRefAddr(MINIMUM_CONNECTIONS_IN_POOL,
Integer.toString(getMinimumConnectionsInPool())));
+ ref.add(new StringRefAddr(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS,
+
Integer.toString(getMaximumConnectionIdleTimeInSeconds())));
+ ref.add(new StringRefAddr(MAXIMUM_SIZE_OF_STATEMENT_CACHE,
Integer.toString(getMaximumSizeOfStatementCache())));
+ ref.add(new StringRefAddr(NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED,
+
Integer.toString(getNumberOfConnectionsToAcquireAsNeeded())));
+ ref.add(new StringRefAddr(IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS,
+
Integer.toString(getIdleTimeInSecondsBeforeTestingConnections())));
+ ref.add(new StringRefAddr(CACHE_TIME_TO_LIVE_IN_MILLISECONDS,
Integer.toString(getCacheTimeToLiveInMilliseconds())));
+ ref.add(new StringRefAddr(LARGE_VALUE_SIZE_IN_BYTES,
Long.toString(getLargeValueSizeInBytes())));
+ if (getModel() != null) {
+ ref.add(new StringRefAddr(MODEL_NAME, getModel()));
+ }
+ ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
+ return ref;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getObjectInstance( Object obj,
+ javax.naming.Name name,
+ Context nameCtx,
+ Hashtable<?, ?> environment ) throws Exception
{
+ if (obj instanceof Reference) {
+ Map<String, String> values = new HashMap<String, String>();
+ Reference ref = (Reference)obj;
+ Enumeration<?> en = ref.getAll();
+ while (en.hasMoreElements()) {
+ RefAddr subref = (RefAddr)en.nextElement();
+ if (subref instanceof StringRefAddr) {
+ String key = subref.getType();
+ Object value = subref.getContent();
+ if (value != null) values.put(key, value.toString());
+ }
+ }
+ String sourceName = values.get(SOURCE_NAME);
+ String rootNodeUuid = values.get(ROOT_NODE_UUID);
+ String dataSourceJndiName = values.get(DATA_SOURCE_JNDI_NAME);
+ String dialect = values.get(DIALECT);
+ String username = values.get(USERNAME);
+ String password = values.get(PASSWORD);
+ String url = values.get(URL);
+ String driverClassName = values.get(DRIVER_CLASS_NAME);
+ String driverClassloaderName = values.get(DRIVER_CLASSLOADER_NAME);
+ String maxConnectionsInPool = values.get(MAXIMUM_CONNECTIONS_IN_POOL);
+ String minConnectionsInPool = values.get(MINIMUM_CONNECTIONS_IN_POOL);
+ String maxConnectionIdleTimeInSec =
values.get(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS);
+ String maxSizeOfStatementCache =
values.get(MAXIMUM_SIZE_OF_STATEMENT_CACHE);
+ String acquisitionIncrement =
values.get(NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED);
+ String idleTimeInSeconds =
values.get(IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS);
+ String cacheTtlInMillis = values.get(CACHE_TIME_TO_LIVE_IN_MILLISECONDS);
+ String modelName = values.get(MODEL_NAME);
+ String retryLimit = values.get(RETRY_LIMIT);
+ String largeModelSize = values.get(LARGE_VALUE_SIZE_IN_BYTES);
+
+ // Create the source instance ...
+ JpaSource source = new JpaSource();
+ if (sourceName != null) source.setName(sourceName);
+ if (rootNodeUuid != null) source.setRootNodeUuid(rootNodeUuid);
+ if (dataSourceJndiName != null)
source.setDataSourceJndiName(dataSourceJndiName);
+ if (dialect != null) source.setDialect(dialect);
+ if (username != null) source.setUsername(username);
+ if (password != null) source.setPassword(password);
+ if (url != null) source.setUrl(url);
+ if (driverClassName != null) source.setDriverClassName(driverClassName);
+ if (driverClassloaderName != null)
source.setDriverClassloaderName(driverClassloaderName);
+ if (maxConnectionsInPool != null)
source.setMaximumConnectionsInPool(Integer.parseInt(maxConnectionsInPool));
+ if (minConnectionsInPool != null)
source.setMinimumConnectionsInPool(Integer.parseInt(minConnectionsInPool));
+ if (maxConnectionIdleTimeInSec != null)
source.setMaximumConnectionIdleTimeInSeconds(Integer.parseInt(maxConnectionIdleTimeInSec));
+ if (maxSizeOfStatementCache != null)
source.setMaximumSizeOfStatementCache(Integer.parseInt(maxSizeOfStatementCache));
+ if (acquisitionIncrement != null)
source.setNumberOfConnectionsToAcquireAsNeeded(Integer.parseInt(acquisitionIncrement));
+ if (idleTimeInSeconds != null)
source.setIdleTimeInSecondsBeforeTestingConnections(Integer.parseInt(idleTimeInSeconds));
+ if (cacheTtlInMillis != null)
source.setCacheTimeToLiveInMilliseconds(Integer.parseInt(cacheTtlInMillis));
+ if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
+ if (modelName != null) source.setModel(modelName);
+ if (largeModelSize != null)
source.setLargeValueSizeInBytes(Long.parseLong(largeModelSize));
+ return source;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getConnection()
+ */
+ public synchronized RepositoryConnection getConnection() throws
RepositorySourceException {
+ if (this.name == null || this.name.trim().length() == 0) {
+ throw new
RepositorySourceException(JpaConnectorI18n.repositorySourceMustHaveName.text());
+ }
+ assert rootNodeUuid != null;
+ assert rootUuid != null;
+ EntityManager entityManager = null;
+ if (entityManagerFactory == null || !entityManagerFactory.isOpen()) {
+ // Create the JPA EntityManagerFactory by programmatically configuring
Hibernate Entity Manager ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+
+ // Configure the entity classes ...
+ configurator.addAnnotatedClass(StoreOptionEntity.class);
+ if (model != null) model.configure(configurator);
+
+ // Configure additional properties, which may be overridden by subclasses
...
+ configure(configurator);
+
+ // Now set the mandatory information, overwriting anything that the
subclasses may have tried ...
+ if (this.dataSource == null && this.dataSourceJndiName != null) {
+ // Try to load the DataSource from JNDI ...
+ try {
+ Context context = new InitialContext();
+ dataSource = (DataSource)context.lookup(this.dataSourceJndiName);
+ } catch (Throwable t) {
+ Logger.getLogger(getClass()).error(t,
JpaConnectorI18n.errorFindingDataSourceInJndi, name, dataSourceJndiName);
+ }
+ }
+
+ if (this.dataSource != null) {
+ // Set the data source ...
+ configurator.setDataSource(this.dataSource);
+ } else {
+ // Set the context class loader, so that the driver could be found ...
+ if (this.repositoryContext != null && this.driverClassloaderName
!= null) {
+ try {
+ ExecutionContext context =
this.repositoryContext.getExecutionContextFactory().create();
+ ClassLoader loader =
context.getClassLoader(this.driverClassloaderName);
+ if (loader != null) {
+ Thread.currentThread().setContextClassLoader(loader);
+ }
+ } catch (Throwable t) {
+ I18n msg = JpaConnectorI18n.errorSettingContextClassLoader;
+ Logger.getLogger(getClass()).error(t, msg, name,
driverClassloaderName);
+ }
+ }
+ // Set the connection properties ...
+ setProperty(configurator, "hibernate.dialect", this.dialect);
+ setProperty(configurator, "hibernate.connection.driver_class",
this.driverClassName);
+ setProperty(configurator, "hibernate.connection.username",
this.username);
+ setProperty(configurator, "hibernate.connection.password",
this.password);
+ setProperty(configurator, "hibernate.connection.url",
this.url);
+ setProperty(configurator,
"hibernate.connection.max_fetch_depth", DEFAULT_MAXIMUM_FETCH_DEPTH);
+ setProperty(configurator, "hibernate.connection.pool_size", 0);
// don't use the built-in pool
+ }
+
+ entityManagerFactory = configurator.buildEntityManagerFactory();
+
+ // Establish a connection and obtain the store options...
+ entityManager = entityManagerFactory.createEntityManager();
+
+ // Find and update/set the root node's UUID ...
+ StoreOptions options = new StoreOptions(entityManager);
+ UUID actualUuid = options.getRootNodeUuid();
+ if (actualUuid != null) this.setRootNodeUuid(actualUuid.toString());
+ else options.setRootNodeUuid(this.rootUuid);
+
+ // Find or set the type of model that will be used.
+ String actualModelName = options.getModelName();
+ if (actualModelName == null) {
+ // This is a new store, so set to the specified model ...
+ if (model == null) setModel(Models.DEFAULT.getName());
+ assert model != null;
+ options.setModelName(model);
+ } else {
+ try {
+ setModel(actualModelName);
+ } catch (Throwable e) {
+ // The actual model name doesn't match what's available in
the software ...
+ entityManagerFactory.close();
+ String msg =
JpaConnectorI18n.existingStoreSpecifiesUnknownModel.text(name, actualModelName);
+ throw new RepositorySourceException(msg);
+ }
+ }
+ entityManagerFactory.close();
+
+ // Now, create another entity manager with the classes from the correct
model
+ model.configure(configurator);
+ entityManagerFactory = configurator.buildEntityManagerFactory();
+ entityManager = entityManagerFactory.createEntityManager();
+ }
+ if (entityManager == null) {
+ entityManager = entityManagerFactory.createEntityManager();
+ }
+ return new JpaConnection(getName(), cachePolicy, entityManager, model, rootUuid,
largeValueSizeInBytes);
+ }
+
+ /**
+ * Set up the JPA configuration using Hibernate, except for the entity classes (which
will already be configured when this
+ * method is called) and the data source or connection information (which will be set
after this method returns). Subclasses
+ * may override this method to customize the configuration.
+ * <p>
+ * This method sets up the C3P0 connection pooling, the cache provider, and some DDL
options.
+ * </p>
+ *
+ * @param configuration the Hibernate configuration; never null
+ */
+ protected void configure( Ejb3Configuration configuration ) {
+ // Set the connection pooling properties (to use C3P0) ...
+ setProperty(configuration, "hibernate.connection.provider_class",
"org.hibernate.connection.C3P0ConnectionProvider");
+ setProperty(configuration, "hibernate.c3p0.max_size",
this.maximumConnectionsInPool);
+ setProperty(configuration, "hibernate.c3p0.min_size",
this.minimumConnectionsInPool);
+ setProperty(configuration, "hibernate.c3p0.timeout",
this.idleTimeInSecondsBeforeTestingConnections);
+ setProperty(configuration, "hibernate.c3p0.max_statements",
this.maximumSizeOfStatementCache);
+ setProperty(configuration, "hibernate.c3p0.idle_test_period",
this.idleTimeInSecondsBeforeTestingConnections);
+ setProperty(configuration, "hibernate.c3p0.acquire_increment",
this.numberOfConnectionsToAcquireAsNeeded);
+ setProperty(configuration, "hibernate.c3p0.validate",
"false");
+
+ // Disable the second-level cache ...
+ setProperty(configuration, "hibernate.cache.provider_class",
"org.hibernate.cache.NoCacheProvider");
+
+ // Set up the schema and DDL options ...
+ // setProperty(configuration, "hibernate.show_sql", "true");
// writes all SQL statements to console
+ setProperty(configuration, "hibernate.format_sql", "true");
+ setProperty(configuration, "hibernate.use_sql_comments",
"true");
+ setProperty(configuration, "hibernate.hbm2ddl.auto",
"create");
+ }
+
+ /**
+ * Close any resources held by this source. This will ensure that all connections are
closed.
+ */
+ public synchronized void close() {
+ if (entityManagerFactory != null) {
+ try {
+ entityManagerFactory.close();
+ } finally {
+ entityManagerFactory = null;
+ }
+ }
+ }
+
+ protected void setProperty( Ejb3Configuration configurator,
+ String propertyName,
+ String propertyValue ) {
+ assert configurator != null;
+ assert propertyName != null;
+ assert propertyName.trim().length() != 0;
+ if (propertyValue != null) {
+ configurator.setProperty(propertyName, propertyValue.trim());
+ }
+ }
+
+ protected void setProperty( Ejb3Configuration configurator,
+ String propertyName,
+ int propertyValue ) {
+ assert configurator != null;
+ assert propertyName != null;
+ assert propertyName.trim().length() != 0;
+ configurator.setProperty(propertyName, Integer.toString(propertyValue));
+ }
+
+ @ThreadSafe
+ protected class Capabilities extends RepositorySourceCapabilities {
+ private final AtomicBoolean supportsUpdates = new
AtomicBoolean(DEFAULT_SUPPORTS_UPDATES);
+
+ /*package*/Capabilities() {
+ super(SUPPORTS_SAME_NAME_SIBLINGS, DEFAULT_SUPPORTS_UPDATES,
SUPPORTS_EVENTS);
+ }
+
+ /*package*/void setSupportsUpdates( boolean supportsUpdates ) {
+ this.supportsUpdates.set(supportsUpdates);
+ }
+
+ @Override
+ public boolean supportsUpdates() {
+ return this.supportsUpdates.get();
+ }
+ }
+
+ @Immutable
+ /*package*/class JpaCachePolicy implements CachePolicy {
+ private static final long serialVersionUID = 1L;
+ private final int ttl;
+
+ /*package*/JpaCachePolicy( int ttl ) {
+ this.ttl = ttl;
+ }
+
+ public long getTimeToLive() {
+ return ttl;
+ }
+
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.Locale;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * A descriptor of a schema used by this connector.
+ *
+ * @see JpaSource.Models
+ * @see JpaSource.Models#addModel(Model)
+ * @see JpaSource#setModel(String)
+ * @see JpaSource#getModel()
+ * @author Randall Hauch
+ */
+public abstract class Model {
+ private final String name;
+ private final I18n description;
+
+ protected Model( String name,
+ I18n description ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(description, "description");
+ this.name = name;
+ this.description = description;
+ }
+
+ public final String getName() {
+ return this.name;
+ }
+
+ /**
+ * Get the description of this model in the default locale.
+ *
+ * @return the description of this model; never null
+ */
+ public String getDescription() {
+ return description.text();
+ }
+
+ /**
+ * Get the description of this model in the supplied locale.
+ *
+ * @param locale the locale in which the description is to be returned
+ * @return the description of this model; never null
+ */
+ public String getDescription( Locale locale ) {
+ return description.text(locale);
+ }
+
+ public abstract RequestProcessor createRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager
entityManager,
+ UUID rootNodeUuid,
+ long
largeValueMinimumSizeInBytes );
+
+ /**
+ * Configure the entity class that will be used by JPA to store information in the
database.
+ *
+ * @param configurator the Hibernate {@link Ejb3Configuration} component; never null
+ */
+ public abstract void configure( Ejb3Configuration configurator );
+
+ @Override
+ public final int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public final boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Model) {
+ Model that = (Model)obj;
+ if (this.getName().equals(that.getName())) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public final String toString() {
+ return name;
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.Model;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * Database model that stores node properties as opaque records and children as
transparent records. Large property values are
+ * stored separately.
+ *
+ * @author Randall Hauch
+ */
+public class BasicModel extends Model {
+
+ public BasicModel() {
+ super("Basic", JpaConnectorI18n.basicModelDescription);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.store.jpa.Model#createRequestProcessor(java.lang.String,
org.jboss.dna.graph.ExecutionContext,
+ * javax.persistence.EntityManager, java.util.UUID, long)
+ */
+ @Override
+ public RequestProcessor createRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes )
{
+ return new BasicRequestProcessor(sourceName, context, entityManager,
rootNodeUuid, largeValueMinimumSizeInBytes);
+ }
+
+ /**
+ * Configure the entity class that will be used by JPA to store information in the
database.
+ *
+ * @param configurator the Hibernate {@link Ejb3Configuration} component; never null
+ */
+ @Override
+ public void configure( Ejb3Configuration configurator ) {
+ // Add the annotated classes ...
+ configurator.addAnnotatedClass(NamespaceEntity.class);
+ configurator.addAnnotatedClass(NodeId.class);
+ configurator.addAnnotatedClass(PropertiesEntity.class);
+ configurator.addAnnotatedClass(LargeValueEntity.class);
+ configurator.addAnnotatedClass(ChildEntity.class);
+ configurator.addAnnotatedClass(ChildId.class);
+
+ // Set the cache information for each persistent class ...
+ // configurator.setProperty("hibernate.ejb.classcache." +
KidpackNode.class.getName(), "read-write");
+ // configurator.setProperty("hibernate.ejb.collectioncache" +
KidpackNode.class.getName() + ".distributors",
+ // "read-write, RegionName");
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,473 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.util.Serializer;
+import org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.NameFactory;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.PathFactory;
+import org.jboss.dna.graph.properties.PathNotFoundException;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.properties.ValueFactories;
+import org.jboss.dna.graph.properties.ValueFactory;
+import org.jboss.dna.graph.requests.CopyBranchRequest;
+import org.jboss.dna.graph.requests.CreateNodeRequest;
+import org.jboss.dna.graph.requests.DeleteBranchRequest;
+import org.jboss.dna.graph.requests.MoveBranchRequest;
+import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
+import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * @author Randall Hauch
+ */
+public class BasicRequestProcessor extends RequestProcessor implements LargeValues {
+
+ private final EntityManager entities;
+ private final ValueFactory<String> stringFactory;
+ private final PathFactory pathFactory;
+ private final NameFactory nameFactory;
+ private final Namespaces namespaces;
+ private final UUID rootNodeUuid;
+ private final Serializer serializer;
+ private final long largeValueMinimumSizeInBytes;
+
+ /**
+ * @param sourceName
+ * @param context
+ * @param entityManager
+ * @param rootNodeUuid
+ * @param largeValueMinimumSizeInBytes
+ */
+ public BasicRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes ) {
+ super(sourceName, context);
+ assert entityManager != null;
+ assert rootNodeUuid != null;
+ this.entities = entityManager;
+ this.stringFactory = context.getValueFactories().getStringFactory();
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ this.nameFactory = context.getValueFactories().getNameFactory();
+ this.namespaces = new Namespaces(entityManager);
+ this.rootNodeUuid = rootNodeUuid;
+ this.serializer = new Serializer(context, this);
+ this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+ // Start the transaction ...
+ this.entities.getTransaction().begin();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ // Location actualLocation = null;
+ // try {
+ // // Create nodes have to be defined via a path ...
+ // Location desiredLocation = request.at();
+ // Path desiredPath = desiredLocation.getPath();
+ // assert desiredPath != null;
+ //
+ // // // Get the parent
+ // // String parentUuidString = getUuidOf(parentLocation);
+ // // Location parentActualLocation = getActualLocation(parentLocation,
parentUuidString);
+ // // Path parentPath = parentActualLocation.getPath();
+ // //
+ // // // Now see where the child is to be created ...
+ // // request.
+ //
+ // } catch (Throwable e) { // Includes PathNotFoundException
+ // request.setError(e);
+ // return;
+ // }
+ // if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllChildrenRequest)
+ */
+ @SuppressWarnings( "unchecked" )
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ Location actualLocation = null;
+ try {
+ Location location = request.of();
+ String parentUuidString = getUuidOf(location);
+ actualLocation = getActualLocation(location, parentUuidString);
+ Path path = actualLocation.getPath();
+
+ // Find the children of the supplied node ...
+ Query query =
entities.createNamedQuery("ChildEntity.findAllUnderParent");
+ query.setParameter("uuid", parentUuidString);
+ List<ChildEntity> children = query.getResultList();
+ for (ChildEntity child : children) {
+ String namespaceUri = child.getChildNamespace().getUri();
+ String localName = child.getChildName();
+ Name childName = nameFactory.create(namespaceUri, localName);
+ Integer sns = child.getSameNameSiblingIndex();
+ if (sns == null) sns = new Integer(1);
+ Path childPath = pathFactory.create(path, childName, sns);
+ String childUuidString = child.getId().getChildUuidString();
+ Location childLocation = new Location(childPath,
UUID.fromString(childUuidString));
+ request.addChild(childLocation);
+ }
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ Location actualLocation = null;
+ try {
+ Location location = request.at();
+ String uuidString = getUuidOf(location);
+ actualLocation = getActualLocation(location, uuidString);
+
+ // Find the properties entity for this node ...
+ Query query =
entities.createNamedQuery("PropertiesEntity.findByUuid");
+ query.setParameter("uuid", uuidString);
+ PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
+
+ // Deserialize the properties ...
+ int propertyCount = entity.getPropertyCount();
+ Collection<Property> properties = new
ArrayList<Property>(propertyCount);
+ byte[] data = entity.getData();
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ serializer.deserializeProperties(ois, properties);
+ for (Property property : properties) {
+ request.addProperty(property);
+ }
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#close()
+ */
+ @Override
+ public void close() {
+ EntityTransaction txn = entities.getTransaction();
+ if (txn != null) txn.commit();
+ super.close();
+ }
+
+ protected Location getActualLocation( Location original,
+ String uuidString ) {
+ // If the original has a path and a UUID, it is complete already ...
+ Path path = original.getPath();
+ if (path != null) {
+ if (original.getIdProperty(DnaLexicon.UUID) != null) return original;
+ return original.with(UUID.fromString(uuidString));
+ }
+ // There is no path, so find it by UUID ...
+ path = getPathForUuid(uuidString);
+ return new Location(path, UUID.fromString(uuidString));
+ }
+
+ protected String getUuidOf( Location location ) throws PathNotFoundException {
+ String uuidString = null;
+ if (location.hasIdProperties()) {
+ // Look for the UUID ...
+ Property uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+ if (uuidProperty != null && !uuidProperty.isEmpty()) {
+ uuidString = stringFactory.create(uuidProperty.iterator().next());
+ }
+ }
+ if (uuidString == null) {
+ // Look up the node by using the path to walk down the children, starting at
the root ...
+ Path path = location.getPath();
+ if (path == null) {
+ // Location does not have path or DnaLexicon.UUID id property
+ }
+ assert path != null;
+ if (path.isRoot()) {
+ uuidString = rootNodeUuid.toString();
+ } else {
+ String parentUuid = this.rootNodeUuid.toString();
+ ChildEntity child = null;
+ for (Path.Segment segment : path) {
+ child = findByPathSegment(parentUuid, segment);
+ if (child == null) {
+ // Determine the lowest path that exists ...
+ Path lowest = path;
+ while (lowest.getLastSegment() != segment) {
+ lowest = lowest.getParent();
+ }
+ lowest = lowest.getParent();
+ throw new PathNotFoundException(location, lowest);
+ }
+ }
+ assert child != null;
+ uuidString = child.getId().getChildUuidString();
+ }
+ }
+ assert uuidString != null;
+ return uuidString;
+ }
+
+ /**
+ * Find the node with the supplied path segment that is a child of the supplied
parent.
+ *
+ * @param parentUuid the UUID of the parent node, in string form
+ * @param pathSegment the path segment of the child
+ * @return the existing namespace, or null if one does not exist
+ * @throws IllegalArgumentException if the manager or URI are null
+ */
+ protected ChildEntity findByPathSegment( String parentUuid,
+ Path.Segment pathSegment ) {
+ assert namespaces != null;
+ assert parentUuid != null;
+ assert pathSegment != null;
+ Name name = pathSegment.getName();
+ String localName = name.getLocalName();
+ String nsUri = name.getNamespaceUri();
+ Integer nsId = namespaces.getId(nsUri, false);
+ if (nsId == null) {
+ // The namespace can't be found, then certainly the node won't be
found ...
+ return null;
+ }
+ Query query =
entities.createNamedQuery("ChildEntity.findByPathSegment");
+ query.setParameter("parentUuidString", parentUuid);
+ query.setParameter("ns", nsId);
+ query.setParameter("childName", localName);
+ if (pathSegment.hasIndex()) {
+ query.setParameter("sns", localName);
+ } else {
+ query.setParameter("sns", null);
+ }
+ try {
+ return (ChildEntity)query.getSingleResult();
+ } catch (NoResultException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Build up the path for the node with the supplied UUID.
+ *
+ * @param uuidString the UUID of the node
+ * @return the path to the node
+ */
+ protected Path getPathForUuid( String uuidString ) {
+ ChildEntity entity = null;
+ String childUuid = uuidString;
+ LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
+ do {
+ // Find the parent of the UUID ...
+ Query query =
entities.createNamedQuery("ChildEntity.findParentByUuid");
+ query.setParameter("childUuidString", childUuid);
+ try {
+ entity = (ChildEntity)query.getSingleResult();
+ String localName = entity.getChildName();
+ String uri = entity.getChildNamespace().getUri();
+ Integer sns = entity.getSameNameSiblingIndex();
+ Name name = nameFactory.create(uri, localName);
+ if (sns != null) {
+ segments.addFirst(pathFactory.createSegment(name, sns));
+ } else {
+ segments.addFirst(pathFactory.createSegment(name));
+ }
+ } catch (NoResultException e) {
+ entity = null;
+ }
+ } while (entity != null);
+ return pathFactory.createAbsolutePath(segments);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return largeValueMinimumSizeInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ String hashStr = StringUtil.getHexString(hash);
+ LargeValueEntity entity = entities.find(LargeValueEntity.class, hashStr);
+ if (entity == null) {
+ throw new
IOException(JpaConnectorI18n.unableToReadLargeValue.text(getSourceName(), hashStr));
+ }
+ byte[] data = entity.getData();
+ return valueFactories.getValueFactory(entity.getType()).create(data);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#write(byte[],
long,
+ * org.jboss.dna.graph.properties.PropertyType, java.lang.Object)
+ */
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException {
+ if (value == null) return;
+ String hashStr = StringUtil.getHexString(hash);
+ LargeValueEntity entity = entities.find(LargeValueEntity.class, hashStr);
+ if (entity == null) {
+ entity = new LargeValueEntity();
+ entity.setCompressed(true);
+ entity.setHash(hashStr);
+ entity.setLength(length);
+ entity.setType(type);
+ ValueFactories factories = getExecutionContext().getValueFactories();
+ switch (type) {
+ case BINARY:
+ Binary binary = factories.getBinaryFactory().create(value);
+ try {
+ binary.acquire();
+ entity.setData(binary.getBytes());
+ } finally {
+ binary.release();
+ }
+ break;
+ default:
+ String str = factories.getStringFactory().create(value);
+ entity.setData(str.getBytes());
+ break;
+ }
+ entities.persist(entity);
+ } else {
+ // There is already an existing value, so we'll reuse it and increment
the usage count ...
+ entity.incrementUsageCount();
+ }
+ }
+
+ protected static class Namespaces {
+
+ private final EntityManager entityManager;
+ private final Map<String, Integer> cache = new HashMap<String,
Integer>();
+
+ public Namespaces( EntityManager manager ) {
+ this.entityManager = manager;
+ }
+
+ public Integer getId( String namespaceUri,
+ boolean createIfRequired ) {
+ Integer id = cache.get(namespaceUri);
+ if (id == null) {
+ NamespaceEntity entity = NamespaceEntity.findByUri(entityManager,
namespaceUri, createIfRequired);
+ if (entity == null) return null;
+ id = entity.getId();
+ cache.put(namespaceUri, id);
+ }
+ assert id != null;
+ return id;
+ }
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,220 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import org.hibernate.annotations.Index;
+import org.hibernate.annotations.Table;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+
+/**
+ * An entity representing the parent-child relationship between two nodes. In addition to
the references to the parent and child
+ * nodes, this entity also maintains the indexInParent of the indexInParent within the
parent node's list of all children, the
+ * child's name ( {@link #getChildName() local part} and {@link #getChildNamespace()
namespace}), and the same-name-sibiling
+ * indexInParent (if there is one).
+ *
+ * @author Randall Hauch
+ */
+@Entity
+(a)javax.persistence.Table( name = "DNA_BASIC_CHILDREN" )
+@Table( appliesTo = "DNA_BASIC_CHILDREN", indexes = @Index( name =
"CHILDINDEX_INX", columnNames = {"PARENT_UUID",
"CHILD_INDEX"} ) )
+@NamedQueries( {
+ @NamedQuery( name = "ChildEntity.findByPathSegment", query = "select
child from ChildEntity as child where child.id.parentUuidString = :parentUuid AND
child.childNamespace.id = :ns AND child.childName = :childName AND
child.sameNameSiblingIndex = :sns" ),
+ @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select
child from ChildEntity as child where child.id.parentUuidString = :parentUuid" )} )
+public class ChildEntity {
+
+ @Id
+ private ChildId id;
+
+ @Column( name = "CHILD_INDEX", nullable = false, unique = false )
+ private Integer indexInParent;
+
+ @ManyToOne
+ @JoinColumn( name = "CHILD_NAME_NS_ID", nullable = false )
+ private NamespaceEntity childNamespace;
+
+ @Column( name = "CHILD_NAME_LOCAL", nullable = true, unique = false, length
= 512 )
+ private String childName;
+
+ @Column( name = "SNS_INDEX", nullable = true, unique = false )
+ private Integer sameNameSiblingIndex;
+
+ public ChildEntity() {
+ }
+
+ public ChildEntity( ChildId id,
+ int indexInParent,
+ NamespaceEntity ns,
+ String name ) {
+ this.id = id;
+ this.indexInParent = indexInParent;
+ this.childNamespace = ns;
+ this.childName = name;
+ }
+
+ public ChildEntity( ChildId id,
+ int indexInParent,
+ NamespaceEntity ns,
+ String name,
+ int sameNameSiblingIndex ) {
+ this.id = id;
+ this.indexInParent = indexInParent;
+ this.childNamespace = ns;
+ this.childName = name;
+ this.sameNameSiblingIndex = sameNameSiblingIndex;
+ }
+
+ /**
+ * @return parent
+ */
+ public ChildId getId() {
+ return id;
+ }
+
+ /**
+ * @param childId Sets parent to the specified value.
+ */
+ public void setId( ChildId childId ) {
+ this.id = childId;
+ }
+
+ /**
+ * @return indexInParent
+ */
+ public Integer getIndexInParent() {
+ return indexInParent;
+ }
+
+ /**
+ * @param index Sets indexInParent to the specified value.
+ */
+ public void setIndexInParent( Integer index ) {
+ this.indexInParent = index;
+ }
+
+ /**
+ * @return childName
+ */
+ public String getChildName() {
+ return childName;
+ }
+
+ /**
+ * @param childName Sets childName to the specified value.
+ */
+ public void setChildName( String childName ) {
+ this.childName = childName;
+ }
+
+ /**
+ * @return childNamespace
+ */
+ public NamespaceEntity getChildNamespace() {
+ return childNamespace;
+ }
+
+ /**
+ * @param childNamespace Sets childNamespace to the specified value.
+ */
+ public void setChildNamespace( NamespaceEntity childNamespace ) {
+ this.childNamespace = childNamespace;
+ }
+
+ /**
+ * @return sameNameSiblingIndex
+ */
+ public Integer getSameNameSiblingIndex() {
+ return sameNameSiblingIndex;
+ }
+
+ /**
+ * @param sameNameSiblingIndex Sets sameNameSiblingIndex to the specified value.
+ */
+ public void setSameNameSiblingIndex( Integer sameNameSiblingIndex ) {
+ this.sameNameSiblingIndex = sameNameSiblingIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(id);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChildEntity) {
+ ChildEntity that = (ChildEntity)obj;
+ if (this.id == null) {
+ if (that.id != null) return false;
+ } else {
+ if (!this.id.equals(that.id)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (childNamespace != null) {
+ sb.append('{').append(childNamespace).append("}:");
+ }
+ sb.append(childName);
+ if (sameNameSiblingIndex != null && sameNameSiblingIndex.intValue() >
1) {
+ sb.append('[').append(sameNameSiblingIndex).append(']');
+ }
+ if (id != null) {
+ sb.append("
(id=").append(id.getChildUuidString()).append(")");
+ String parentId = id.getParentUuidString();
+ if (parentId != null) {
+ sb.append(" is child of ").append(parentId);
+ } else {
+ sb.append(" is root");
+ }
+ }
+ return sb.toString();
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import java.io.Serializable;
+import java.util.UUID;
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+
+/**
+ * A unique identifer for a parent-child relationship.
+ *
+ * @author Randall Hauch
+ */
+@Embeddable
+public class ChildId implements Serializable {
+
+ /**
+ * Version {@value}
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Column( name = "PARENT_UUID", nullable = false )
+ private String parentUuidString;
+
+ @Column( name = "CHILD_UUID", nullable = false )
+ private String childUuidString;
+
+ private transient UUID parentUuid;
+ private transient UUID childUuid;
+
+ public ChildId() {
+ }
+
+ public ChildId( UUID parentUuid,
+ UUID childUuid ) {
+ setParentUuid(parentUuid);
+ setChildUuid(childUuid);
+ }
+
+ public ChildId( NodeId parentId,
+ NodeId childId ) {
+ if (parentId != null) setParentUuid(parentId.getUuid());
+ if (childId != null) setChildUuid(childId.getUuid());
+ }
+
+ public ChildId( String parentUuid,
+ String childUuid ) {
+ setParentUuidString(parentUuid);
+ setChildUuidString(childUuid);
+ }
+
+ public UUID getParentUuid() {
+ if (parentUuidString == null) return null;
+ if (parentUuid == null) {
+ parentUuid = UUID.fromString(parentUuidString);
+ }
+ return parentUuid;
+ }
+
+ public void setParentUuid( UUID uuid ) {
+ this.parentUuid = uuid;
+ this.parentUuidString = uuid != null ? uuid.toString() : null;
+ }
+
+ public UUID getChildUuid() {
+ if (childUuidString == null) return null;
+ if (childUuid == null) {
+ childUuid = UUID.fromString(childUuidString);
+ }
+ return childUuid;
+ }
+
+ public void setChildUuid( UUID uuid ) {
+ this.childUuid = uuid;
+ this.childUuidString = uuid != null ? uuid.toString() : null;
+ }
+
+ /**
+ * @return parentUuidString
+ */
+ public String getParentUuidString() {
+ return parentUuidString;
+ }
+
+ /**
+ * @param parentUuidString Sets parentUuidString to the specified value.
+ */
+ public void setParentUuidString( String parentUuidString ) {
+ this.parentUuid = null;
+ this.parentUuidString = parentUuidString;
+ }
+
+ /**
+ * @return childUuidString
+ */
+ public String getChildUuidString() {
+ return childUuidString;
+ }
+
+ /**
+ * @param childUuidString Sets childUuidString to the specified value.
+ */
+ public void setChildUuidString( String childUuidString ) {
+ this.childUuid = null;
+ this.childUuidString = childUuidString;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(parentUuidString, childUuidString);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChildId) {
+ ChildId that = (ChildId)obj;
+ if (this.parentUuidString == null) {
+ if (that.parentUuidString != null) return false;
+ } else {
+ if (!this.parentUuidString.equals(that.parentUuidString)) return false;
+ }
+ if (this.childUuidString == null) {
+ if (that.childUuidString != null) return false;
+ } else {
+ if (!this.childUuidString.equals(that.childUuidString)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Child " + childUuidString + " of " +
parentUuidString;
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,204 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import org.jboss.dna.graph.properties.PropertyType;
+
+/**
+ * A single property value that is too large to be stored on the individual node.
+ *
+ * @author Randall Hauch
+ */
+@Entity( name = "DNA_BASIC_LARGE_VALUES" )
+public class LargeValueEntity {
+
+ /**
+ * The 160-bit SHA-1 hash of this value, in hex form (40-bytes). The SHA-1 algorithm
is fast and has not yet proven to have
+ * any duplicates. Even if SHA-2 and SHA-3 are better for cryptographically secure
purposes, it is doubtful whether a
+ * repository needs more than SHA-1.
+ */
+ @Id
+ @Column( name = "SHA1", nullable = false, unique = true, length = 40 )
+ private String hash;
+
+ /**
+ * The property type for this value. Typically, this is {@link PropertyType#STRING}
or {@link PropertyType#BINARY}, although
+ * technically it could be any type.
+ */
+ @Enumerated( value = EnumType.STRING )
+ @Column( name = "TYPE", nullable = false )
+ private PropertyType type;
+
+ /**
+ * The number of bytes in this value.
+ */
+ @Column( name = "LENGTH", nullable = false )
+ private long length;
+
+ /**
+ * The number of times this value is used. If this value drops below 1, the value
could be removed from the store.
+ */
+ @Column( name = "USAGE_COUNT", nullable = false )
+ private int usageCount = 1;
+
+ /**
+ * Flag specifying whether the binary data is stored in a compressed format.
+ */
+ @Column( name = "COMPRESSED", nullable = true )
+ private Boolean compressed;
+
+ /**
+ * Lazily-fetched value
+ */
+ @Lob
+ @Column( name = "DATA", nullable = false )
+ private byte[] data;
+
+ /**
+ * @return hash
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * @param hash Sets hash to the specified value.
+ */
+ public void setHash( String hash ) {
+ this.hash = hash;
+ }
+
+ /**
+ * @return length
+ */
+ public long getLength() {
+ return length;
+ }
+
+ /**
+ * @param length Sets length to the specified value.
+ */
+ public void setLength( long length ) {
+ this.length = length;
+ }
+
+ /**
+ * @return type
+ */
+ public PropertyType getType() {
+ return type;
+ }
+
+ /**
+ * @param type Sets type to the specified value.
+ */
+ public void setType( PropertyType type ) {
+ this.type = type;
+ }
+
+ /**
+ * @return data
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * @param data Sets data to the specified value.
+ */
+ public void setData( byte[] data ) {
+ this.data = data;
+ }
+
+ /**
+ * @return usageCount
+ */
+ public int getUsageCount() {
+ return usageCount;
+ }
+
+ /**
+ * @param usageCount Sets usageCount to the specified value.
+ */
+ public void setUsageCount( int usageCount ) {
+ this.usageCount = usageCount;
+ }
+
+ public void incrementUsageCount() {
+ this.usageCount++;
+ }
+
+ /**
+ * @return compressed
+ */
+ public boolean isCompressed() {
+ return compressed != null && compressed.booleanValue();
+ }
+
+ /**
+ * @param compressed Sets compressed to the specified value.
+ */
+ public void setCompressed( boolean compressed ) {
+ this.compressed = Boolean.valueOf(compressed);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getHash().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof LargeValueEntity) {
+ LargeValueEntity that = (LargeValueEntity)obj;
+ if (this.getHash().equals(that.getHash())) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Large " + this.type + " value (hash=" + this.hash +
",compressed=" + isCompressed() + ")";
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,167 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+import org.jboss.dna.connector.store.jpa.util.Serializer;
+
+/**
+ * Represents the packed properties of a single node. Node that the object has the
node's identifier and the packed properties,
+ * but nothing else. The PropertiesEntity doesn't even have the name. This is because
this class is used to read, modify, and save
+ * the properties of a node. Finding a node by its name or working with the children,
however, requires working with the
+ * {@link ChildEntity node children}.
+ *
+ * @author Randall Hauch
+ */
+@Entity
+@Table( name = "DNA_BASIC_NODEPROPS" )
+@NamedQueries( {@NamedQuery( name = "PropertiesEntity.findByUuid", query =
"select prop from PropertiesEntity as prop where prop.id.uuidString = :uuid" )}
)
+public class PropertiesEntity {
+ @EmbeddedId
+ private NodeId id;
+
+ @Lob
+ @Column( name = "DATA", nullable = false, unique = false )
+ private byte[] data;
+
+ @Column( name = "NUM_PROPS", nullable = false )
+ private int propertyCount;
+
+ /**
+ * Flag specifying whether the binary data is stored in a compressed format.
+ */
+ @Column( name = "COMPRESSED", nullable = true )
+ private Boolean compressed;
+
+ public PropertiesEntity() {
+ }
+
+ public PropertiesEntity( NodeId id ) {
+ setId(id);
+ }
+
+ /**
+ * Get the node's identifier.
+ *
+ * @return the node's identifier
+ */
+ public NodeId getId() {
+ return id;
+ }
+
+ /**
+ * Set the node's identifier.
+ *
+ * @param id the new identifier for the node
+ */
+ public void setId( NodeId id ) {
+ this.id = id;
+ }
+
+ /**
+ * Get the data that represents the {@link Serializer packed} properties.
+ *
+ * @return the raw data representing the properties
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Set the data that represents the {@link Serializer packed} properties.
+ *
+ * @param data the raw data representing the properties
+ */
+ public void setData( byte[] data ) {
+ this.data = data;
+ }
+
+ /**
+ * @return propertyCount
+ */
+ public int getPropertyCount() {
+ return propertyCount;
+ }
+
+ /**
+ * @param propertyCount Sets propertyCount to the specified value.
+ */
+ public void setPropertyCount( int propertyCount ) {
+ this.propertyCount = propertyCount;
+ }
+
+ /**
+ * @return compressed
+ */
+ public boolean isCompressed() {
+ return compressed != null && compressed.booleanValue();
+ }
+
+ /**
+ * @param compressed Sets compressed to the specified value.
+ */
+ public void setCompressed( boolean compressed ) {
+ this.compressed = Boolean.valueOf(compressed);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PropertiesEntity) {
+ PropertiesEntity that = (PropertiesEntity)obj;
+ if (this.getId().equals(that.getId())) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Properties for " + this.id;
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,170 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.common;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A NamespaceEntity represents a namespace that has been used in the store.
NamespaceEntity records are immutable and shared by
+ * one or more enities.
+ *
+ * @author Randall Hauch
+ */
+@Entity( name = "DNA_NAMESPACES" )
+@NamedQueries( {@NamedQuery( name = "NamespaceEntity.findAll", query =
"SELECT ns FROM DNA_NAMESPACES AS ns" ),
+ @NamedQuery( name = "NamespaceEntity.findByUri", query = "SELECT ns
FROM DNA_NAMESPACES AS ns WHERE ns.uri = ?1" )} )
+public class NamespaceEntity {
+
+ @Id
+ @GeneratedValue( strategy = GenerationType.AUTO )
+ private Integer id;
+
+ @Column( name = "URI", nullable = false, unique = false, length = 512,
updatable = false )
+ private String uri;
+
+ /**
+ *
+ */
+ public NamespaceEntity() {
+ }
+
+ /**
+ * @param uri the namespace URI
+ */
+ public NamespaceEntity( String uri ) {
+ setUri(uri);
+ }
+
+ /**
+ * @return id
+ */
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @param id Sets id to the specified value.
+ */
+ public void setId( Integer id ) {
+ this.id = id;
+ }
+
+ /**
+ * @return uri
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * @param uri Sets uri to the specified value.
+ */
+ public void setUri( String uri ) {
+ this.uri = uri;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NamespaceEntity) {
+ NamespaceEntity that = (NamespaceEntity)obj;
+ if (!this.id.equals(that.id)) return false;
+ if (!this.uri.equals(that.uri)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return uri;
+ }
+
+ /**
+ * Find an existing namespace by its URI, or create and return one if it does not
already exist.
+ *
+ * @param manager the entity manager
+ * @param uri the URI
+ * @return the existing namespace, or null if one does not exist
+ * @throws IllegalArgumentException if the manager or URI are null
+ */
+ public static NamespaceEntity findByUri( EntityManager manager,
+ String uri ) {
+ return findByUri(manager, uri, true);
+ }
+
+ /**
+ * Find an existing namespace by its URI.
+ *
+ * @param manager the entity manager
+ * @param uri the URI
+ * @param createIfRequired if the namespace should be persisted if it does not yet
exist
+ * @return the existing namespace, or null if one does not exist
+ * @throws IllegalArgumentException if the manager or URI are null
+ */
+ public static NamespaceEntity findByUri( EntityManager manager,
+ String uri,
+ boolean createIfRequired ) {
+ CheckArg.isNotNull(manager, "manager");
+ CheckArg.isNotNull(uri, "uri");
+ Query query = manager.createNamedQuery("NamespaceEntity.findByUri");
+ query.setParameter(1, uri);
+ try {
+ return (NamespaceEntity)query.getSingleResult();
+ } catch (NoResultException e) {
+ if (!createIfRequired) return null;
+ NamespaceEntity namespace = new NamespaceEntity(uri);
+ manager.persist(namespace);
+ return namespace;
+ }
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,123 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.common;
+
+import java.io.Serializable;
+import java.util.UUID;
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+
+/**
+ * An identifier for a node, comprised of a single {@link UUID}, and {@link Embeddable
embeddable} in a persistent entity. The
+ * identifier takes the form of two <code>long</code> columns: one for the
UUID's {@link UUID#getMostSignificantBits() most
+ * significant bits} and one for its {@link UUID#getLeastSignificantBits() least
significant bits}.
+ *
+ * @author Randall Hauch
+ */
+@Embeddable
+public class NodeId implements Serializable {
+
+ /**
+ * Version {@value}
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Column( name = "UUID", nullable = true )
+ private String uuidString;
+
+ private transient UUID uuid;
+
+ public NodeId() {
+ }
+
+ public NodeId( UUID uuid ) {
+ setUuid(uuid);
+ }
+
+ public UUID getUuid() {
+ if (uuid == null) {
+ // No need to synchronize, since it is idempotent ...
+ uuid = UUID.fromString(uuidString);
+ }
+ return uuid;
+ }
+
+ public void setUuid( UUID uuid ) {
+ assert uuid != null;
+ this.uuid = uuid;
+ this.uuidString = uuid.toString();
+ }
+
+ /**
+ * @return uuidString
+ */
+ public String getUuidString() {
+ return uuidString;
+ }
+
+ /**
+ * @param uuidString Sets uuidString to the specified value.
+ */
+ public void setUuidString( String uuidString ) {
+ this.uuidString = uuidString;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getUuid().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NodeId) {
+ NodeId that = (NodeId)obj;
+ if (this.uuidString == null) {
+ if (that.uuidString != null) return false;
+ } else {
+ if (!this.uuidString.equals(that.uuidString)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getUuid().toString();
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.persistence.EntityManager;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+
+/**
+ * @author Randall Hauch
+ */
+public class Namespaces {
+
+ private final EntityManager entityManager;
+ private final Map<String, Integer> cache = new HashMap<String,
Integer>();
+
+ public Namespaces( EntityManager manager ) {
+ this.entityManager = manager;
+ }
+
+ public int getId( String namespaceUri ) {
+ Integer id = cache.get(namespaceUri);
+ if (id == null) {
+ NamespaceEntity entity = NamespaceEntity.findByUri(entityManager,
namespaceUri, true);
+ id = entity.getId();
+ cache.put(namespaceUri, id);
+ }
+ assert id != null;
+ return id;
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,415 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+import java.util.UUID;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.connector.store.jpa.models.basic.LargeValueEntity;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.DateTime;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.PropertyFactory;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.properties.Reference;
+import org.jboss.dna.graph.properties.ValueFactories;
+
+/**
+ * @author Randall Hauch
+ */
+public class Serializer {
+
+ private final PropertyFactory propertyFactory;
+ private final ValueFactories valueFactories;
+ private final LargeValues largeValues;
+
+ public Serializer( ExecutionContext context,
+ LargeValues largeValues ) {
+ this.propertyFactory = context.getPropertyFactory();
+ this.valueFactories = context.getValueFactories();
+ this.largeValues = largeValues;
+ }
+
+ /**
+ * Interface that represents the location where "large" objects are
stored.
+ *
+ * @author Randall Hauch
+ */
+ public interface LargeValues {
+ /**
+ * Get the minimum size for large values, specified as {@link String#length()
number of characters} for a {@link String}
+ * or the {@link Binary#getSize() number of bytes for a binary value}
+ *
+ * @return the size at which a property value is considered to be
<i>large</i>
+ */
+ long getMinimumSize();
+
+ void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException;
+
+ Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException;
+ }
+
+ /**
+ * Serialize the properties' values to the object stream.
+ * <p>
+ * If any of the property values are considered {@link LargeValues#getMinimumSize()
large}, the value's hash and length of the
+ * property value will be written to the object stream, but the property value will
be sent to the supplied
+ * {@link LargeValueEntity} object.
+ * </p>
+ * <p>
+ * This method does not automatically write each property value to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)}, but instead serializes the
primitive values that make up the property value
+ * object with a code that describes the {@link PropertyType property's type}.
This is more efficient, since most of the
+ * property values are really non-primitive objects, and writing to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)} would include larger class
metadata.
+ * </p>
+ *
+ * @param stream the stream where the properties' values are to be serialized;
may not be null
+ * @param number the number of properties exposed by the supplied
<code>properties</code> iterator; must be 0 or positive
+ * @param properties the iterator over the properties that are to be serialized; may
not be null
+ * @throws IOException if there is an error writing to the
<code>stream</code> or <code>largeValues</code>
+ * @see #deserializeProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property)
+ */
+ public void serializeProperties( ObjectOutputStream stream,
+ int number,
+ Iterable<Property> properties ) throws
IOException {
+ assert number >= 0;
+ assert properties != null;
+ stream.writeInt(number);
+ for (Property property : properties) {
+ if (property == null) continue;
+ serializeProperty(stream, property);
+ }
+ }
+
+ /**
+ * Serialize the property's values to the object stream.
+ * <p>
+ * If any of the property values are considered {@link LargeValues#getMinimumSize()
large}, the value's hash and length of the
+ * property value will be written to the object stream, but the property value will
be sent to the supplied
+ * {@link LargeValueEntity} object.
+ * </p>
+ * <p>
+ * This method does not automatically write each property value to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)}, but instead serializes the
primitive values that make up the property value
+ * object with a code that describes the {@link PropertyType property's type}.
This is more efficient, since most of the
+ * property values are really non-primitive objects, and writing to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)} would include larger class
metadata.
+ * </p>
+ *
+ * @param stream the stream where the property's values are to be serialized; may
not be null
+ * @param property the property to be serialized; may not be null
+ * @throws IOException if there is an error writing to the
<code>stream</code> or <code>largeValues</code>
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable)
+ * @see #deserializeProperty(ObjectInputStream)
+ */
+ public void serializeProperty( ObjectOutputStream stream,
+ Property property ) throws IOException {
+ assert stream != null;
+ assert property != null;
+ // Write the name ...
+ stream.writeObject(property.getName().getString());
+ // Write the number of values ...
+ stream.writeInt(property.size());
+ for (Object value : property) {
+ if (value instanceof String) {
+ String stringValue = (String)value;
+ if (largeValues != null && stringValue.length() >
largeValues.getMinimumSize()) {
+ // Store the value in the large values area, but record the hash and
length here.
+ byte[] hash = computeHash(stringValue);
+ stream.writeChar('L');
+ stream.writeInt(hash.length);
+ stream.write(hash);
+ stream.writeLong(stringValue.length());
+ // Now write to the large objects ...
+ largeValues.write(computeHash(stringValue), stringValue.length(),
PropertyType.STRING, stringValue);
+ } else {
+ stream.writeChar('S');
+ stream.writeObject(stringValue);
+ }
+ } else if (value instanceof Boolean) {
+ stream.writeChar('b');
+ stream.writeBoolean(((Boolean)value).booleanValue());
+ } else if (value instanceof Long) {
+ stream.writeChar('l');
+ stream.writeLong(((Long)value).longValue());
+ } else if (value instanceof Double) {
+ stream.writeChar('d');
+ stream.writeDouble(((Double)value).doubleValue());
+ } else if (value instanceof Integer) {
+ stream.writeChar('i');
+ stream.writeInt(((Integer)value).intValue());
+ } else if (value instanceof Short) {
+ stream.writeChar('s');
+ stream.writeShort(((Short)value).shortValue());
+ } else if (value instanceof Float) {
+ stream.writeChar('f');
+ stream.writeFloat(((Float)value).floatValue());
+ } else if (value instanceof UUID) {
+ stream.writeChar('U');
+ UUID uuid = (UUID)value;
+ stream.writeLong(uuid.getMostSignificantBits());
+ stream.writeLong(uuid.getLeastSignificantBits());
+ } else if (value instanceof URI) {
+ stream.writeChar('I');
+ stream.writeObject(((URI)value).toString());
+ } else if (value instanceof Name) {
+ stream.writeChar('N');
+ stream.writeObject(((Name)value).getString());
+ } else if (value instanceof Path) {
+ stream.writeChar('P');
+ stream.writeObject(((Path)value).getString());
+ } else if (value instanceof DateTime) {
+ stream.writeChar('T');
+ stream.writeObject(((DateTime)value).getString());
+ } else if (value instanceof BigDecimal) {
+ stream.writeChar('D');
+ stream.writeObject(value);
+ } else if (value instanceof Character) {
+ stream.writeChar('c');
+ char c = ((Character)value).charValue();
+ stream.writeChar(c);
+ } else if (value instanceof Reference) {
+ stream.writeChar('R');
+ stream.writeObject(((Reference)value).getString());
+ } else if (value instanceof Binary) {
+ Binary binary = (Binary)value;
+ byte[] hash = null;
+ long length = 0;
+ try {
+ binary.acquire();
+ length = binary.getSize();
+ if (largeValues != null && length >
largeValues.getMinimumSize()) {
+ // Store the value in the large values area, but record the hash
and length here.
+ hash = binary.getHash();
+ stream.writeChar('L');
+ stream.writeInt(hash.length);
+ stream.write(hash);
+ stream.writeLong(length);
+ // Write to large objects after releasing the binary
+ } else {
+ // The value is small enough to store here ...
+ stream.writeChar('B');
+ stream.writeLong(length);
+ InputStream data = binary.getStream();
+ try {
+ byte[] buffer = new byte[1024];
+ int numRead = 0;
+ while ((numRead = data.read(buffer)) > -1) {
+ stream.write(buffer, 0, numRead);
+ }
+ } finally {
+ data.close();
+ }
+ }
+ } finally {
+ binary.release();
+ }
+ // If this is a large value and the binary has been released, write it to
the large objects ...
+ if (largeValues != null && hash != null) {
+ largeValues.write(hash, length, PropertyType.BINARY, value);
+ }
+ } else {
+ // Other kinds of values ...
+ stream.writeChar('O');
+ stream.writeObject(value);
+ }
+ }
+ stream.flush();
+ }
+
+ /**
+ * Deserialize the serialized properties on the supplied object stream.
+ *
+ * @param stream the stream that contains the serialized properties; may not be null
+ * @param properties the collection into which each deserialized property is to be
placed; may not be null
+ * @throws IOException if there is an error writing to the
<code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not
be found
+ * @see #deserializeProperty(ObjectInputStream)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable)
+ */
+ public void deserializeProperties( ObjectInputStream stream,
+ Collection<Property> properties ) throws
IOException, ClassNotFoundException {
+ assert propertyFactory != null;
+ assert valueFactories != null;
+ assert stream != null;
+ assert properties != null;
+ assert largeValues != null;
+ // Read the number of properties ...
+ int count = stream.readInt();
+ for (int i = 0; i != count; ++i) {
+ Property property = deserializeProperty(stream);
+ assert property != null;
+ properties.add(property);
+ }
+ }
+
+ /**
+ * Deserialize the serialized property on the supplied object stream.
+ *
+ * @param stream the stream that contains the serialized properties; may not be null
+ * @return the deserialized property; never null
+ * @throws IOException if there is an error writing to the
<code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not
be found
+ * @see #deserializeProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property)
+ */
+ public Property deserializeProperty( ObjectInputStream stream ) throws IOException,
ClassNotFoundException {
+ assert propertyFactory != null;
+ assert valueFactories != null;
+ assert stream != null;
+ assert largeValues != null;
+ // Read the name ...
+ String nameStr = (String)stream.readObject();
+ Name name = valueFactories.getNameFactory().create(nameStr);
+ assert name != null;
+ // Read the number of values ...
+ int size = stream.readInt();
+ Object[] values = new Object[size];
+ for (int i = 0; i != size; ++i) {
+ Object value = null;
+ // Read the type of value ...
+ char type = stream.readChar();
+ switch (type) {
+ case 'S':
+ // String
+ value =
valueFactories.getStringFactory().create((String)stream.readObject());
+ break;
+ case 'b':
+ // boolean
+ value =
valueFactories.getBooleanFactory().create(stream.readBoolean());
+ break;
+ case 'i':
+ // integer
+ value = valueFactories.getLongFactory().create(stream.readInt());
+ break;
+ case 'l':
+ // long
+ value = valueFactories.getLongFactory().create(stream.readLong());
+ break;
+ case 's':
+ // short
+ value = valueFactories.getLongFactory().create(stream.readShort());
+ break;
+ case 'f':
+ // float
+ value =
valueFactories.getDoubleFactory().create(stream.readFloat());
+ break;
+ case 'd':
+ // double
+ value =
valueFactories.getDoubleFactory().create(stream.readDouble());
+ break;
+ case 'c':
+ // double
+ value = valueFactories.getStringFactory().create("" +
stream.readChar());
+ break;
+ case 'U':
+ // UUID
+ long msb = stream.readLong();
+ long lsb = stream.readLong();
+ UUID uuid = new UUID(msb, lsb);
+ value = valueFactories.getUuidFactory().create(uuid);
+ break;
+ case 'I':
+ // URI
+ String uriStr = (String)stream.readObject();
+ value = valueFactories.getUriFactory().create(uriStr);
+ break;
+ case 'N':
+ // Name
+ String nameValueStr = (String)stream.readObject();
+ value = valueFactories.getNameFactory().create(nameValueStr);
+ break;
+ case 'P':
+ // Path
+ String pathStr = (String)stream.readObject();
+ value = valueFactories.getPathFactory().create(pathStr);
+ break;
+ case 'T':
+ // DateTime
+ String dateTimeStr = (String)stream.readObject();
+ value = valueFactories.getDateFactory().create(dateTimeStr);
+ break;
+ case 'D':
+ // BigDecimal
+ Object bigDecimal = stream.readObject();
+ value = valueFactories.getDecimalFactory().create(bigDecimal);
+ break;
+ case 'R':
+ // Reference
+ value =
valueFactories.getReferenceFactory().create((String)stream.readObject());
+ break;
+ case 'B':
+ // Binary
+ // Read the length of the content ...
+ long binaryLength = stream.readLong();
+ value = valueFactories.getBinaryFactory().create(stream,
binaryLength);
+ break;
+ case 'L':
+ // Large object ...
+ // Read the hash ...
+ int hashLength = stream.readInt();
+ byte[] hash = new byte[hashLength];
+ stream.read(hash);
+ // Read the length of the content ...
+ long length = stream.readLong();
+ value = largeValues.read(valueFactories, hash, length);
+ break;
+ default:
+ // All other objects ...
+ Object object = stream.readObject();
+ value = valueFactories.getObjectFactory().create(object);
+ break;
+ }
+ assert value != null;
+ values[i] = value;
+ }
+ // Add the property to the collection ...
+ return propertyFactory.create(name, values);
+ }
+
+ public byte[] computeHash( String value ) {
+ try {
+ return SecureHash.getHash(SecureHash.Algorithm.SHA_1, value.getBytes());
+ } catch (NoSuchAlgorithmException e) {
+ throw new SystemFailureException(e);
+ }
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,143 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.Model;
+
+/**
+ * An option for the store. This is typically used to save store-specific values.
+ * <p>
+ * This JPA entity is always added to the {@link Ejb3Configuration} in the {@link
JpaSource#getConnection() JpaSource}, and
+ * therefore should not be {@link Model#configure(Ejb3Configuration) added to the
configuration} by a {@link Model}.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+@Entity( name = "DNA_OPTIONS" )
+@NamedQueries( {@NamedQuery( name = "StoreOptionEntity.findAll", query =
"SELECT option FROM DNA_OPTIONS AS option" )} )
+public class StoreOptionEntity {
+
+ @Id
+ @Column( name = "NAME", nullable = false, unique = true, length = 512 )
+ private String name;
+
+ @Column( name = "VALUE", nullable = false, unique = false, length = 512 )
+ private String value;
+
+ /**
+ *
+ */
+ protected StoreOptionEntity() {
+ }
+
+ /**
+ * @param name the name of the option; may not be null or empty
+ * @param value the value of the option; may be null
+ */
+ public StoreOptionEntity( String name,
+ String value ) {
+ CheckArg.isNotEmpty(name, "name");
+ setName(name);
+ setValue(value);
+ }
+
+ /**
+ * @param name the name of the option; may not be null or empty
+ */
+ public StoreOptionEntity( String name ) {
+ CheckArg.isNotEmpty(name, "name");
+ setName(name);
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ /**
+ * @return value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * @param value Sets value to the specified value.
+ */
+ public void setValue( String value ) {
+ this.value = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof StoreOptionEntity) {
+ StoreOptionEntity that = (StoreOptionEntity)obj;
+ if (!this.getName().equals(that.getName())) return false;
+ if (!this.getValue().equals(that.getValue())) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Option " + getName() + " = \"" + getValue() +
"\"";
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,103 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.connector.store.jpa.Model;
+
+/**
+ * @author Randall Hauch
+ */
+public class StoreOptions {
+
+ public static final String ROOT_NODE_UUID =
"org.jboss.dna.store.rootNodeUuid";
+ public static final String VERSION = "org.jboss.dna.store.version";
+ public static final String MODEL = "org.jboss.dna.store.model";
+
+ private final EntityManager entityManager;
+
+ public StoreOptions( EntityManager manager ) {
+ CheckArg.isNotNull(manager, "manager");
+ this.entityManager = manager;
+ }
+
+ public UUID getRootNodeUuid() {
+ String value = getOption(ROOT_NODE_UUID);
+ return value != null ? UUID.fromString(value) : null;
+ }
+
+ public void setRootNodeUuid( UUID uuid ) {
+ CheckArg.isNotNull(uuid, "uuid");
+ setOption(ROOT_NODE_UUID, uuid.toString());
+ }
+
+ public String getVersion() {
+ return getOption(VERSION);
+ }
+
+ public void setVersion( String version ) {
+ setOption(VERSION, version);
+ }
+
+ public String getModelName() {
+ return getOption(MODEL);
+ }
+
+ public void setModelName( Model model ) {
+ String modelName = model != null ? model.getName() : null;
+ setOption(MODEL, modelName);
+ }
+
+ public String getOption( String name ) {
+ StoreOptionEntity entity = entityManager.find(StoreOptionEntity.class, name);
+ return entity != null ? entity.getValue() : null;
+ }
+
+ public void setOption( String name,
+ String value ) {
+ CheckArg.isNotEmpty(name, "name");
+ if (value != null) value = value.trim();
+ StoreOptionEntity entity = entityManager.find(StoreOptionEntity.class, name);
+ if (entity == null) {
+ if (value != null) {
+ // There is no existing entity, but there is a valid value ...
+ entity = new StoreOptionEntity(name, value);
+ entityManager.persist(entity);
+ }
+ } else {
+ if (value != null) {
+ // Set value on the entity ...
+ entity.setValue(value);
+ } else {
+ // The existing entity is to be removed ...
+ entityManager.remove(entity);
+ }
+ }
+ }
+
+ public void removeOption( String name ) {
+ StoreOptionEntity entity = new StoreOptionEntity(name);
+ entityManager.remove(entity);
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,33 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2008, Red Hat Middleware LLC, and individual contributors
+# as indicated by the @author tags. See the copyright.txt file in the
+# distribution for a full listing of individual contributors.
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of
+# the License, or (at your option) any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+#
+
+connectorName = JPA Graph Store Connector
+nodeDoesNotExist = Could not find an existing node at {0}
+propertyIsRequired = The {0} property is required but has no value
+errorFindingDataSourceInJndi = Repository source {0} unable to find DataSource in JNDI at
{1}
+repositorySourceMustHaveName = The JPA Graph Store repository source must have a name
+unknownModelName = The model name "{0}" is unknown; expected one of {1}
+errorSettingContextClassLoader = Error while setting the current context class loader for
JAP repository source {0} to classloader name {1}
+existingStoreSpecifiesUnknownModel = The JPA repository source {0} uses a model that is
not known: {1}
+unableToReadLargeValue = Unable to read from {0} the large property with hash = {1}
+
+basicModelDescription = Database model that stores node properties as opaque records and
children as transparent records. Large property values are stored separately.
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import static org.mockito.Mockito.mock;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.connector.store.jpa.models.basic.BasicModel;
+import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnectionTest {
+
+ private ExecutionContext context;
+ private JpaConnection connection;
+ private EntityManagerFactory factory;
+ private EntityManager manager;
+ private Model model;
+ private CachePolicy cachePolicy;
+ private UUID rootNodeUuid;
+ private long largeValueSize;
+
+ @Before
+ public void beforeEach() throws Exception {
+ context = new BasicExecutionContext();
+ model = new BasicModel();
+ rootNodeUuid = UUID.randomUUID();
+ largeValueSize = 2 ^ 10; // 1 kilobyte
+
+ // Connect to the database ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+ model.configure(configurator);
+ configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username",
"sa");
+ configurator.setProperty("hibernate.connection.password",
"");
+ configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
+ configurator.setProperty("hibernate.show_sql", "true");
+ configurator.setProperty("hibernate.format_sql", "true");
+ configurator.setProperty("hibernate.use_sql_comments",
"true");
+ configurator.setProperty("hibernate.hbm2ddl.auto",
"create");
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+
+ // Create the connection ...
+ cachePolicy = mock(CachePolicy.class);
+ connection = new JpaConnection("source", cachePolicy, manager, model,
rootNodeUuid, largeValueSize);
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ try {
+ if (manager != null) manager.close();
+ } finally {
+ manager = null;
+ if (factory != null) {
+ try {
+ factory.close();
+ } finally {
+ factory = null;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void shouldAlwaysReadRootNode() {
+
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import org.jboss.dna.common.AbstractI18nTest;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnectorI18nTest extends AbstractI18nTest {
+
+ public JpaConnectorI18nTest() {
+ super(JpaConnectorI18n.class);
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,111 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaSourceTest {
+
+ private JpaSource source;
+ private JpaConnection connection;
+
+ @Before
+ public void beforeEach() throws Exception {
+ this.source = new JpaSource();
+ // Set the connection properties to be an in-memory HSQL database ...
+ this.source.setName("Test Repository");
+ this.source.setDialect("org.hibernate.dialect.HSQLDialect");
+ this.source.setDriverClassName("org.hsqldb.jdbcDriver");
+ this.source.setUsername("sa");
+ this.source.setPassword("");
+ this.source.setUrl("jdbc:hsqldb:.");
+ this.source.setMaximumConnectionsInPool(3);
+ this.source.setMinimumConnectionsInPool(0);
+ this.source.setNumberOfConnectionsToAcquireAsNeeded(1);
+ this.source.setMaximumSizeOfStatementCache(100);
+ this.source.setMaximumConnectionIdleTimeInSeconds(0);
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ try {
+ if (this.connection != null) {
+ this.connection.close();
+ }
+ } finally {
+ this.source.close();
+ }
+ }
+
+ @Test( expected = RepositorySourceException.class )
+ public void shouldFailToCreateConnectionIfSourceHasNoName() {
+ source.setName(null);
+ source.getConnection();
+ }
+
+ @Test
+ public void shouldHaveNoDefaultModelUponConstruction() {
+ assertThat(source.getModel(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldCreateConnection() throws Exception {
+ connection = (JpaConnection)source.getConnection();
+ assertThat(connection, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldAllowMultipleConnectionsToBeOpenAtTheSameTime() throws Exception {
+ List<RepositoryConnection> connections = new
ArrayList<RepositoryConnection>();
+ try {
+ for (int i = 0; i != 10; ++i) {
+ RepositoryConnection conn = source.getConnection();
+ assertThat(conn, is(notNullValue()));
+ connections.add(conn);
+ }
+ } finally {
+ // Close all open connections ...
+ for (RepositoryConnection conn : connections) {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,114 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class ModelTest {
+
+ private String validName;
+ private I18n validDescription;
+ private I18n validDescription2;
+ private Model model1;
+ private Model model2;
+ private Model model3;
+ @Mock
+ private RequestProcessor requestProcessor;
+
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ validName = "Concrete Model";
+ validDescription = JpaConnectorI18n.basicModelDescription;
+ validDescription2 = JpaConnectorI18n.connectorName;
+ model1 = new ConcreteModel(validName, validDescription);
+ model2 = new ConcreteModel(validName, validDescription2);
+ model3 = new ConcreteModel(validName + " ", validDescription);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowNullNameInConstructor() {
+ new ConcreteModel(null, validDescription);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowEmptyNameInConstructor() {
+ new ConcreteModel("", validDescription);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowNullDescriptionInConstructor() {
+ new ConcreteModel(validName, null);
+ }
+
+ @Test
+ public void shouldNotTrimName() {
+ assertThat(model3.getName(), is(validName + " "));
+ }
+
+ @Test
+ public void shouldConsiderTwoModelsEqualIfTheyHaveTheSameName() {
+ assertThat(model1, is(model2));
+ }
+
+ @Test
+ public void shouldConsiderTwoModelsNotEqualIfTheyHaveDifferentNames() {
+ assertThat(model1, is(not(model3)));
+ assertThat(model2, is(not(model3)));
+ }
+
+ protected class ConcreteModel extends Model {
+ protected ConcreteModel( String name,
+ I18n description ) {
+ super(name, description);
+ }
+
+ @Override
+ public void configure( Ejb3Configuration configurator ) {
+ }
+
+ @SuppressWarnings( "synthetic-access" )
+ @Override
+ public RequestProcessor createRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes
) {
+ return requestProcessor;
+ }
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,354 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * This test not only verifies the (minimal) functionality of the {@link BasicModel}
class, but it also verifies that the entity
+ * classes used by the {@link BasicModel#configure(Ejb3Configuration) configuration} are
consistent and error-free. In other
+ * words, if there are any problems with any of the entity annotations, they will be
found when the {@link EntityManager} is
+ * {@link #startEntityManager() started}.
+ *
+ * @author Randall Hauch
+ */
+public class BasicModelTest {
+
+ private EntityManagerFactory factory;
+ private EntityManager manager;
+ private BasicModel model;
+ private ExecutionContext context;
+
+ @BeforeClass
+ public static void beforeAll() throws Exception {
+ }
+
+ @Before
+ public void beforeEach() throws Exception {
+ model = new BasicModel();
+ context = new BasicExecutionContext();
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ try {
+ if (manager != null) manager.close();
+ } finally {
+ manager = null;
+ if (factory != null) {
+ try {
+ factory.close();
+ } finally {
+ factory = null;
+ }
+ }
+ }
+ }
+
+ protected EntityManager startEntityManager() {
+ if (manager == null) {
+ // Connect to the database ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+ model.configure(configurator);
+ configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username",
"sa");
+ configurator.setProperty("hibernate.connection.password",
"");
+ configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
+ configurator.setProperty("hibernate.show_sql", "true");
+ configurator.setProperty("hibernate.format_sql",
"true");
+ configurator.setProperty("hibernate.use_sql_comments",
"true");
+ configurator.setProperty("hibernate.hbm2ddl.auto",
"create");
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+ }
+ return manager;
+ }
+
+ @Test
+ public void shouldHaveName() {
+ assertThat(model.getName(), is("Basic"));
+ }
+
+ @Test
+ public void shouldHaveDescription() {
+ assertThat(model.getDescription(),
is(JpaConnectorI18n.basicModelDescription.text()));
+ assertThat(model.getDescription(Locale.US),
is(JpaConnectorI18n.basicModelDescription.text()));
+ }
+
+ @Test
+ public void shouldCreateRequestProcessor() {
+ EntityManager manager = mock(EntityManager.class);
+ EntityTransaction txn = mock(EntityTransaction.class);
+ stub(manager.getTransaction()).toReturn(txn);
+ RequestProcessor proc = model.createRequestProcessor("test source",
context, manager, UUID.randomUUID(), 100);
+ assertThat(proc, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldPersistPropertyEntityWithCompressedFlagAndNoChildren() {
+ startEntityManager();
+ NodeId nodeId = new NodeId(UUID.randomUUID());
+ PropertiesEntity prop = new PropertiesEntity();
+ prop.setCompressed(true);
+ prop.setData("Hello, World".getBytes());
+ prop.setId(nodeId);
+ manager.getTransaction().begin();
+ try {
+ // Save a properties entity (with compressed data) ...
+ manager.persist(prop);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ PropertiesEntity prop2 = manager.find(PropertiesEntity.class, nodeId);
+ assertThat(prop2.isCompressed(), is(prop.isCompressed()));
+ assertThat(prop2.getId(), is(prop.getId()));
+ assertThat(prop2.getData(), is(prop.getData()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistPropertyEntityWithUncompressedFlagAndNoChildren() {
+ startEntityManager();
+ NodeId nodeId = new NodeId(UUID.randomUUID());
+ PropertiesEntity prop = new PropertiesEntity();
+ prop.setData("Hello, World".getBytes());
+ prop.setId(nodeId);
+ manager.getTransaction().begin();
+ try {
+ // Save a properties entity (with compressed data) ...
+ manager.persist(prop);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ PropertiesEntity prop2 = manager.find(PropertiesEntity.class, nodeId);
+ assertThat(prop2.isCompressed(), is(prop.isCompressed()));
+ assertThat(prop2.getId(), is(prop.getId()));
+ assertThat(prop2.getData(), is(prop.getData()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistLargeValueEntityWithCompressedFlag() throws
UnsupportedEncodingException, NoSuchAlgorithmException {
+ startEntityManager();
+ byte[] content = "Jack and Jill went up the hill to grab a pail of
water.".getBytes();
+ String hash =
StringUtil.getHexString(SecureHash.getHash(SecureHash.Algorithm.SHA_1, content));
+ LargeValueEntity entity = new LargeValueEntity();
+ entity.setCompressed(true);
+ entity.setHash(hash);
+ entity.setLength(content.length);
+ entity.setData(content);
+ entity.setType(PropertyType.STRING);
+ manager.getTransaction().begin();
+ try {
+ // Save the entity ...
+ manager.persist(entity);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ LargeValueEntity entity2 = manager.find(LargeValueEntity.class, hash);
+ assertThat(entity2.isCompressed(), is(entity.isCompressed()));
+ assertThat(entity2.getHash(), is(entity.getHash()));
+ assertThat(entity2.getData(), is(entity.getData()));
+ assertThat(entity2.getLength(), is(entity.getLength()));
+ assertThat(entity2.getType(), is(entity.getType()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistLargeValueEntityWithUncompressedFlag() throws
UnsupportedEncodingException, NoSuchAlgorithmException {
+ startEntityManager();
+ byte[] content = "Jack and Jill went up the hill to grab a pail of
water.".getBytes();
+ String hash =
StringUtil.getHexString(SecureHash.getHash(SecureHash.Algorithm.SHA_1, content));
+ LargeValueEntity entity = new LargeValueEntity();
+ // entity.setCompressed(false);
+ entity.setHash(hash);
+ entity.setLength(content.length);
+ entity.setData(content);
+ entity.setType(PropertyType.STRING);
+ manager.getTransaction().begin();
+ try {
+ // Save the entity ...
+ manager.persist(entity);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ LargeValueEntity entity2 = manager.find(LargeValueEntity.class, hash);
+ assertThat(entity2.isCompressed(), is(entity.isCompressed()));
+ assertThat(entity2.getHash(), is(entity.getHash()));
+ assertThat(entity2.getData(), is(entity.getData()));
+ assertThat(entity2.getLength(), is(entity.getLength()));
+ assertThat(entity2.getType(), is(entity.getType()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistNamespaceEntity() {
+ startEntityManager();
+ String uri = "http://www.example.com";
+ NamespaceEntity namespace = new NamespaceEntity(uri);
+ manager.getTransaction().begin();
+ try {
+ // Save a namespace entity ...
+ manager.persist(namespace);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns2 = manager.find(NamespaceEntity.class,
namespace.getId());
+ assertThat(ns2.getUri(), is(namespace.getUri()));
+ assertThat(ns2.getId(), is(namespace.getId()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ // Look up by namespace ...
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns2 = NamespaceEntity.findByUri(manager, uri);
+ assertThat(ns2.getUri(), is(namespace.getUri()));
+ assertThat(ns2.getId(), is(namespace.getId()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistChildEntity() {
+ startEntityManager();
+ UUID parentId = UUID.randomUUID();
+
+ // Create UUIDs for several children ...
+ ChildId childId1 = new ChildId(parentId, UUID.randomUUID());
+ ChildId childId2 = new ChildId(parentId, UUID.randomUUID());
+ ChildId childId3 = new ChildId(parentId, UUID.randomUUID());
+ assertThat(childId1, is(not(childId2)));
+ assertThat(childId1, is(not(childId3)));
+ assertThat(childId2, is(not(childId3)));
+
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns = NamespaceEntity.findByUri(manager,
"http://www.example.com");
+
+ // Create the child entities ...
+ ChildEntity child1 = new ChildEntity(childId1, 1, ns, "child1");
+ ChildEntity child2 = new ChildEntity(childId2, 2, ns, "child2");
+ ChildEntity child3 = new ChildEntity(childId3, 3, ns, "child3",
1);
+
+ // Save a properties entities ...
+ manager.persist(child1);
+ manager.persist(child2);
+ manager.persist(child3);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ // manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns = NamespaceEntity.findByUri(manager,
"http://www.example.com");
+
+ ChildEntity child1a = manager.find(ChildEntity.class, childId1);
+ ChildEntity child2a = manager.find(ChildEntity.class, childId2);
+ ChildEntity child3a = manager.find(ChildEntity.class, childId3);
+
+ assertThat(child1a.getId(), is(childId1));
+ assertThat(child1a.getIndexInParent(), is(1));
+ assertThat(child1a.getChildName(), is("child1"));
+ assertThat(child1a.getChildNamespace(), is(ns));
+ assertThat(child1a.getSameNameSiblingIndex(), is(nullValue()));
+
+ assertThat(child2a.getId(), is(childId2));
+ assertThat(child2a.getIndexInParent(), is(2));
+ assertThat(child2a.getChildName(), is("child2"));
+ assertThat(child2a.getChildNamespace(), is(ns));
+ assertThat(child2a.getSameNameSiblingIndex(), is(nullValue()));
+
+ assertThat(child3a.getId(), is(childId3));
+ assertThat(child3a.getIndexInParent(), is(3));
+ assertThat(child3a.getChildName(), is("child3"));
+ assertThat(child3a.getChildNamespace(), is(ns));
+ assertThat(child3a.getSameNameSiblingIndex(), is(1));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,378 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.PropertyFactory;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.properties.ValueFactories;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class SerializerTest {
+
+ private Serializer serializer;
+ private ExecutionContext context;
+ private LargeValuesHolder largeValues;
+ private PropertyFactory propertyFactory;
+ private ValueFactories valueFactories;
+
+ @Before
+ public void beforeEach() {
+ context = new BasicExecutionContext();
+ propertyFactory = context.getPropertyFactory();
+ valueFactories = context.getValueFactories();
+ largeValues = new LargeValuesHolder();
+ serializer = new Serializer(context, largeValues);
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeLongProperty() throws Exception {
+ Property prop = createProperty("p1", new Long(1));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeIntegerProperty() throws Exception {
+ Property prop = createProperty("p1", new Integer(1));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeShortProperty() throws Exception {
+ Property prop = createProperty("p1", new Short((short)1));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeFloatProperty() throws Exception {
+ Property prop = createProperty("p1", new Float(1.0f));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeDoubleProperty() throws Exception {
+ Property prop = createProperty("p1", new Double(1.0d));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeBooleanProperty() throws Exception {
+ Property prop = createProperty("p1", new Boolean(true));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeNameProperty() throws Exception {
+ Property prop = createProperty("p1",
valueFactories.getNameFactory().create("something"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializePathProperty() throws Exception {
+ Property prop = createProperty("p1",
valueFactories.getPathFactory().create("/a/b/c/something"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeDateTimeProperty() throws Exception {
+ Property prop = createProperty("p1",
valueFactories.getDateFactory().createUtc());
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+
+ prop = createProperty("p1", valueFactories.getDateFactory().create());
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeUuidProperty() throws Exception {
+ Property prop = createProperty("p1", UUID.randomUUID());
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeUriProperty() throws Exception {
+ Property prop = createProperty("p1", new
URI("http://example.com"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeReferenceProperty() throws Exception {
+ UUID uuid = UUID.randomUUID();
+ Property prop = createProperty("p1",
valueFactories.getReferenceFactory().create(uuid.toString()));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeBigDecimalProperty() throws Exception {
+ Property prop = createProperty("p1",
valueFactories.getDecimalFactory().create("1.0123455243284347375478525485466895512"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeSmallBinaryProperty() throws Exception {
+ String value = "v1";
+ Property prop = createProperty("p1",
valueFactories.getBinaryFactory().create(value));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeLargeBinaryProperty() throws Exception {
+ String value = "really really long string that will be converted to a binary
value and tested like that";
+ Property prop = createProperty("p1",
valueFactories.getBinaryFactory().create(value));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(1));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeSmallStringProperty() throws Exception {
+ Property prop = createProperty("p1", "v1");
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeLargeStringProperty() throws Exception {
+ String value = "v234567890123456789012345678901234567890";
+ Property prop = createProperty("p1", value);
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(1));
+ assertThat(largeValues.get(value).value, is((Object)value));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeSmallAndLargeStringProperty() throws
Exception {
+ Property prop1 = createProperty("p1", "v1");
+ String value = "v234567890123456789012345678901234567890";
+ Property prop2 = createProperty("p1", value);
+ Property prop3 = createProperty("p1", "v2");
+ Property prop4 = createProperty("p1", new String(value)); // make sure
it's a different String object
+
+ assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4);
+ assertThat(largeValues.getCount(), is(1));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeMixtureOfSmallAndLargeProperties() throws
Exception {
+ Property prop1 = createProperty("p1", "v1");
+ String value = "v234567890123456789012345678901234567890";
+ Property prop2 = createProperty("p1", value);
+ Property prop3 = createProperty("p1", "v2");
+ Property prop4 = createProperty("p1", new String(value)); // make sure
it's a different String object
+ Property prop5 = createProperty("p1",
valueFactories.getBinaryFactory().create("something"));
+ String binaryValue = "really really long string that will be converted to a
binary value and tested like that";
+ Property prop6 = createProperty("p1",
valueFactories.getBinaryFactory().create(binaryValue));
+
+ assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4,
prop5, prop6);
+ assertThat(largeValues.getCount(), is(2));
+ }
+
+ protected Property createProperty( String name,
+ Object... values ) {
+ return propertyFactory.create(valueFactories.getNameFactory().create(name),
values);
+ }
+
+ protected void assertSerializableAndDeserializable( Serializer serializer,
+ Property... properties ) throws
IOException, ClassNotFoundException {
+ for (Property property : properties) {
+ // Serialize the properties one at a time ...
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ try {
+ serializer.serializeProperty(oos, property);
+ } finally {
+ oos.close();
+ }
+ byte[] bytes = baos.toByteArray();
+
+ // Deserialize ...
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ Property copy = null;
+ try {
+ copy = serializer.deserializeProperty(ois);
+ } finally {
+ ois.close();
+ }
+ // Check the property ...
+ assertThat(copy, is(property));
+ }
+
+ // Now serialize and deserialize the list of properties ...
+ List<Property> propertyList = Arrays.asList(properties);
+ List<Property> outputProperties = new
ArrayList<Property>(propertyList.size());
+
+ // Serialize the properties one at a time ...
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ try {
+ serializer.serializeProperties(oos, propertyList.size(), propertyList);
+ } finally {
+ oos.close();
+ }
+ byte[] bytes = baos.toByteArray();
+
+ // Deserialize ...
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ try {
+ serializer.deserializeProperties(ois, outputProperties);
+ } finally {
+ ois.close();
+ }
+
+ // Check the properties match ...
+ assertThat(outputProperties.size(), is(propertyList.size()));
+ assertThat(outputProperties, hasItems(propertyList.toArray(new
Property[propertyList.size()])));
+ }
+
+ protected class LargeValuesHolder implements Serializer.LargeValues {
+ private int minimumSize = 20;
+ private final Map<String, LargeValue> largeValuesByHexHash = new
HashMap<String, LargeValue>();
+
+ public LargeValuesHolder() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return minimumSize;
+ }
+
+ /**
+ * @param minimumSize Sets minimumSize to the specified value.
+ */
+ public void setMinimumSize( int minimumSize ) {
+ CheckArg.isPositive(minimumSize, "minimumSize");
+ this.minimumSize = minimumSize;
+ }
+
+ public int getCount() {
+ return this.largeValuesByHexHash.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ LargeValue largeValue = get(hash);
+ return largeValue != null ? largeValue.value : null;
+ }
+
+ public LargeValue get( String obj ) throws IOException, NoSuchAlgorithmException
{
+ byte[] hash = SecureHash.getHash(SecureHash.Algorithm.SHA_1,
obj.getBytes());
+ return get(hash);
+ }
+
+ public LargeValue get( Binary obj ) throws IOException {
+ return get(obj.getHash());
+ }
+
+ public LargeValue get( byte[] hash ) throws IOException {
+ String hexHash = StringUtil.getHexString(hash);
+ return largeValuesByHexHash.get(hexHash);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#write(byte[], long,
+ * org.jboss.dna.graph.properties.PropertyType, java.lang.Object)
+ */
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException {
+ String hexHash = StringUtil.getHexString(hash);
+ largeValuesByHexHash.put(hexHash, new LargeValue(hash, length, type,
value));
+ }
+
+ protected class LargeValue {
+ protected final byte[] hash;
+ protected final long length;
+ protected final PropertyType type;
+ protected final Object value;
+
+ protected LargeValue( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) {
+ assert hash != null;
+ assert length > 0;
+ assert type != null;
+ assert value != null;
+ this.hash = hash;
+ this.length = length;
+ this.type = type;
+ this.value = value;
+ }
+ }
+ }
+
+}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,212 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.List;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Query;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class StoreOptionsTest {
+
+ protected static EntityManagerFactory factory;
+ protected static EntityManager manager;
+ protected StoreOptions options;
+
+ @BeforeClass
+ public static void beforeAll() throws Exception {
+ // Connect to the database ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+ configurator.addAnnotatedClass(StoreOptionEntity.class);
+ configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username",
"sa");
+ configurator.setProperty("hibernate.connection.password",
"");
+ configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
+ configurator.setProperty("hibernate.show_sql", "false");
+ configurator.setProperty("hibernate.format_sql", "true");
+ configurator.setProperty("hibernate.use_sql_comments",
"true");
+ configurator.setProperty("hibernate.hbm2ddl.auto",
"create");
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+ }
+
+ @Before
+ public void beforeEach() throws Exception {
+ removeAllOptionEntities();
+ options = new StoreOptions(manager);
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ removeAllOptionEntities();
+ }
+
+ @AfterClass
+ public static void afterAll() throws Exception {
+ try {
+ manager.close();
+ } finally {
+ factory.close();
+ }
+ }
+
+ protected void removeAllOptionEntities() {
+ try {
+ manager.getTransaction().begin();
+ List<StoreOptionEntity> optionEntities = getAllOptionEntities();
+ for (StoreOptionEntity entity : optionEntities) {
+ manager.remove(entity);
+ }
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ @SuppressWarnings( "unchecked" )
+ protected List<StoreOptionEntity> getAllOptionEntities() {
+ Query query = manager.createNamedQuery("StoreOptionEntity.findAll");
+ return query.getResultList();
+ }
+
+ protected void assertNoOptions() {
+ try {
+ manager.getTransaction().begin();
+ assertThat(getAllOptionEntities().isEmpty(), is(true));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ protected void assertOption( String name,
+ String expectedValue ) {
+ try {
+ manager.getTransaction().begin();
+ String actualValue = options.getOption(name);
+ assertThat(expectedValue, is(actualValue));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ //
+ // protected StoreOptionEntity getOption( String name ) {
+ // return options.readStoreOption(name);
+ // }
+ //
+ protected void setOptionInTxn( String name,
+ String value ) {
+ try {
+ manager.getTransaction().begin();
+ options.setOption(name, value);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ assertOption(name, value);
+ }
+
+ @Test
+ public void shouldReturnNullForNonExistantOption() {
+ assertNoOptions();
+ assertThat(options.getOption("non-existant name"), is(nullValue()));
+ }
+
+ @Test
+ public void shouldReturnValueForExistingOption() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ @Test
+ public void shouldSetValueOnExistingOption() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ options.setOption("name1", "value2");
+ assertThat(options.getOption("name1"), is("value2"));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+
+ assertOption("name1", "value2");
+ }
+
+ @Test
+ public void shouldRemoveOptionWhenSetToNullValue() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ options.setOption("name1", null);
+ assertThat(options.getOption("name1"), is(nullValue()));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ @Test
+ public void shouldNotRemoveOptionWhenSetToEmptyValue() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ options.setOption("name1", "");
+ assertThat(options.getOption("name1"), is(""));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties 2008-11-19
19:34:05 UTC (rev 641)
@@ -0,0 +1,19 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Set up the default logging to be INFO level, then override specific units
+log4j.logger.org.jboss.dna=INFO
+# Hibernate
+log4j.logger.org.hibernate=ERROR
+# C3P0
+log4j.logger.com.mchange=ERROR
+
+# JBoss Cache logging
+log4j.logger.org.jboss.cache=WARN, stdout
+
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2008-11-18 13:00:30 UTC (rev 640)
+++ trunk/pom.xml 2008-11-19 19:34:05 UTC (rev 641)
@@ -129,6 +129,7 @@
<module>extensions/dna-connector-inmemory</module>
<module>extensions/dna-connector-jbosscache</module>
<module>extensions/dna-connector-svn</module>
+ <module>extensions/dna-connector-store-jpa</module>
<module>extensions/dna-mimetype-detector-aperture</module>
<module>dna-integration-tests</module>
<module>docs/examples/gettingstarted</module>