Index: dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java
===================================================================
--- dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java (revision 1382)
+++ dna-graph/src/main/java/org/jboss/dna/graph/connector/map/MapRequestProcessor.java (working copy)
@@ -456,6 +456,13 @@
String nameOfWorkspaceToBeCloned = request.nameOfWorkspaceToBeCloned();
MapWorkspace original = repository.getWorkspace(nameOfWorkspaceToBeCloned);
MapWorkspace target = repository.getWorkspace(targetWorkspaceName);
+
+ if (target != null) {
+ String msg = GraphI18n.workspaceAlreadyExistsInRepository.text(targetWorkspaceName, repository.getSourceName());
+ request.setError(new InvalidWorkspaceException(msg));
+ return;
+ }
+
if (original == null) {
switch (request.cloneConflictBehavior()) {
case DO_NOT_CLONE:
@@ -465,14 +472,11 @@
return;
case SKIP_CLONE:
target = repository.createWorkspace(context, targetWorkspaceName, request.targetConflictBehavior());
- if (target == null) {
- msg = GraphI18n.workspaceAlreadyExistsInRepository.text(targetWorkspaceName, repository.getSourceName());
- request.setError(new InvalidWorkspaceException(msg));
- } else {
- MapNode root = target.getRoot();
- request.setActualRootLocation(Location.create(pathFactory.createRootPath(), root.getUuid()));
- request.setActualWorkspaceName(target.getName());
- }
+ assert target != null;
+
+ MapNode root = target.getRoot();
+ request.setActualRootLocation(Location.create(pathFactory.createRootPath(), root.getUuid()));
+ request.setActualWorkspaceName(target.getName());
return;
}
}
@@ -481,17 +485,11 @@
targetWorkspaceName,
request.targetConflictBehavior(),
nameOfWorkspaceToBeCloned);
- if (target == null) {
- // Since the original was there, the only reason the target wasn't created was because the workspace already existed
- // ...
- String msg = GraphI18n.workspaceAlreadyExistsInRepository.text(targetWorkspaceName, repository.getSourceName());
- request.setError(new InvalidWorkspaceException(msg));
- } else {
- MapNode root = target.getRoot();
- request.setActualRootLocation(Location.create(pathFactory.createRootPath(), root.getUuid()));
- request.setActualWorkspaceName(target.getName());
- recordChange(request);
- }
+ assert target != null;
+ MapNode root = target.getRoot();
+ request.setActualRootLocation(Location.create(pathFactory.createRootPath(), root.getUuid()));
+ request.setActualWorkspaceName(target.getName());
+ recordChange(request);
}
/**
Index: dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
--- dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java (revision 1382)
+++ dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java (working copy)
@@ -967,16 +967,24 @@
* @author Randall Hauch
*/
@Immutable
- protected static class LocationWithDepth {
+ protected class LocationWithDepth {
protected final Location location;
protected final int depth;
- protected LocationWithDepth( Location location,
+ public LocationWithDepth( Location location,
int depth ) {
this.location = location;
this.depth = depth;
}
+ public Location getLocation() {
+ return location;
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
@Override
public int hashCode() {
return location.hashCode();
Index: extensions/dna-connector-store-jpa/pom.xml
===================================================================
--- extensions/dna-connector-store-jpa/pom.xml (revision 1382)
+++ extensions/dna-connector-store-jpa/pom.xml (working copy)
@@ -45,7 +45,13 @@
hsqldb
hsqldb
1.8.0.2
+ test
+
+ com.google.code.google-collections
+ google-collect
+ snapshot-20080530
+
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java (working copy)
@@ -55,6 +55,7 @@
public static I18n connectionIsNoLongerOpen;
public static I18n basicModelDescription;
+ public static I18n simpleModelDescription;
static {
try {
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java (working copy)
@@ -50,6 +50,7 @@
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.connector.store.jpa.model.basic.BasicModel;
+import org.jboss.dna.connector.store.jpa.model.simple.SimpleModel;
import org.jboss.dna.connector.store.jpa.util.StoreOptionEntity;
import org.jboss.dna.connector.store.jpa.util.StoreOptions;
import org.jboss.dna.graph.ExecutionContext;
@@ -74,8 +75,8 @@
*/
public static class Models {
public static final Model BASIC = new BasicModel();
- // public static final Model SIMPLE = new SimpleModel();
- private static final Model[] ALL_ARRAY = new Model[] {BASIC /*, SIMPLE */};
+ public static final Model SIMPLE = new SimpleModel();
+ private static final Model[] ALL_ARRAY = new Model[] {BASIC, SIMPLE};
private static final List MODIFIABLE_MODELS = new ArrayList(Arrays.asList(ALL_ARRAY));
public static final Collection ALL = Collections.unmodifiableCollection(MODIFIABLE_MODELS);
public static final Model DEFAULT = BASIC;
@@ -169,7 +170,7 @@
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
+ private static final int DEFAULT_LARGE_VALUE_SIZE_IN_BYTES = 1 << 10; // 1 kilobyte
private static final boolean DEFAULT_COMPRESS_DATA = true;
private static final boolean DEFAULT_ENFORCE_REFERENTIAL_INTEGRITY = true;
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/BasicJpaConnection.java (working copy)
@@ -38,7 +38,7 @@
import org.jboss.dna.graph.request.processor.RequestProcessor;
/**
- * The repository connection to JPA repository sources.
+ * The repository connection to JPA repository sources that use the {@link BasicModel basic model}.
*/
class BasicJpaConnection implements RepositoryConnection {
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/SubgraphQuery.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/SubgraphQuery.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/basic/SubgraphQuery.java (working copy)
@@ -71,6 +71,7 @@
assert subgraphRootUuid != null;
assert workspaceId != null;
assert maxDepth >= 0;
+
if (maxDepth == 0) maxDepth = Integer.MAX_VALUE;
final String subgraphRootUuidString = subgraphRootUuid.toString();
// Create a new subgraph query, and add a child for the root ...
@@ -110,6 +111,7 @@
}
throw t;
}
+
return new SubgraphQuery(context, entities, workspaceId, query, subgraphRootPath, maxDepth);
}
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/LargeValueEntity.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/LargeValueEntity.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/LargeValueEntity.java (revision 0)
@@ -0,0 +1,221 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.NamedQuery;
+import javax.persistence.Query;
+import javax.persistence.Table;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.property.PropertyType;
+
+/**
+ * A single property value that is too large to be stored on the individual node, and which will be shared among all properties
+ * that have the same value. Note that the large values are stored independently of workspace, so one large value may be shared by
+ * properties of nodes in different workspaces.
+ */
+@Entity
+@Table( name = "DNA_SIMPLE_LARGE_VALUES" )
+@NamedQuery( name = "LargeValueEntity.deleteUnused", query = "delete LargeValueEntity value where value.hash not in (select values.hash from NodeEntity node join node.largeValues values)" )
+public class LargeValueEntity {
+
+ @Id
+ @Column( name = "SHA1", nullable = false, 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;
+
+ /**
+ * 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;
+
+ public String getHash() {
+ return hash;
+ }
+
+ 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 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 hash.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.getHash() + ",compressed=" + isCompressed() + ")";
+ }
+
+ /**
+ * Delete all unused large value entities.
+ *
+ * @param manager the manager; never null
+ * @return the number of deleted large values
+ */
+ public static int deleteUnused( EntityManager manager ) {
+ assert manager != null;
+ Query delete = manager.createNamedQuery("LargeValueEntity.deleteUnused");
+ int result = delete.executeUpdate();
+ manager.flush();
+ return result;
+ }
+
+ private static byte[] computeHash( byte[] value ) {
+ try {
+ return SecureHash.getHash(SecureHash.Algorithm.SHA_1, value);
+ } catch (NoSuchAlgorithmException e) {
+ throw new SystemFailureException(e);
+ }
+ }
+
+ public static LargeValueEntity create( byte[] data,
+ PropertyType type,
+ boolean compressed ) {
+ try {
+ String hashStr = StringUtil.getHexString(computeHash(data));
+ LargeValueEntity entity = new LargeValueEntity();
+
+ entity.setData(data);
+ entity.setType(type);
+ entity.setCompressed(compressed);
+ entity.setHash(hashStr);
+ return entity;
+ } catch (UnsupportedEncodingException uee) {
+ throw new IllegalStateException(uee);
+ }
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\LargeValueEntity.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/NodeEntity.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/NodeEntity.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/NodeEntity.java (revision 0)
@@ -0,0 +1,530 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.Lob;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OrderBy;
+import javax.persistence.Query;
+import javax.persistence.Table;
+import org.hibernate.annotations.Index;
+import org.jboss.dna.common.text.Inflector;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.connector.store.jpa.model.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.util.Serializer;
+
+/**
+ * An entity a node and its properties. 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-sibling indexInParent (if
+ * there is one).
+ */
+@Entity
+@org.hibernate.annotations.Table( appliesTo = "DNA_SIMPLE_NODE", indexes = {
+ @Index( name = "NODEUUID_INX", columnNames = {"WORKSPACE_ID", "NODE_UUID"} ),
+ @Index( name = "CHILDINDEX_INX", columnNames = {"WORKSPACE_ID", "PARENT_ID", "CHILD_INDEX"} ),
+ @Index( name = "CHILDNAME_INX", columnNames = {"WORKSPACE_ID", "PARENT_ID", "CHILD_NAME_NS_ID", "CHILD_NAME_LOCAL",
+ "SNS_INDEX"} )} )
+@Table( name = "DNA_SIMPLE_NODE" )
+@NamedQueries( {
+ @NamedQuery( name = "NodeEntity.findByNodeUuid", query = "from NodeEntity as node where node.workspaceId = :workspaceId and node.nodeUuidString = :nodeUuidString" ),
+ @NamedQuery( name = "NodeEntity.findInWorkspace", query = "from NodeEntity as node where node.workspaceId = :workspaceId" ),
+ @NamedQuery( name = "NodeEntity.deleteAllInWorkspace", query = "delete from NodeEntity where workspaceId = :workspaceId" ),
+ @NamedQuery( name = "NodeEntity.withLargeValues", query = "from NodeEntity as node where node.workspaceId = :workspaceId and size(node.largeValues) > 0" )} )
+public class NodeEntity {
+
+ @Id
+ @GeneratedValue( strategy = GenerationType.AUTO )
+ @Column( name = "ID" )
+ private long id;
+
+ @Column( name = "WORKSPACE_ID", nullable = false )
+ private long workspaceId;
+
+ @ManyToOne( fetch = FetchType.LAZY )
+ @JoinColumn( name = "PARENT_ID", referencedColumnName = "id", nullable = true )
+ private NodeEntity parent;
+
+ @Column( name = "NODE_UUID", nullable = false, length = 36 )
+ private String nodeUuidString;
+
+ /** The zero-based index */
+ @Column( name = "CHILD_INDEX", nullable = false, unique = false )
+ private int indexInParent = 0;
+
+ @ManyToOne( fetch = FetchType.LAZY )
+ @JoinColumn( name = "CHILD_NAME_NS_ID", nullable = true )
+ private NamespaceEntity childNamespace;
+
+ @Column( name = "CHILD_NAME_LOCAL", nullable = true, unique = false, length = 512 )
+ private String childName;
+
+ @Column( name = "SNS_INDEX", nullable = false, unique = false )
+ private int sameNameSiblingIndex = 1;
+
+ @OneToMany( fetch = FetchType.LAZY, mappedBy = "parent" )
+ @OrderBy( "indexInParent" )
+ private final List children = new ArrayList();
+ /**
+ * Tracks whether this node allows or disallows its children to have the same names (to be same-name-siblings). The model uses
+ * this to know whether it can optimization the database operations when creating, inserting, or removing children.
+ */
+ @Column( name = "ALLOWS_SNS", nullable = false, unique = false )
+ private boolean allowsSameNameChildren;
+
+ @Lob
+ @Column( name = "DATA", nullable = true, 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;
+
+ /**
+ * Flag specifying whether this node should be included in referential integrity enforcement.
+ */
+ @Column( name = "ENFORCEREFINTEG", nullable = false )
+ private boolean referentialIntegrityEnforced = true;
+
+ // @org.hibernate.annotations.CollectionOfElements( fetch = FetchType.LAZY )
+ // @JoinTable( name = "DNA_LARGEVALUE_USAGES", joinColumns = {@JoinColumn( name = "WORKSPACE_ID" ),
+ // @JoinColumn( name = "NODE_UUID" )} )
+ @ManyToMany
+ @JoinTable( name = "DNA_LARGEVALUE_USAGES", joinColumns = {@JoinColumn( name = "ID" )} )
+ private final Collection largeValues = new HashSet();
+
+ public NodeEntity() {
+ }
+
+ public NodeEntity( long id,
+ NodeEntity parent,
+ String nodeUuidString,
+ long workspaceId,
+ int indexInParent,
+ NamespaceEntity ns,
+ String name ) {
+ this.id = id;
+ this.parent = parent;
+ this.nodeUuidString = nodeUuidString;
+ this.workspaceId = workspaceId;
+ this.indexInParent = indexInParent;
+ this.childNamespace = ns;
+ this.childName = name;
+ this.sameNameSiblingIndex = 1;
+ }
+
+ public NodeEntity( long id,
+ NodeEntity parent,
+ String nodeUuidString,
+ long workspaceId,
+ int indexInParent,
+ NamespaceEntity ns,
+ String name,
+ int sameNameSiblingIndex ) {
+ this.id = id;
+ this.parent = parent;
+ this.nodeUuidString = nodeUuidString;
+ this.workspaceId = workspaceId;
+ this.indexInParent = indexInParent;
+ this.childNamespace = ns;
+ this.childName = name;
+ this.sameNameSiblingIndex = sameNameSiblingIndex;
+ }
+
+ /**
+ * Returns this node's unique identifier
+ *
+ * @return this node's unique identifier
+ */
+ public long getNodeId() {
+ return id;
+ }
+
+ /**
+ * @param id Sets this node's unique identifier
+ */
+ public void setNodeId( long id ) {
+ this.id = id;
+ }
+
+ /**
+ * Returns the parent identifier
+ *
+ * @return the parent identifier
+ */
+ public NodeEntity getParent() {
+ return parent;
+ }
+
+ /**
+ * Sets the parent identifier
+ *
+ * @param parent the parent identifier
+ */
+ public void setParent( NodeEntity parent ) {
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the node UUID string
+ *
+ * @return the node UUID string
+ */
+ public String getNodeUuidString() {
+ return nodeUuidString;
+ }
+
+ /**
+ * Sets the node UUID string
+ *
+ * @param nodeUuidString the node UUID string
+ */
+ public void setNodeUuidString( String nodeUuidString ) {
+ this.nodeUuidString = nodeUuidString;
+ }
+
+ /**
+ * Returns the identifier of the workspace containing this node
+ *
+ * @return the identifier of the workspace containing this node
+ */
+ public long getWorkspaceId() {
+ return workspaceId;
+ }
+
+ /**
+ * Sets the identifier of the workspace containing this node
+ *
+ * @param workspaceId the identifier of the workspace containing this node
+ */
+ public void setWorkspaceId( long workspaceId ) {
+ this.workspaceId = workspaceId;
+ }
+
+ /**
+ * Get the zero-based index of this child within the parent's list of children
+ *
+ * @return the zero-based index of this child
+ */
+ public int getIndexInParent() {
+ return indexInParent;
+ }
+
+ /**
+ * @param index Sets indexInParent to the specified value.
+ */
+ public void setIndexInParent( int 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 int getSameNameSiblingIndex() {
+ return sameNameSiblingIndex;
+ }
+
+ /**
+ * @param sameNameSiblingIndex Sets sameNameSiblingIndex to the specified value.
+ */
+ public void setSameNameSiblingIndex( int sameNameSiblingIndex ) {
+ this.sameNameSiblingIndex = sameNameSiblingIndex;
+ }
+
+ /**
+ * @return allowsSameNameChildren
+ */
+ public boolean getAllowsSameNameChildren() {
+ return allowsSameNameChildren;
+ }
+
+ /**
+ * @param allowsSameNameChildren Sets allowsSameNameChildren to the specified value.
+ */
+ public void setAllowsSameNameChildren( boolean allowsSameNameChildren ) {
+ this.allowsSameNameChildren = allowsSameNameChildren;
+ }
+
+ /**
+ * 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);
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void addChild( NodeEntity child ) {
+ children.add(child);
+ child.setIndexInParent(children.size() - 1);
+ }
+
+ public void addChild( int index,
+ NodeEntity child ) {
+ for (NodeEntity existing : children.subList(index, children.size() - 1)) {
+ existing.setIndexInParent(existing.getIndexInParent() + 1);
+ }
+
+ children.add(index, child);
+ child.setIndexInParent(index);
+ }
+
+ public boolean removeChild( int index ) {
+ NodeEntity removedNode = children.remove(index);
+ if (removedNode == null) return false;
+ removedNode.setParent(null);
+
+ if (index < children.size()) {
+ for (NodeEntity child : children.subList(index, children.size() - 1)) {
+ child.setIndexInParent(child.getIndexInParent() - 1);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return largeValues
+ */
+ public Collection getLargeValues() {
+ return largeValues;
+ }
+
+ /**
+ * @return referentialIntegrityEnforced
+ */
+ public boolean isReferentialIntegrityEnforced() {
+ return referentialIntegrityEnforced;
+ }
+
+ /**
+ * @param referentialIntegrityEnforced Sets referentialIntegrityEnforced to the specified value.
+ */
+ public void setReferentialIntegrityEnforced( boolean referentialIntegrityEnforced ) {
+ this.referentialIntegrityEnforced = referentialIntegrityEnforced;
+ }
+
+ /**
+ * {@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 NodeEntity) {
+ NodeEntity that = (NodeEntity)obj;
+ if (this.id != 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 > 1) {
+ sb.append('[').append(sameNameSiblingIndex).append(']');
+ }
+ sb.append(" (id=").append(getNodeUuidString()).append(")");
+ if (parent != null) {
+ sb.append(" is ");
+ sb.append(Inflector.getInstance().ordinalize(indexInParent));
+ sb.append(" child of ");
+ sb.append(parent.getNodeId());
+ sb.append(" in workspace ");
+ sb.append(getWorkspaceId());
+ } else {
+ sb.append(" is root in workspace ");
+ sb.append(getWorkspaceId());
+ }
+ return sb.toString();
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public static void adjustSnsIndexesAndIndexesAfterRemoving( EntityManager entities,
+ Long workspaceId,
+ String uuidParent,
+ String childName,
+ long childNamespaceIndex,
+ int childIndex ) {
+ // Decrement the 'indexInParent' index values for all nodes above the previously removed sibling ...
+ Query query = entities.createNamedQuery("NodeEntity.findChildrenAfterIndexUnderParent");
+ query.setParameter("workspaceId", workspaceId);
+ query.setParameter("parentUuidString", uuidParent);
+ query.setParameter("afterIndex", childIndex);
+ for (NodeEntity entity : (List)query.getResultList()) {
+ // Decrement the index in parent ...
+ entity.setIndexInParent(entity.getIndexInParent() - 1);
+ if (entity.getChildName().equals(childName) && entity.getChildNamespace().getId() == childNamespaceIndex) {
+ // The name matches, so decrement the SNS index ...
+ entity.setSameNameSiblingIndex(entity.getSameNameSiblingIndex() - 1);
+ }
+ }
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public static int adjustSnsIndexesAndIndexes( EntityManager entities,
+ Long workspaceId,
+ String uuidParent,
+ int afterIndex,
+ int untilIndex,
+ long childNamespaceIndex,
+ String childName,
+ int modifier ) {
+ int snsCount = 0;
+
+ // Decrement the 'indexInParent' index values for all nodes above the previously removed sibling ...
+ Query query = entities.createNamedQuery("NodeEntity.findChildrenAfterIndexUnderParent");
+ query.setParameter("workspaceId", workspaceId);
+ query.setParameter("parentUuidString", uuidParent);
+ query.setParameter("afterIndex", afterIndex);
+
+ int index = afterIndex;
+ for (NodeEntity entity : (List)query.getResultList()) {
+ if (++index > untilIndex) {
+ break;
+ }
+
+ // Decrement the index in parent ...
+ entity.setIndexInParent(entity.getIndexInParent() + modifier);
+ if (entity.getChildName().equals(childName) && entity.getChildNamespace().getId() == childNamespaceIndex) {
+ // The name matches, so decrement the SNS index ...
+ entity.setSameNameSiblingIndex(entity.getSameNameSiblingIndex() + modifier);
+ snsCount++;
+ }
+ }
+
+ return snsCount;
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\NodeEntity.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/package-info.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/package-info.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/package-info.java (revision 0)
@@ -0,0 +1,29 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.
+ */
+/**
+ * The classes that define the "basic" storage model for the JPA connector.
+ */
+
+package org.jboss.dna.connector.store.jpa.model.simple;
+
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\package-info.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/ReferenceEntity.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/ReferenceEntity.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/ReferenceEntity.java (revision 0)
@@ -0,0 +1,211 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.util.Collection;
+import java.util.List;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import javax.persistence.Table;
+import org.hibernate.annotations.Index;
+
+/**
+ * A record of a reference from one node to another.
+ */
+@Entity
+@Table( name = "DNA_SIMPLE_REFERENCES" )
+@org.hibernate.annotations.Table( appliesTo = "DNA_SIMPLE_REFERENCES", indexes = {
+ @Index( name = "REFINDEX_INX", columnNames = {"WORKSPACE_ID", "FROM_UUID", "TO_UUID"} ),
+ @Index( name = "REFTOUUID_INX", columnNames = {"WORKSPACE_ID", "TO_UUID"} )} )
+@NamedQueries( {
+ @NamedQuery( name = "ReferenceEntity.removeReferencesFrom", query = "delete ReferenceEntity where id.workspaceId = :workspaceId and id.fromUuidString = :fromUuid" ),
+ @NamedQuery( name = "ReferenceEntity.removeNonEnforcedReferences", query = "delete ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.fromUuidString not in ( select props.id.uuidString from PropertiesEntity props where props.referentialIntegrityEnforced = true and props.id.workspaceId = :workspaceId )" ),
+ @NamedQuery( name = "ReferenceEntity.countUnresolveReferences", query = "select count(*) from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.toUuidString not in ( select props.id.uuidString from PropertiesEntity props where props.referentialIntegrityEnforced = true and props.id.workspaceId = :workspaceId )" ),
+ @NamedQuery( name = "ReferenceEntity.getUnresolveReferences", query = "select ref from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.toUuidString not in ( select props.id.uuidString from PropertiesEntity props where props.referentialIntegrityEnforced = true and props.id.workspaceId = :workspaceId )" ),
+ @NamedQuery( name = "ReferenceEntity.findInWorkspace", query = "select ref from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId" ),
+ @NamedQuery( name = "ReferenceEntity.getInwardReferencesForList", query = "select ref from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.toUuidString in (:toUuidList)" )} )
+public class ReferenceEntity {
+
+ @Id
+ private ReferenceId id;
+
+ /**
+ *
+ */
+ public ReferenceEntity() {
+ }
+
+ /**
+ * @param id the id
+ */
+ public ReferenceEntity( ReferenceId id ) {
+ this.id = id;
+ }
+
+ /**
+ * @return id
+ */
+ public ReferenceId getId() {
+ return id;
+ }
+
+ /**
+ * @param id Sets id to the specified value.
+ */
+ public void setId( ReferenceId id ) {
+ this.id = id;
+ }
+
+ /**
+ * {@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 ReferenceEntity) {
+ ReferenceEntity that = (ReferenceEntity)obj;
+ if (this.getId().equals(that.getId())) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.id.toString();
+ }
+
+ /**
+ * Delete all references that start from the node with the supplied UUID.
+ *
+ * @param workspaceId the ID of the workspace; may not be null
+ * @param uuid the UUID of the node from which the references start
+ * @param manager the manager; may not be null
+ * @return the number of deleted references
+ */
+ public static int deleteReferencesFrom( Long workspaceId,
+ String uuid,
+ EntityManager manager ) {
+ assert manager != null;
+ Query delete = manager.createNamedQuery("ReferenceEntity.removeReferencesFrom");
+ delete.setParameter("fromUuid", uuid);
+ delete.setParameter("workspaceId", workspaceId);
+ int result = delete.executeUpdate();
+ manager.flush();
+ return result;
+ }
+
+ /**
+ * Delete all references (in all workspaces) that start from nodes that do not require enforced referential integrity.
+ *
+ * @param workspaceId the ID of the workspace; may not be null
+ * @param manager the manager; may not be null
+ * @return the number of deleted references
+ */
+ public static int deleteUnenforcedReferences( Long workspaceId,
+ EntityManager manager ) {
+ assert manager != null;
+ Query delete = manager.createNamedQuery("ReferenceEntity.removeNonEnforcedReferences");
+ delete.setParameter("workspaceId", workspaceId);
+ int result = delete.executeUpdate();
+ manager.flush();
+ return result;
+ }
+
+ /**
+ * Delete all references that start from nodes that do not support enforced referential integrity.
+ *
+ * @param workspaceId the ID of the workspace; may not be null
+ * @param manager the manager; may not be null
+ * @return the number of deleted references
+ */
+ public static int countAllReferencesResolved( Long workspaceId,
+ EntityManager manager ) {
+ assert manager != null;
+ Query query = manager.createNamedQuery("ReferenceEntity.getUnresolveReferences");
+ query.setParameter("workspaceId", workspaceId);
+ try {
+ return (Integer)query.getSingleResult();
+ } catch (NoResultException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Delete all references that start from nodes that do not support enforced referential integrity.
+ *
+ * @param workspaceId the ID of the workspace; may not be null
+ * @param manager the manager; may not be null
+ * @return the number of deleted references
+ */
+ @SuppressWarnings( "unchecked" )
+ public static List verifyAllReferencesResolved( Long workspaceId,
+ EntityManager manager ) {
+ assert manager != null;
+ Query query = manager.createNamedQuery("ReferenceEntity.getUnresolveReferences");
+ query.setParameter("workspaceId", workspaceId);
+ return query.getResultList();
+ }
+
+ /**
+ * Returns a list of all references to UUIDs in the given list within the given workspace
+ *
+ * @param workspaceId the ID of the workspace; may not be null
+ * @param uuids the UUIDs (as strings) of the nodes to check; may not be null
+ * @param manager the manager; may not be null
+ * @return the number of deleted references
+ */
+ @SuppressWarnings( "unchecked" )
+ public static List getReferencesToUuids( Long workspaceId,
+ Collection uuids,
+ EntityManager manager ) {
+ assert manager != null;
+
+ Query query = manager.createNamedQuery("ReferenceEntity.getInwardReferencesForList");
+ query.setParameter("workspaceId", workspaceId);
+ query.setParameter("toUuidList", uuids);
+ return query.getResultList();
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\ReferenceEntity.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/ReferenceId.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/ReferenceId.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/ReferenceId.java (revision 0)
@@ -0,0 +1,137 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.io.Serializable;
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+
+/**
+ * An identifier for a reference, comprised of a workspace ID, a single UUID (in string form) of the node containing the
+ * reference, and a single UUID (in string form) of the node being referenced.
+ */
+@Embeddable
+@Immutable
+@org.hibernate.annotations.Immutable
+public class ReferenceId implements Serializable {
+
+ /**
+ * Version {@value}
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Column( name = "WORKSPACE_ID", nullable = false )
+ private Long workspaceId;
+
+ @Column( name = "FROM_UUID", nullable = false, updatable = false, length = 36 )
+ private String fromUuidString;
+
+ @Column( name = "TO_UUID", nullable = false, updatable = false, length = 36 )
+ private String toUuidString;
+
+ public ReferenceId() {
+ }
+
+ public ReferenceId( Long workspaceId,
+ String fromUuid,
+ String toUuid ) {
+ this.workspaceId = workspaceId;
+ this.fromUuidString = fromUuid;
+ this.toUuidString = toUuid;
+ }
+
+ /**
+ * @return fromUuidString
+ */
+ public String getFromUuidString() {
+ return fromUuidString;
+ }
+
+ /**
+ * @return toUuidString
+ */
+ public String getToUuidString() {
+ return toUuidString;
+ }
+
+ /**
+ * @return workspaceId
+ */
+ public Long getWorkspaceId() {
+ return workspaceId;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(fromUuidString, toUuidString);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ReferenceId) {
+ ReferenceId that = (ReferenceId)obj;
+ if (this.workspaceId == null) {
+ if (that.workspaceId != null) return false;
+ } else {
+ if (!this.workspaceId.equals(that.workspaceId)) return false;
+ }
+ if (this.fromUuidString == null) {
+ if (that.fromUuidString != null) return false;
+ } else {
+ if (!this.fromUuidString.equals(that.fromUuidString)) return false;
+ }
+ if (this.toUuidString == null) {
+ if (that.toUuidString != null) return false;
+ } else {
+ if (!this.toUuidString.equals(that.toUuidString)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Reference from " + fromUuidString + " to " + toUuidString + " in workspace " + workspaceId;
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\ReferenceId.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnection.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnection.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnection.java (revision 0)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.util.concurrent.TimeUnit;
+import javax.persistence.EntityManager;
+import javax.transaction.xa.XAResource;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.observe.Observer;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * The repository connection to JPA repository sources that use the {@link SimpleModel simple model}.
+ */
+@NotThreadSafe
+public class SimpleJpaConnection implements RepositoryConnection {
+
+ private final SimpleJpaRepository repository;
+ private final JpaSource source;
+ private EntityManager entityManager;
+
+ public SimpleJpaConnection( JpaSource source ) {
+ this.source = source;
+
+ this.entityManager = source.getEntityManagers().checkout();
+ this.entityManager.getTransaction().begin();
+ this.repository = new SimpleJpaRepository(source.getName(), source.getRootUuid(), source.getDefaultWorkspaceName(),
+ source.getPredefinedWorkspaceNames(), entityManager,
+ source.getRepositoryContext().getExecutionContext(), source.isCompressData(),
+ source.isCreatingWorkspacesAllowed(), source.isReferentialIntegrityEnforced(),
+ source.getLargeValueSizeInBytes());
+ }
+
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return entityManager != null && entityManager.isOpen();
+ }
+
+ public CachePolicy getDefaultCachePolicy() {
+ return source.getCachePolicy();
+ }
+
+ public String getSourceName() {
+ return source.getName();
+ }
+
+ @Override
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ public void close() {
+ if (entityManager != null) {
+ try {
+ source.getEntityManagers().checkin(entityManager);
+ } finally {
+ entityManager = null;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.request.Request)
+ */
+ public void execute( ExecutionContext context,
+ Request request ) throws RepositorySourceException {
+ Logger logger = context.getLogger(getClass());
+ Stopwatch sw = null;
+ if (logger.isTraceEnabled()) {
+ sw = new Stopwatch();
+ sw.start();
+ }
+ // Do any commands update/write?
+ Observer observer = this.source.getRepositoryContext().getObserver();
+ RequestProcessor processor = new SimpleRequestProcessor(context, this.repository, observer);
+
+ try {
+ // Obtain the lock and execute the commands ...
+ processor.process(request);
+ } finally {
+ processor.close();
+ }
+ if (logger.isTraceEnabled()) {
+ assert sw != null;
+ sw.stop();
+ logger.trace("MapRepositoryConnection.execute(...) took " + sw.getTotalDuration());
+ }
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleJpaConnection.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaRepository.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaRepository.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaRepository.java (revision 0)
@@ -0,0 +1,825 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.util.IoUtil;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.model.common.WorkspaceEntity;
+import org.jboss.dna.connector.store.jpa.util.Namespaces;
+import org.jboss.dna.connector.store.jpa.util.Serializer;
+import org.jboss.dna.connector.store.jpa.util.Workspaces;
+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.connector.LockFailedException;
+import org.jboss.dna.graph.connector.map.AbstractMapWorkspace;
+import org.jboss.dna.graph.connector.map.MapNode;
+import org.jboss.dna.graph.connector.map.MapRepository;
+import org.jboss.dna.graph.connector.map.MapWorkspace;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.PropertyType;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
+
+/**
+ * Implementation of {@link MapRepository} for the {@link SimpleModel Simple JPA connector model}. This class exposes a map of
+ * workspace names to {@link Workspace workspaces} and each workspace provides a logical mapping of node UUIDs to {@link JpaNode
+ * nodes}. The {@code JpaNode} class functions as an adapter between the {@link NodeEntity persistent entity for nodes} and the
+ * {@link MapNode map repository interface for nodes}.
+ *
+ * This class differs slightly from the other {@link MapRepository} implementations in that it exists only within the lifetime of
+ * a single {@link EntityManager} (which itself is opened and closed within the lifetime of a single {@link SimpleJpaConnection}.
+ * The other map repository implementations all outlive any particular connection and generally survive for the lifetime of the
+ * DNA server.
+ *
+ */
+public class SimpleJpaRepository extends MapRepository {
+
+ private final EntityManager entityManager;
+ private final Workspaces workspaceEntities;
+ private final Namespaces namespaceEntities;
+ private final ExecutionContext context;
+ private final PathFactory pathFactory;
+ private final NameFactory nameFactory;
+ private final List predefinedWorkspaceNames;
+ private final boolean compressData;
+ private final boolean creatingWorkspacesAllowed;
+ private final long minimumSizeOfLargeValuesInBytes;
+
+ // private final boolean referentialIntegrityEnforced;
+
+ public SimpleJpaRepository( String sourceName,
+ UUID rootNodeUuid,
+ String defaultWorkspaceName,
+ String[] predefinedWorkspaceNames,
+ EntityManager entityManager,
+ ExecutionContext context,
+ boolean compressData,
+ boolean creatingWorkspacesAllowed,
+ boolean referentialIntegrityEnforced,
+ long minimumSizeOfLargeValuesInBytes ) {
+ super(sourceName, rootNodeUuid, defaultWorkspaceName);
+
+ this.context = context;
+ ValueFactories valueFactories = context.getValueFactories();
+ this.nameFactory = valueFactories.getNameFactory();
+ this.pathFactory = valueFactories.getPathFactory();
+ this.predefinedWorkspaceNames = Arrays.asList(predefinedWorkspaceNames);
+ this.compressData = compressData;
+ this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
+ // this.referentialIntegrityEnforced = referentialIntegrityEnforced;
+ this.minimumSizeOfLargeValuesInBytes = minimumSizeOfLargeValuesInBytes;
+
+ this.entityManager = entityManager;
+ workspaceEntities = new Workspaces(entityManager);
+ namespaceEntities = new Namespaces(entityManager);
+ super.initialize();
+ }
+
+ public SimpleJpaRepository( String sourceName,
+ UUID rootNodeUuid,
+ EntityManager entityManager,
+ ExecutionContext context,
+ boolean compressData,
+ boolean creatingWorkspacesAllowed,
+ boolean referentialIntegrityEnforced,
+ long minimumSizeOfLargeValuesInBytes ) {
+ super(sourceName, rootNodeUuid);
+
+ this.context = context;
+ ValueFactories valueFactories = context.getValueFactories();
+ this.nameFactory = valueFactories.getNameFactory();
+ this.pathFactory = valueFactories.getPathFactory();
+ this.predefinedWorkspaceNames = Collections.emptyList();
+ this.compressData = compressData;
+ this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
+ // this.referentialIntegrityEnforced = referentialIntegrityEnforced;
+ this.minimumSizeOfLargeValuesInBytes = minimumSizeOfLargeValuesInBytes;
+
+ this.entityManager = entityManager;
+ workspaceEntities = new Workspaces(entityManager);
+ namespaceEntities = new Namespaces(entityManager);
+ super.initialize();
+ }
+
+ final EntityManager entityManager() {
+ return entityManager;
+ }
+
+ /**
+ * @see org.jboss.dna.connector.store.jpa.JpaSource#isCreatingWorkspacesAllowed()
+ */
+ final boolean creatingWorkspacesAllowed() {
+ return this.creatingWorkspacesAllowed;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.MapRepository#createWorkspace(org.jboss.dna.graph.ExecutionContext, java.lang.String)
+ */
+ @Override
+ protected MapWorkspace createWorkspace( ExecutionContext context,
+ String name ) {
+
+ WorkspaceEntity entity = workspaceEntities.get(name, false);
+
+ if (entity != null) {
+ return new Workspace(this, name, entity.getId().intValue());
+ }
+
+ entity = workspaceEntities.create(name);
+
+ // Flush to ensure that the entity ID is set
+ entityManager.flush();
+
+ Workspace workspace = new Workspace(this, name, entity.getId().intValue());
+ workspace.createRootNode();
+
+ return workspace;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.MapRepository#getWorkspace(java.lang.String)
+ */
+ @Override
+ public MapWorkspace getWorkspace( String name ) {
+ MapWorkspace workspace = super.getWorkspace(name);
+ if (workspace != null) return workspace;
+
+ // There's no such workspace in the local cache, check if one exists in the DB
+ if (name == null) name = getDefaultWorkspaceName();
+ WorkspaceEntity entity = workspaceEntities.get(name, false);
+ if (entity == null) {
+ if (this.predefinedWorkspaceNames.contains(name)) {
+ return createWorkspace(context, name);
+ }
+
+ return null;
+ }
+
+ return new Workspace(this, name, entity.getId());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.MapRepository#getWorkspaceNames()
+ */
+ @Override
+ public Set getWorkspaceNames() {
+ Set workspaceNames = new HashSet(super.getWorkspaceNames());
+ workspaceNames.addAll(predefinedWorkspaceNames);
+
+ return workspaceNames;
+ }
+
+ /**
+ * This class provides a logical mapping of UUIDs to {@link JpaNode nodes} within a named workspace.
+ *
+ * Like its enclosing class, this class only survives for the lifetime of a single request (which may be a
+ * {@link CompositeRequest}).
+ *
+ */
+ protected class Workspace extends AbstractMapWorkspace {
+ private final long workspaceId;
+ private final Map nodesByPath = new HashMap();
+
+ public Workspace( MapRepository repository,
+ String name,
+ long workspaceId ) {
+ super(repository, name);
+
+ this.workspaceId = workspaceId;
+
+ // This gets called from the repository for this connector since the repository
+ // already knows whether this workspace existed in the database before this call.
+ // initialize();
+ }
+
+ void createRootNode() {
+ initialize();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.AbstractMapWorkspace#correctSameNameSiblingIndexes(org.jboss.dna.graph.ExecutionContext, org.jboss.dna.graph.connector.map.MapNode, org.jboss.dna.graph.property.Name)
+ */
+ @Override
+ protected void correctSameNameSiblingIndexes( ExecutionContext context,
+ MapNode parentNode,
+ Name name ) {
+ int snsIndex = 1;
+ int parentIndex = 0;
+ List children = parentNode.getChildren();
+
+ for (MapNode child : children) {
+ NodeEntity childNode = ((JpaNode)child).entity;
+ if (parentIndex != childNode.getIndexInParent()) {
+ childNode.setIndexInParent(parentIndex);
+ }
+
+ if (name.equals(child.getName().getName())) {
+ if (snsIndex != childNode.getSameNameSiblingIndex()) {
+ childNode.setSameNameSiblingIndex(snsIndex);
+ }
+ snsIndex++;
+
+ }
+ parentIndex++;
+ }
+
+ }
+
+ /**
+ * Adds the given node to the persistent store, replacing any node already in the persistent store with the same UUID.
+ *
+ * Invoking this method causes a database INSERT statement to execute immediately.
+ *
+ *
+ * @param node the node to add to the persistent store; may not be null
+ */
+ @Override
+ protected void addNodeToMap( MapNode node ) {
+ assert node != null;
+
+ NodeEntity nodeEntity = ((JpaNode)node).entity;
+ nodeEntity.setWorkspaceId(this.workspaceId);
+
+ entityManager.persist(nodeEntity);
+ }
+
+ @Override
+ protected MapNode removeNodeFromMap( UUID nodeUuid ) {
+ throw new IllegalStateException("This code should be unreachable");
+ }
+
+ /**
+ * Removes the given node and its children from the persistent store using the
+ * {@link SubgraphQuery#deleteSubgraph(boolean) subgraph bulk delete method}.
+ *
+ * @param node the root of the branch to be removed
+ */
+ @Override
+ protected void removeUuidReference( MapNode node ) {
+ SubgraphQuery branch = SubgraphQuery.create(context, entityManager, workspaceId, node.getUuid(), null, 0);
+ branch.deleteSubgraph(true);
+ branch.close();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.AbstractMapWorkspace#createMapNode(java.util.UUID)
+ */
+ @Override
+ protected MapNode createMapNode( UUID uuid ) {
+ return new JpaNode(uuid);
+ }
+
+ /**
+ * Removes all of the nodes in this workspace from the persistent store with a single query.
+ */
+ @Override
+ protected void removeAllNodesFromMap() {
+ Query query = entityManager.createQuery("NodeEntity.deleteAllInWorkspace");
+ query.setParameter("workspaceId", workspaceId);
+ query.executeUpdate();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.AbstractMapWorkspace#getNode(java.util.UUID)
+ */
+ @Override
+ public JpaNode getNode( UUID nodeUuid ) {
+ assert nodeUuid != null;
+
+ Query query = entityManager.createNamedQuery("NodeEntity.findByNodeUuid");
+ query.setParameter("workspaceId", workspaceId);
+ query.setParameter("nodeUuidString", nodeUuid.toString());
+ try {
+ // Find the parent of the UUID ...
+ NodeEntity result = (NodeEntity)query.getSingleResult();
+ return new JpaNode(result);
+ } catch (NoResultException e) {
+ return null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.jboss.dna.graph.connector.map.AbstractMapWorkspace#getNode(org.jboss.dna.graph.property.Path)
+ */
+ @Override
+ public MapNode getNode( Path path ) {
+ MapNode node = nodesByPath.get(path);
+ if (node != null) return node;
+
+ node = super.getNode(path);
+ nodesByPath.put(path, node);
+ return node;
+ }
+
+ /**
+ * Retrieves the branch of nodes rooted at the given location using the {@link SubgraphQuery#getNodes(boolean, boolean)
+ * subgraph bulk accessor method}.
+ *
+ * @param rootLocation the root of the branch of nodes to retrieve
+ * @param maximumDepth the maximum depth to retrieve; a negative number indicates that the entire branch should be
+ * retrieved
+ * @return the list of nodes in the branch rooted at {@code rootLocation}
+ */
+ public List getBranch( Location rootLocation,
+ int maximumDepth ) {
+ assert rootLocation.getUuid() != null || rootLocation.getPath() != null;
+ UUID subgraphRootUuid = rootLocation.getUuid();
+
+ if (subgraphRootUuid == null) {
+ MapNode rootNode = getNode(rootLocation.getPath());
+ subgraphRootUuid = rootNode.getUuid();
+ assert subgraphRootUuid != null;
+ }
+
+ Path subgraphRootPath = null; // Don't need the path for this
+ SubgraphQuery subgraph = SubgraphQuery.create(context,
+ entityManager,
+ workspaceId,
+ subgraphRootUuid,
+ subgraphRootPath,
+ maximumDepth);
+
+ List entities = subgraph.getNodes(true, true);
+ List nodes = new ArrayList(entities.size());
+
+ for (NodeEntity entity : entities) {
+ nodes.add(new JpaNode(entity));
+ }
+
+ subgraph.close();
+
+ return nodes;
+ }
+
+ /**
+ * This connector does not support connector-level, persistent locking of nodes.
+ */
+ public void lockNode( MapNode node,
+ LockScope lockScope,
+ long lockTimeoutInMillis ) throws LockFailedException {
+ // Locking is not supported by this connector
+ }
+
+ /**
+ * This connector does not support connector-level, persistent locking of nodes.
+ */
+ public void unlockNode( MapNode node ) {
+ // Locking is not supported by this connector
+ }
+
+ }
+
+ /**
+ * Adapter between the {@link NodeEntity persistent entity for nodes} and the {@link MapNode map repository interface for
+ * nodes}.
+ */
+ @NotThreadSafe
+ protected class JpaNode implements MapNode {
+ private final NodeEntity entity;
+ private Map properties = null;
+
+ protected JpaNode( NodeEntity entity ) {
+ this.entity = entity;
+ }
+
+ public JpaNode( UUID uuid ) {
+ this.entity = new NodeEntity();
+ entity.setNodeUuidString(uuid.toString());
+ }
+
+ private final JpaNode jpaNodeFor( MapNode node ) {
+ if (!(node instanceof JpaNode)) {
+ throw new IllegalStateException();
+ }
+ return (JpaNode)node;
+ }
+
+ public void addChild( int index,
+ MapNode child ) {
+ entity.addChild(index, jpaNodeFor(child).entity);
+ }
+
+ public void addChild( MapNode child ) {
+ entity.addChild(jpaNodeFor(child).entity);
+ }
+
+ public List getChildren() {
+ List children = new ArrayList(entity.getChildren().size());
+
+ for (NodeEntity child : entity.getChildren()) {
+ children.add(new JpaNode(child));
+ }
+
+ return Collections.unmodifiableList(children);
+ }
+
+ public Segment getName() {
+ return pathFactory.createSegment(nameFactory.create(entity.getChildNamespace().getUri(), entity.getChildName()),
+ entity.getSameNameSiblingIndex());
+ }
+
+ public MapNode getParent() {
+ if (entity.getParent() == null) return null;
+ return new JpaNode(entity.getParent());
+ }
+
+ private void ensurePropertiesLoaded() {
+ if (properties != null) return;
+
+ Collection propsCollection = new LinkedList();
+
+ if (entity.getData() != null) {
+ Serializer serializer = new Serializer(context, true);
+ ObjectInputStream ois = null;
+
+ try {
+ LargeValueSerializer largeValues = new LargeValueSerializer(entity);
+ ois = new ObjectInputStream(new ByteArrayInputStream(entity.getData()));
+ serializer.deserializeAllProperties(ois, propsCollection, largeValues);
+
+ } catch (IOException ioe) {
+ throw new IllegalStateException(ioe);
+ } catch (ClassNotFoundException cnfe) {
+ throw new IllegalStateException(cnfe);
+ } finally {
+ try {
+ if (ois != null) ois.close();
+ } catch (Exception ex) {
+ }
+ }
+ }
+
+ PropertyFactory propertyFactory = context.getPropertyFactory();
+ Map properties = new HashMap();
+ properties.put(DnaLexicon.UUID, propertyFactory.create(DnaLexicon.UUID, getUuid()));
+ for (Property prop : propsCollection) {
+ properties.put(prop.getName(), prop);
+ }
+
+ this.properties = properties;
+ }
+
+ private void serializeProperties() {
+ Serializer serializer = new Serializer(context, true);
+ ObjectOutputStream oos = null;
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(baos);
+
+ LargeValueSerializer largeValues = new LargeValueSerializer(entity);
+ // dna:uuid prop is in collection but won't be serialized
+ int numberOfPropertiesToSerialize = properties.size() - 1;
+ serializer.serializeProperties(oos,
+ numberOfPropertiesToSerialize,
+ properties.values(),
+ largeValues,
+ Serializer.NO_REFERENCES_VALUES);
+ oos.flush();
+ entity.setData(baos.toByteArray());
+ entity.setPropertyCount(properties.size());
+ } catch (IOException ioe) {
+ throw new IllegalStateException(ioe);
+ } finally {
+ try {
+ if (oos != null) oos.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ @Override
+ public MapNode removeProperty( Name propertyName ) {
+ ensurePropertiesLoaded();
+
+ if (properties.containsKey(propertyName)) {
+ properties.remove(propertyName);
+ serializeProperties();
+ }
+ return this;
+ }
+
+ public Map getProperties() {
+ ensurePropertiesLoaded();
+ return properties;
+ }
+
+ public Property getProperty( ExecutionContext context,
+ String name ) {
+ return getProperty(context.getValueFactories().getNameFactory().create(name));
+ }
+
+ public Property getProperty( Name name ) {
+ ensurePropertiesLoaded();
+ return properties.get(name);
+ }
+
+ public Set getUniqueChildNames() {
+ List children = entity.getChildren();
+ Set uniqueNames = new HashSet(children.size());
+
+ for (NodeEntity child : children) {
+ uniqueNames.add(nameFactory.create(child.getChildNamespace().getUri(), child.getChildName()));
+ }
+
+ return uniqueNames;
+ }
+
+ public UUID getUuid() {
+ if (entity.getNodeUuidString() == null) return null;
+ return UUID.fromString(entity.getNodeUuidString());
+ }
+
+ public boolean removeChild( MapNode child ) {
+
+ /*
+ * The NodeEntity.equals method compares on the Hibernate identifier to avoid
+ * confusing Hibernate. However, different nodes can be loaded in the same
+ * session for the same UUID in the same workspace, forcing us to roll our own
+ * implementation of indexOf that tests for the equality of the NodeEntity UUIDs,
+ * rather than their Hibernate identifiers.
+ */
+ List children = entity.getChildren();
+
+ int index = -1;
+ String childUuidString = jpaNodeFor(child).entity.getNodeUuidString();
+ for (int i = 0; i < children.size(); i++) {
+ if (childUuidString.equals(children.get(i).getNodeUuidString())) {
+ index = i;
+ break;
+ }
+ }
+
+ // int index = entity.getChildren().indexOf(jpaNodeFor(child).entity);
+ // assert entity.getChildren().contains(jpaNodeFor(child).entity);
+ if (index < 0) return false;
+
+ entity.removeChild(index);
+
+ assert !entity.getChildren().contains(child);
+ assert child.getParent() == null;
+
+ return true;
+ }
+
+ public void clearChildren() {
+ entity.getChildren().clear();
+ }
+
+ public void setName( Segment name ) {
+ entity.setChildNamespace(namespaceEntities.get(name.getName().getNamespaceUri(), true));
+ // entity.setChildNamespace(NamespaceEntity.findByUri(entityManager, name.getName().getNamespaceUri(), true));
+ entity.setChildName(name.getName().getLocalName());
+ entity.setSameNameSiblingIndex(name.getIndex());
+ }
+
+ public void setParent( MapNode parent ) {
+ if (parent == null) {
+ entity.setParent(null);
+ } else {
+ entity.setParent(jpaNodeFor(parent).entity);
+ }
+ }
+
+ public MapNode setProperty( ExecutionContext context,
+ String name,
+ Object... values ) {
+ PropertyFactory propertyFactory = context.getPropertyFactory();
+
+ return this.setProperty(propertyFactory.create(nameFactory.create(name), values));
+ }
+
+ public MapNode setProperty( Property property ) {
+ ensurePropertiesLoaded();
+
+ properties.put(property.getName(), property);
+ serializeProperties();
+
+ return this;
+ }
+
+ public MapNode setProperties( Iterable properties ) {
+ ensurePropertiesLoaded();
+
+ for (Property property : properties) {
+ this.properties.put(property.getName(), property);
+ }
+
+ serializeProperties();
+
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ if (entity.getNodeUuidString().equals(rootNodeUuid.toString())) return "";
+ return getName().getString() + " (" + entity.getNodeUuidString() + ")";
+ }
+
+ @Override
+ public boolean equals( Object obj ) {
+ if (!(obj instanceof JpaNode)) return false;
+
+ JpaNode other = (JpaNode)obj;
+ return entity.getNodeUuidString().equals(other.entity.getNodeUuidString());
+ }
+
+ @Override
+ public int hashCode() {
+ return entity.getNodeUuidString().hashCode();
+ }
+
+ }
+
+ protected class LargeValueSerializer implements LargeValues {
+ private final NodeEntity node;
+ private final Set written;
+
+ public LargeValueSerializer( NodeEntity entity ) {
+ this.node = entity;
+ this.written = null;
+ }
+
+ public LargeValueSerializer( NodeEntity entity,
+ Set written ) {
+ this.node = entity;
+ this.written = written;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return minimumSizeOfLargeValuesInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.property.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ String hashStr = StringUtil.getHexString(hash);
+ // Find the large value ...
+ LargeValueEntity entity = entityManager.find(LargeValueEntity.class, hashStr);
+ if (entity != null) {
+ // Find the large value from the existing property entity ...
+ byte[] data = entity.getData();
+ if (entity.isCompressed()) {
+ InputStream stream = new GZIPInputStream(new ByteArrayInputStream(data));
+ try {
+ data = IoUtil.readBytes(stream);
+ } finally {
+ stream.close();
+ }
+ }
+ return valueFactories.getValueFactory(entity.getType()).create(data);
+ }
+ throw new IOException(JpaConnectorI18n.unableToReadLargeValue.text(getSourceName(), hashStr));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#write(byte[], long,
+ * org.jboss.dna.graph.property.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);
+ if (written != null) written.add(hashStr);
+
+ // Look for an existing value in the collection ...
+ for (LargeValueEntity existing : node.getLargeValues()) {
+ if (existing.getHash().equals(hashStr)) {
+ // Already associated with this properties entity
+ return;
+ }
+ }
+ LargeValueEntity entity = entityManager.find(LargeValueEntity.class, hashStr);
+ if (entity == null) {
+ // We have to create the large value entity ...
+ entity = new LargeValueEntity();
+ entity.setCompressed(compressData);
+ entity.setHash(hashStr);
+ entity.setLength(length);
+ entity.setType(type);
+ ValueFactories factories = context.getValueFactories();
+ byte[] bytes = null;
+ switch (type) {
+ case BINARY:
+ Binary binary = factories.getBinaryFactory().create(value);
+ InputStream stream = null;
+ try {
+ binary.acquire();
+ stream = binary.getStream();
+ if (compressData) stream = new GZIPInputStream(stream);
+ bytes = IoUtil.readBytes(stream);
+ } finally {
+ try {
+ if (stream != null) stream.close();
+ } finally {
+ binary.release();
+ }
+ }
+ break;
+ case URI:
+ // This will be treated as a string ...
+ default:
+ String str = factories.getStringFactory().create(value);
+ if (compressData) {
+ ByteArrayOutputStream bs = new ByteArrayOutputStream();
+ OutputStream strStream = new GZIPOutputStream(bs);
+ try {
+ IoUtil.write(str, strStream);
+ } finally {
+ strStream.close();
+ }
+ bytes = bs.toByteArray();
+ } else {
+ bytes = str.getBytes();
+ }
+ break;
+ }
+ entity.setData(bytes);
+ entityManager.persist(entity);
+ }
+ // Now associate the large value with the properties entity ...
+ assert entity.getHash() != null;
+ node.getLargeValues().add(entity);
+ }
+
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleJpaRepository.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleModel.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleModel.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleModel.java (revision 0)
@@ -0,0 +1,115 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.Model;
+import org.jboss.dna.connector.store.jpa.model.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.model.common.WorkspaceEntity;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+
+/**
+ * Database model that stores each node (transparently) and its properties (opaquely) in a single row. Large property values are
+ * stored separately and can be shared between nodes.
+ *
+ * The set of tables used in this model includes:
+ *
+ * - Namespaces - the set of namespace URIs used in paths, property names, and property values.
+ * - Nodes - each node along with its name, seralized properties, parent, UUID, and position within its parent. This approach
+ * makes it possible to efficiently work with nodes containing large numbers of children, where adding and removing child nodes is
+ * largely independent of the number of children. Also, working with properties is also completely independent of the number of
+ * child nodes.
+ * - Large values - property values larger than a certain size will be broken out into this table, where they are tracked by
+ * their SHA-1 hash and shared by all properties that have that same value. The values are stored in a binary (and optionally
+ * compressed) form.
+ * - ReferenceChanges - the references from one node to another
+ * - Subgraph - a working area for efficiently computing the space of a subgraph; see below
+ * - Change log - a record of the changes that have been made to the repository. This is used to distribute change events across
+ * multiple distributed processes, and to allow a recently-connected client to identify the set of changes that have been made
+ * since a particular time or date. Changes are serialized into a binary, compressed format.
+ * - Options - the parameters for this store's configuration (common to all models)
+ *
+ *
+ * Subgraph queries
+ *
+ * This database model contains two tables that are used in an efficient mechanism to find all of the nodes in the subgraph below
+ * a certain node. This process starts by creating a record for the subgraph query, and then proceeds by executing a join to find
+ * all the children of the top-level node, and inserting them into the database (in a working area associated with the subgraph
+ * query). Then, another join finds all the children of those children and inserts them into the same working area. This continues
+ * until the maximum depth has been reached, or until there are no more children (whichever comes first). All of the nodes in the
+ * subgraph are then represented by records in the working area, and can be used to quickly and efficient work with the subgraph
+ * nodes. When finished, the mechanism deletes the records in the working area associated with the subgraph query.
+ *
+ *
+ * This subgraph query mechanism is extremely efficient, performing one join/insert statement per level of the subgraph,
+ * and is completely independent of the number of nodes in the subgraph. For example, consider a subgraph of node A, where A has
+ * 10 children, and each child contains 10 children, and each grandchild contains 10 children. This subgraph has a total of 1111
+ * nodes (1 root + 10 children + 10*10 grandchildren + 10*10*10 great-grandchildren). Finding the nodes in this subgraph would
+ * normally require 1 query per node (in other words, 1111 queries). But with this subgraph query mechanism, all of the nodes in
+ * the subgraph can be found with 1 insert plus 4 additional join/inserts.
+ *
+ *
+ * This mechanism has the added benefit that the set of nodes in the subgraph are kept in a working area in the database, meaning
+ * they don't have to be pulled into memory.
+ *
+ *
+ * Subgraph queries are used to efficiently process a number of different requests, including {@link ReadBranchRequest},
+ * {@link DeleteBranchRequest}, {@link MoveBranchRequest}, and {@link CopyBranchRequest}. Processing each of these kinds of
+ * requests requires knowledge of the subgraph, and in fact all but the ReadBranchRequest
need to know the complete
+ * subgraph.
+ *
+ */
+public class SimpleModel extends Model {
+
+ public SimpleModel() {
+ super("Simple", JpaConnectorI18n.simpleModelDescription);
+ }
+
+ /**
+ * 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(WorkspaceEntity.class);
+ configurator.addAnnotatedClass(NamespaceEntity.class);
+ configurator.addAnnotatedClass(LargeValueEntity.class);
+ configurator.addAnnotatedClass(NodeEntity.class);
+ configurator.addAnnotatedClass(SubgraphNodeEntity.class);
+ configurator.addAnnotatedClass(SubgraphQueryEntity.class);
+ }
+
+ @Override
+ public RepositoryConnection createConnection( JpaSource source ) {
+ return new SimpleJpaConnection(source);
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleModel.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleRequestProcessor.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleRequestProcessor.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleRequestProcessor.java (revision 0)
@@ -0,0 +1,151 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import javax.persistence.EntityTransaction;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.map.MapNode;
+import org.jboss.dna.graph.connector.map.MapRequestProcessor;
+import org.jboss.dna.graph.observe.Observer;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+import com.google.common.collect.LinkedListMultimap;
+
+/**
+ * Extension of the {@link MapRequestProcessor} that provides a {@link #process(ReadBranchRequest)} implementation optimized for
+ * the {@link SimpleModel simple JPA model}. This class also provides some JPA-specific functionality for the ability to control
+ * whether {@link JpaSource#isCreatingWorkspacesAllowed() creating workspaces is allowed}.
+ */
+public class SimpleRequestProcessor extends MapRequestProcessor {
+
+ private final SimpleJpaRepository repository;
+ private final PathFactory pathFactory;
+
+ public SimpleRequestProcessor( ExecutionContext context,
+ SimpleJpaRepository repository,
+ Observer observer ) {
+ super(context, repository, observer);
+
+ this.repository = repository;
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ }
+
+ @Override
+ public void close() {
+ EntityTransaction tx = repository.entityManager().getTransaction();
+ if (tx != null) {
+ tx.commit();
+ }
+ super.close();
+ }
+
+ /**
+ * Override the {@link RequestProcessor#process(ReadBranchRequest) default handling} for a read branch request to optimize the
+ * queries involved.
+ *
+ * @param request the request to read
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ SimpleJpaRepository.Workspace workspace = (SimpleJpaRepository.Workspace)getWorkspace(request, request.inWorkspace());
+
+ int maximumDepth = request.maximumDepth();
+ List branch = workspace.getBranch(request.at(), maximumDepth);
+
+ if (!branch.isEmpty()) {
+ Map locations = new HashMap(branch.size());
+
+ /*
+ * Add the first (root) node to the request
+ */
+ MapNode root = branch.get(0);
+ Location rootLocation = getActualLocation(request.at(), root);
+ request.setActualLocationOfNode(rootLocation);
+ locations.put(root.getUuid(), new LocationWithDepth(rootLocation, 0));
+
+ /*
+ * The obvious thing to do here would be to call root.getChildren(), but that would
+ * result in the JPA implementation running an extra query to load the collection of
+ * children for the entity even though we've already loaded all of the children
+ * with the call to workspace.getBranch(...) earlier.
+ *
+ * We'll build the list of children ourselves knowing that all children are in the result set.
+ *
+ * The concrete type is used in the variable declaration instead of the relevant interface
+ * (Multimap) because we need to cast the result of a .get(UUID) operation
+ * to a List below and the interface only guarantees a Collection.
+ */
+ LinkedListMultimap childrenByParentUuid = LinkedListMultimap.create();
+
+ /*
+ * We don't want to process the root node (the first node) in this loop
+ * as this would cause us to unnecessarily load the root node's parent node.
+ */
+ for (int i = 1; i < branch.size(); i++) {
+ MapNode node = branch.get(i);
+ UUID parentUuid = node.getParent().getUuid();
+
+ LocationWithDepth parentLocation = locations.get(parentUuid);
+ Location nodeLocation = locationFor(parentLocation.getLocation(), node);
+ locations.put(node.getUuid(), new LocationWithDepth(nodeLocation, parentLocation.getDepth() + 1));
+
+ childrenByParentUuid.put(parentUuid, locationFor(locations.get(parentUuid).getLocation(), node));
+ }
+
+ request.setChildren(rootLocation, childrenByParentUuid.get(root.getUuid()));
+ request.setProperties(rootLocation, root.getProperties().values());
+
+ /*
+ * Process the subsequent nodes
+ */
+ for (int i = 1; i < branch.size(); i++) {
+ MapNode node = branch.get(i);
+
+ UUID nodeUuid = node.getUuid();
+ LocationWithDepth nodeLocation = locations.get(nodeUuid);
+ if (nodeLocation.getDepth() < maximumDepth) {
+ request.setChildren(nodeLocation.getLocation(), childrenByParentUuid.get(nodeUuid));
+ request.setProperties(nodeLocation.getLocation(), node.getProperties().values());
+ }
+ }
+ }
+
+ setCacheableInfo(request);
+ }
+
+ private Location locationFor( Location parentLocation,
+ MapNode node ) {
+ return Location.create(pathFactory.create(parentLocation.getPath(), node.getName()), node.getUuid());
+ }
+
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ if (!repository.creatingWorkspacesAllowed()) {
+ String msg = JpaConnectorI18n.unableToCreateWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ return;
+ }
+
+ super.process(request);
+ }
+
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ if (!repository.creatingWorkspacesAllowed()) {
+ String msg = JpaConnectorI18n.unableToCreateWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ return;
+ }
+
+ super.process(request);
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleRequestProcessor.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphNodeEntity.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphNodeEntity.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphNodeEntity.java (revision 0)
@@ -0,0 +1,175 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import org.hibernate.annotations.Index;
+
+/**
+ * Represents a single node that appears in a subgraph.
+ *
+ * @see SubgraphQueryEntity
+ */
+@Entity
+@org.hibernate.annotations.Table( appliesTo = "DNA_SUBGRAPH_NODES", indexes = @Index( name = "QUERYID_INX", columnNames = {
+ "QUERY_ID", "UUID", "DEPTH"} ) )
+@Table( name = "DNA_SUBGRAPH_NODES" )
+@NamedQueries( {
+ @NamedQuery( name = "SubgraphNodeEntity.insertChildren", query = "insert into SubgraphNodeEntity(queryId,nodeUuid,depth,parentIndexInParent,indexInParent) select parentNode.queryId, child.nodeUuidString, parentNode.depth+1, parentNode.indexInParent, child.indexInParent from NodeEntity child, SubgraphNodeEntity parentNode where child.workspaceId = :workspaceId and child.parent.nodeUuidString = parentNode.nodeUuid and parentNode.queryId = :queryId and parentNode.depth = :parentDepth" ),
+ @NamedQuery( name = "SubgraphNodeEntity.getCount", query = "select count(*) from SubgraphNodeEntity where queryId = :queryId" ),
+ @NamedQuery( name = "SubgraphNodeEntity.getNodeEntitiesWithLargeValues", query = "select props from NodeEntity props, SubgraphNodeEntity node where props.workspaceId = :workspaceId and props.nodeUuidString = node.nodeUuid and node.queryId = :queryId and node.depth >= :depth and size(props.largeValues) > 0" ),
+ @NamedQuery( name = "SubgraphNodeEntity.getChildEntities", query = "select child from NodeEntity child, SubgraphNodeEntity node where child.workspaceId = :workspaceId and child.nodeUuidString = node.nodeUuid and node.queryId = :queryId and node.depth >= :depth and node.depth <= :maxDepth order by node.depth, node.parentIndexInParent, node.indexInParent" ),
+ // @NamedQuery( name = "SubgraphNodeEntity.getInternalReferences", query =
+ // "select ref from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.toUuidString in ( select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId) and ref.id.fromUuidString in (select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId)"
+ // ),
+ // @NamedQuery( name = "SubgraphNodeEntity.getOutwardReferences", query =
+ // "select ref from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.toUuidString not in ( select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId) and ref.id.fromUuidString in (select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId)"
+ // ),
+ // @NamedQuery( name = "SubgraphNodeEntity.getInwardReferences", query =
+ // "select ref from ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.toUuidString in ( select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId) and ref.id.fromUuidString not in (select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId)"
+ // ),
+ @NamedQuery( name = "SubgraphNodeEntity.clearParentReferences", query = "update NodeEntity child set child.parent = null where child.workspaceId = :workspaceId and child.nodeUuidString in ( select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId and node.depth >= :depth )" ),
+ @NamedQuery( name = "SubgraphNodeEntity.deleteChildEntities", query = "delete NodeEntity child where child.workspaceId = :workspaceId and child.nodeUuidString in ( select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId and node.depth >= :depth )" ),
+ // @NamedQuery( name = "SubgraphNodeEntity.deleteReferences", query =
+ // "delete ReferenceEntity as ref where ref.id.workspaceId = :workspaceId and ref.id.fromUuidString in ( select node.nodeUuid from SubgraphNodeEntity node where node.queryId = :queryId )"
+ // ),
+ @NamedQuery( name = "SubgraphNodeEntity.deleteByQueryId", query = "delete SubgraphNodeEntity where queryId = :queryId" )} )
+public class SubgraphNodeEntity {
+
+ @Id
+ @Column( name = "ID" )
+ @GeneratedValue( strategy = GenerationType.AUTO )
+ private Long id;
+
+ @Column( name = "QUERY_ID", nullable = false, unique = false, updatable = false )
+ private Long queryId;
+
+ @Column( name = "UUID", updatable = false, nullable = false, length = 36 )
+ private String nodeUuid;
+
+ @Column( name = "DEPTH", updatable = false, nullable = false )
+ private int depth;
+
+ @Column( name = "PARENT_NUM", updatable = false, nullable = false )
+ private int parentIndexInParent;
+
+ @Column( name = "CHILD_NUM", updatable = false, nullable = false )
+ private int indexInParent;
+
+ public SubgraphNodeEntity() {
+ }
+
+ public SubgraphNodeEntity( Long queryId,
+ String nodeUuid,
+ int depth ) {
+ this.queryId = queryId;
+ this.nodeUuid = nodeUuid;
+ this.depth = depth;
+ }
+
+ /**
+ * @return id
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * @return depth
+ */
+ public int getDepth() {
+ return depth;
+ }
+
+ /**
+ * @return nodeUuid
+ */
+ public String getNodeUuid() {
+ return nodeUuid;
+ }
+
+ /**
+ * @return queryId
+ */
+ public Long getQueryId() {
+ return queryId;
+ }
+
+ /**
+ * @return indexInParent
+ */
+ public int getIndexInParent() {
+ return indexInParent;
+ }
+
+ /**
+ * @return parentIndexInParent
+ */
+ public int getParentIndexInParent() {
+ return parentIndexInParent;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return id != null ? id.intValue() : 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof SubgraphNodeEntity) {
+ SubgraphNodeEntity that = (SubgraphNodeEntity)obj;
+ if (this.id.equals(that.id)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "" + id + " - Query " + queryId + "; depth=" + depth + "; node=" + nodeUuid + " at index " + indexInParent;
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SubgraphNodeEntity.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphQuery.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphQuery.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphQuery.java (revision 0)
@@ -0,0 +1,388 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Path;
+
+/**
+ * Represents a temporary working area for a query that efficiently retrieves the nodes in a subgraph. This class uses the
+ * database to build up the content of the subgraph, and therefore requires write privilege on the database. The benefit is that
+ * it minimizes the amount of memory required to process the subgraph, plus the set of nodes that make up the subgraph can be
+ * produced with database joins.
+ *
+ * The use of database joins also produces another benefit: the number of SQL statements necessary to build the set of nodes in a
+ * subgraph is equal to the depth of the subgraph, regardless of the number of child nodes at any level.
+ *
+ */
+public class SubgraphQuery {
+
+ /**
+ * Create a query that returns a subgraph at and below the node with the supplied path and the supplied UUID.
+ *
+ * @param context the execution context; may not be null
+ * @param entities the entity manager; may not be null
+ * @param workspaceId the ID of the workspace; may not be null
+ * @param subgraphRootUuid the UUID (in string form) of the root node in the subgraph
+ * @param subgraphRootPath the path of the root node in the subgraph
+ * @param maxDepth the maximum depth of the subgraph, or 0 if there is no maximum depth
+ * @return the object representing the subgraph
+ */
+ public static SubgraphQuery create( ExecutionContext context,
+ EntityManager entities,
+ Long workspaceId,
+ UUID subgraphRootUuid,
+ Path subgraphRootPath,
+ int maxDepth ) {
+ assert entities != null;
+ assert subgraphRootUuid != null;
+ assert workspaceId != null;
+ assert maxDepth >= 0;
+ if (maxDepth == 0) maxDepth = Integer.MAX_VALUE;
+ final String subgraphRootUuidString = subgraphRootUuid.toString();
+ // Create a new subgraph query, and add a child for the root ...
+
+ SubgraphQueryEntity query = new SubgraphQueryEntity(workspaceId, subgraphRootUuidString);
+ entities.persist(query);
+ Long queryId = query.getId();
+
+ try {
+ // Insert a node for the root (this will be the starting point for the recursive operation) ...
+ SubgraphNodeEntity root = new SubgraphNodeEntity(queryId, subgraphRootUuidString, 0);
+ entities.persist(root);
+
+ // Now add the children by inserting the children, one level at a time.
+ // Note that we do this for the root, and for each level until 1 BEYOND
+ // the max depth (so that we can get the children for the nodes that are
+ // at the maximum depth)...
+ Query statement = entities.createNamedQuery("SubgraphNodeEntity.insertChildren");
+ int numChildrenInserted = 0;
+ int parentLevel = 0;
+ while (parentLevel <= maxDepth) {
+ // Insert the children of the next level by inserting via a select (join) of the children
+ statement.setParameter("queryId", queryId);
+ statement.setParameter("workspaceId", workspaceId);
+ statement.setParameter("parentDepth", parentLevel);
+ numChildrenInserted = statement.executeUpdate();
+ if (numChildrenInserted == 0) break;
+ parentLevel = parentLevel + 1;
+ }
+ } catch (RuntimeException t) {
+ // Clean up the search and results ...
+ try {
+ Query search = entities.createNamedQuery("SubgraphNodeEntity.deleteByQueryId");
+ search.setParameter("queryId", query.getId());
+ search.executeUpdate();
+ } finally {
+ entities.remove(query);
+ }
+ throw t;
+ }
+
+ return new SubgraphQuery(context, entities, workspaceId, query, subgraphRootPath, maxDepth);
+ }
+
+ // private final ExecutionContext context;
+ private final EntityManager manager;
+ private final Long workspaceId;
+ private SubgraphQueryEntity query;
+ private final int maxDepth;
+ private final Path subgraphRootPath;
+
+ protected SubgraphQuery( ExecutionContext context,
+ EntityManager manager,
+ Long workspaceId,
+ SubgraphQueryEntity query,
+ Path subgraphRootPath,
+ int maxDepth ) {
+ assert manager != null;
+ assert query != null;
+ assert context != null;
+ // assert subgraphRootPath != null;
+ assert workspaceId != null;
+ // this.context = context;
+ this.manager = manager;
+ this.workspaceId = workspaceId;
+ this.query = query;
+ this.maxDepth = maxDepth;
+ this.subgraphRootPath = subgraphRootPath;
+ }
+
+ /**
+ * @return maxDepth
+ */
+ public int getMaxDepth() {
+ return maxDepth;
+ }
+
+ /**
+ * @return manager
+ */
+ public EntityManager getEntityManager() {
+ return manager;
+ }
+
+ /**
+ * @return subgraphRootPath
+ */
+ public Path getSubgraphRootPath() {
+ return subgraphRootPath;
+ }
+
+ /**
+ * @return query
+ */
+ public SubgraphQueryEntity getSubgraphQueryEntity() {
+ if (query == null) throw new IllegalStateException();
+ return query;
+ }
+
+ public int getNodeCount( boolean includeRoot ) {
+ if (query == null) throw new IllegalStateException();
+ // Now query for all the nodes and put into a list ...
+ Query search = manager.createNamedQuery("SubgraphNodeEntity.getCount");
+ search.setParameter("queryId", query.getId());
+
+ // Now process the nodes below the subgraph's root ...
+ try {
+ return ((Long)search.getSingleResult()).intValue() - (includeRoot ? 0 : 1);
+ } catch (NoResultException e) {
+ return 0;
+ }
+ }
+
+ /**
+ * Get the {@link NodeEntity root node} of the subgraph. This must be called before the query is {@link #close() closed}.
+ *
+ * @return the subgraph's root nodes
+ */
+ public NodeEntity getNode() {
+ // Now query for all the nodes and put into a list ...
+ Query search = manager.createNamedQuery("SubgraphNodeEntity.getChildEntities");
+ search.setParameter("queryId", query.getId());
+ search.setParameter("workspaceId", workspaceId);
+ search.setParameter("depth", 0);
+ search.setParameter("maxDepth", 0);
+
+ // Now process the nodes below the subgraph's root ...
+ return (NodeEntity)search.getSingleResult();
+ }
+
+ /**
+ * Get the {@link NodeEntity nodes} in the subgraph. This must be called before the query is {@link #close() closed}.
+ *
+ * @param includeRoot true if the subgraph's root node is to be included, or false otherwise
+ * @param includeChildrenOfMaxDepthNodes true if the method is to include nodes that are children of nodes that are at the
+ * maximum depth, or false if only nodes up to the maximum depth are to be included
+ * @return the list of nodes, in breadth-first order
+ */
+ @SuppressWarnings( "unchecked" )
+ public List getNodes( boolean includeRoot,
+ boolean includeChildrenOfMaxDepthNodes ) {
+ if (query == null) throw new IllegalStateException();
+ // Now query for all the nodes and put into a list ...
+ Query search = manager.createNamedQuery("SubgraphNodeEntity.getChildEntities");
+ search.setParameter("queryId", query.getId());
+ search.setParameter("workspaceId", workspaceId);
+ search.setParameter("depth", includeRoot ? 0 : 1);
+ search.setParameter("maxDepth", includeChildrenOfMaxDepthNodes ? maxDepth : maxDepth - 1);
+
+ // Now process the nodes below the subgraph's root ...
+ return search.getResultList();
+ }
+
+ /**
+ * Get the {@link Location} for each of the nodes in the subgraph. This must be called before the query is {@link #close()
+ * closed}.
+ *
+ * This method calls {@link #getNodes(boolean,boolean)}. Therefore, calling {@link #getNodes(boolean,boolean)} and this method
+ * for the same subgraph is not efficient; consider just calling {@link #getNodes(boolean,boolean)} alone.
+ *
+ *
+ * @param includeRoot true if the properties for the subgraph's root node are to be included, or false otherwise
+ * @param includeChildrenOfMaxDepthNodes true if the method is to include nodes that are children of nodes that are at the
+ * maximum depth, or false if only nodes up to the maximum depth are to be included
+ * @return the list of {@link Location locations}, one for each of the nodes in the subgraph, in breadth-first order
+ */
+ // public List getNodeLocations( boolean includeRoot,
+ // boolean includeChildrenOfMaxDepthNodes ) {
+ // if (query == null) throw new IllegalStateException();
+ // // Set up a map of the paths to the nodes, keyed by UUIDs. This saves us from having to build
+ // // the paths every time ...
+ // Map pathByUuid = new HashMap();
+ // LinkedList locations = new LinkedList();
+ // String subgraphRootUuid = query.getRootUuid();
+ // pathByUuid.put(subgraphRootUuid, subgraphRootPath);
+ // UUID uuid = UUID.fromString(subgraphRootUuid);
+ // if (includeRoot) {
+ // locations.add(Location.create(subgraphRootPath, uuid));
+ // }
+ //
+ // // Now iterate over the child nodes in the subgraph (we've already included the root) ...
+ // final PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ // final NameFactory nameFactory = context.getValueFactories().getNameFactory();
+ // for (ChildEntity entity : getNodes(false, includeChildrenOfMaxDepthNodes)) {
+ // String parentUuid = entity.getParentUuidString();
+ // Path parentPath = pathByUuid.get(parentUuid);
+ // assert parentPath != null;
+ // String nsUri = entity.getChildNamespace().getUri();
+ // String localName = entity.getChildName();
+ // int sns = entity.getSameNameSiblingIndex();
+ // Name childName = nameFactory.create(nsUri, localName);
+ // Path childPath = pathFactory.create(parentPath, childName, sns);
+ // String childUuid = entity.getId().getChildUuidString();
+ // pathByUuid.put(childUuid, childPath);
+ // uuid = UUID.fromString(childUuid);
+ // locations.add(Location.create(childPath, uuid));
+ //
+ // }
+ // return locations;
+ // }
+
+ /**
+ * Get the list of references that are owned by nodes within the subgraph and that point to other nodes in this same
+ * subgraph. This set of references is important in copying a subgraph, since all intra-subgraph references in the
+ * original subgraph must also be intra-subgraph references in the copy.
+ *
+ * @return the list of references completely contained by this subgraphs
+ */
+ @SuppressWarnings( "unchecked" )
+ public List getInternalReferences() {
+ Query references = manager.createNamedQuery("SubgraphNodeEntity.getInternalReferences");
+ references.setParameter("queryId", query.getId());
+ references.setParameter("workspaceId", workspaceId);
+ return references.getResultList();
+ }
+
+ /**
+ * Get the list of references that are owned by nodes within the subgraph and that point to nodes not in this same
+ * subgraph. This set of references is important in copying a subgraph.
+ *
+ * @return the list of references that are owned by the subgraph but that point to nodes outside of the subgraph
+ */
+ @SuppressWarnings( "unchecked" )
+ public List getOutwardReferences() {
+ Query references = manager.createNamedQuery("SubgraphNodeEntity.getOutwardReferences");
+ references.setParameter("queryId", query.getId());
+ references.setParameter("workspaceId", workspaceId);
+ return references.getResultList();
+ }
+
+ /**
+ * Get the list of references that are owned by nodes outside of the subgraph that point to nodes in this
+ * subgraph. This set of references is important in deleting nodes, since such references prevent the deletion of the
+ * subgraph.
+ *
+ * @return the list of references that are no longer valid
+ */
+ @SuppressWarnings( "unchecked" )
+ public List getInwardReferences() {
+ // Verify referential integrity: that none of the deleted nodes are referenced by nodes not being deleted.
+ Query references = manager.createNamedQuery("SubgraphNodeEntity.getInwardReferences");
+ references.setParameter("queryId", query.getId());
+ references.setParameter("workspaceId", workspaceId);
+ return references.getResultList();
+ }
+
+ /**
+ * Delete the nodes in the subgraph. This method first does not check for referential integrity (see
+ * {@link #getInwardReferences()}).
+ *
+ * @param includeRoot true if the root node should also be deleted
+ */
+ @SuppressWarnings( "unchecked" )
+ public void deleteSubgraph( boolean includeRoot ) {
+ if (query == null) throw new IllegalStateException();
+
+ List nodes = getNodes(true, true);
+ List uuids = new ArrayList(nodes.size());
+ for (NodeEntity node : nodes) {
+ uuids.add(node.getNodeUuidString());
+ }
+
+ // Delete the LargeValueEntities ...
+ Query withLargeValues = manager.createNamedQuery("SubgraphNodeEntity.getNodeEntitiesWithLargeValues");
+ withLargeValues.setParameter("queryId", query.getId());
+ withLargeValues.setParameter("depth", includeRoot ? 0 : 1);
+ withLargeValues.setParameter("workspaceId", workspaceId);
+ List nodesWithLargeValues = withLargeValues.getResultList();
+ if (nodesWithLargeValues.size() != 0) {
+ for (NodeEntity node : nodesWithLargeValues) {
+ node.getLargeValues().clear();
+ }
+ manager.flush();
+ }
+
+ // Delete the ChildEntities ...
+ Query delete = manager.createNamedQuery("SubgraphNodeEntity.clearParentReferences");
+ delete.setParameter("queryId", query.getId());
+ delete.setParameter("depth", includeRoot ? 0 : 1);
+ delete.setParameter("workspaceId", workspaceId);
+ delete.executeUpdate();
+
+ delete = manager.createNamedQuery("SubgraphNodeEntity.deleteChildEntities");
+ delete.setParameter("queryId", query.getId());
+ delete.setParameter("depth", includeRoot ? 0 : 1);
+ delete.setParameter("workspaceId", workspaceId);
+ delete.executeUpdate();
+
+ // Delete references ...
+ // delete = manager.createNamedQuery("SubgraphNodeEntity.deleteReferences");
+ // delete.setParameter("queryId", query.getId());
+ // delete.setParameter("depth", includeRoot ? 0 : 1);
+ // delete.setParameter("workspaceId", workspaceId);
+ // delete.executeUpdate();
+
+ // Delete unused large values ...
+ LargeValueEntity.deleteUnused(manager);
+
+ manager.flush();
+ }
+
+ /**
+ * Close this query object and clean up all in-database records associated with this query. This method must be called
+ * when this query is no longer needed, and once it is called, this subgraph query is no longer usable.
+ */
+ public void close() {
+ if (query == null) return;
+ // Clean up the search and results ...
+ try {
+ Query search = manager.createNamedQuery("SubgraphNodeEntity.deleteByQueryId");
+ search.setParameter("queryId", query.getId());
+ search.executeUpdate();
+ } finally {
+ try {
+ manager.remove(query);
+ } finally {
+ query = null;
+ }
+ }
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SubgraphQuery.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphQueryEntity.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphQueryEntity.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/model/simple/SubgraphQueryEntity.java (revision 0)
@@ -0,0 +1,75 @@
+/*
+ * JBoss DNA (http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you 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.
+ *
+ * JBoss DNA 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.model.simple;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+/**
+ * Represents a temporary working area for a query that retrieves the nodes in a subgraph.
+ */
+@Entity( name = "DNA_SUBGRAPH_QUERIES" )
+public class SubgraphQueryEntity {
+
+ @Id
+ @GeneratedValue( strategy = GenerationType.AUTO )
+ @Column( name = "ID", updatable = false )
+ private Long id;
+
+ @Column( name = "WORKSPACE_ID", nullable = false )
+ private Long workspaceId;
+
+ @Column( name = "ROOT_UUID", updatable = false, nullable = false, length = 36 )
+ private String rootUuid;
+
+ public SubgraphQueryEntity( Long workspaceId,
+ String rootUuid ) {
+ this.rootUuid = rootUuid;
+ this.workspaceId = workspaceId;
+ }
+
+ /**
+ * @return id
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * @return rootUuid
+ */
+ public String getRootUuid() {
+ return rootUuid;
+ }
+
+ /**
+ * @return workspaceId
+ */
+ public Long getWorkspaceId() {
+ return workspaceId;
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\main\java\org\jboss\dna\connector\store\jpa\model\simple\SubgraphQueryEntity.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
===================================================================
--- extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java (working copy)
@@ -786,7 +786,7 @@
// Read the length of the content ...
long binaryLength = stream.readLong();
byte[] content = new byte[(int)binaryLength];
- stream.read(content);
+ stream.readFully(content, 0, content.length);
if (!skip) {
value = valueFactories.getBinaryFactory().create(content);
}
@@ -796,7 +796,7 @@
// Read the hash ...
int hashLength = stream.readInt();
byte[] hash = new byte[hashLength];
- stream.read(hash);
+ stream.readFully(hash, 0, hashLength);
// Read the length of the content ...
long length = stream.readLong();
if (skip) {
Index: extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
===================================================================
--- extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties (revision 1382)
+++ extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties (working copy)
@@ -43,3 +43,4 @@
connectionIsNoLongerOpen = This connection for source {0} has already been closed
basicModelDescription = Database model that stores node properties as opaque records and children as transparent records. Large property values are stored separately.
+simpleModelDescription = Database model that stores nodes (transparently) and their properties (opaquely) in the same row. Large property values are stored separately.
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorCreateWorkspacesTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorCreateWorkspacesTest.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorCreateWorkspacesTest.java (working copy)
@@ -30,7 +30,6 @@
import org.jboss.dna.common.statistic.Stopwatch;
import org.jboss.dna.graph.Graph;
import org.jboss.dna.graph.Workspace;
-import org.jboss.dna.graph.connector.RepositorySource;
import org.jboss.dna.graph.connector.test.WorkspaceConnectorTest;
import org.junit.Test;
@@ -38,32 +37,13 @@
* These tests verify that the JPA connector behaves correctly when the source is configured to
* {@link JpaSource#setCreatingWorkspacesAllowed(boolean) allow the creation of workspaces}.
*/
-public class JpaConnectorCreateWorkspacesTest extends WorkspaceConnectorTest {
+public abstract class JpaConnectorCreateWorkspacesTest extends WorkspaceConnectorTest {
- private String[] predefinedWorkspaces;
+ protected String[] predefinedWorkspaces;
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#setUpSource()
- */
- @Override
- protected RepositorySource setUpSource() {
- predefinedWorkspaces = new String[] {"workspace1", "workspace1a"};
-
- // Set the connection properties using the environment defined in the POM files ...
- JpaSource source = TestEnvironment.configureJpaSource("Test Repository", this);
-
- // Override the inherited properties, since that's the focus of these tests ...
- source.setCreatingWorkspacesAllowed(true);
- source.setPredefinedWorkspaceNames(predefinedWorkspaces);
-
- return source;
- }
-
- /**
- * {@inheritDoc}
- *
* @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
*/
@Override
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorNoCreateWorkspaceTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorNoCreateWorkspaceTest.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorNoCreateWorkspaceTest.java (working copy)
@@ -40,7 +40,7 @@
*/
public class JpaConnectorNoCreateWorkspaceTest extends WorkspaceConnectorTest {
- private String[] predefinedWorkspaces;
+ protected String[] predefinedWorkspaces;
/**
* {@inheritDoc}
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicCreateWorkspacesTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicCreateWorkspacesTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicCreateWorkspacesTest.java (revision 0)
@@ -0,0 +1,24 @@
+package org.jboss.dna.connector.store.jpa.model.basic;
+
+import org.jboss.dna.connector.store.jpa.JpaConnectorCreateWorkspacesTest;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.TestEnvironment;
+import org.jboss.dna.graph.connector.RepositorySource;
+
+public class BasicCreateWorkspacesTest extends JpaConnectorCreateWorkspacesTest {
+
+ @Override
+ protected RepositorySource setUpSource() {
+ predefinedWorkspaces = new String[] {"workspace1", "workspace1a"};
+
+ // Set the connection properties using the environment defined in the POM files ...
+ JpaSource source = TestEnvironment.configureJpaSource("Test Repository", this);
+
+ // Override the inherited properties, since that's the focus of these tests ...
+ source.setCreatingWorkspacesAllowed(true);
+ source.setPredefinedWorkspaceNames(predefinedWorkspaces);
+
+ return source;
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\basic\BasicCreateWorkspacesTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicNoCreateWorkspaceTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicNoCreateWorkspaceTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/basic/BasicNoCreateWorkspaceTest.java (revision 0)
@@ -0,0 +1,23 @@
+package org.jboss.dna.connector.store.jpa.model.basic;
+
+import org.jboss.dna.connector.store.jpa.JpaConnectorNoCreateWorkspaceTest;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.TestEnvironment;
+import org.jboss.dna.graph.connector.RepositorySource;
+
+public class BasicNoCreateWorkspaceTest extends JpaConnectorNoCreateWorkspaceTest {
+
+ @Override
+ protected RepositorySource setUpSource() {
+ predefinedWorkspaces = new String[] {"workspace1", "workspace1a"};
+
+ // Set the connection properties using the environment defined in the POM files ...
+ JpaSource source = TestEnvironment.configureJpaSource("Test Repository", this);
+
+ // Override the inherited properties, since that's the focus of these tests ...
+ source.setCreatingWorkspacesAllowed(true);
+ source.setPredefinedWorkspaceNames(predefinedWorkspaces);
+
+ return source;
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\basic\BasicNoCreateWorkspaceTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/NodeEntityTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/NodeEntityTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/NodeEntityTest.java (revision 0)
@@ -0,0 +1,168 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Query;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.connector.store.jpa.model.common.NamespaceEntity;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.PropertyType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NodeEntityTest {
+
+ private static final Boolean SHOW_SQL = false;
+ private static final Boolean USE_CACHE = false;
+
+ private ExecutionContext context;
+ private EntityManagerFactory factory;
+ private EntityManager manager;
+ private SimpleModel model;
+
+ @Before
+ public void beforeEach() throws Exception {
+ model = new SimpleModel();
+
+ // 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:mem:.");
+ configurator.setProperty("hibernate.show_sql", SHOW_SQL.toString());
+ configurator.setProperty("hibernate.format_sql", "true");
+ configurator.setProperty("hibernate.use_sql_comments", "true");
+ configurator.setProperty("hibernate.hbm2ddl.auto", "create");
+ if (USE_CACHE) {
+ configurator.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.HashtableCacheProvider");
+
+ }
+
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+ context = new ExecutionContext();
+ }
+
+ @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 shouldSaveAndReloadNode() {
+ String rootUuid = UUID.randomUUID().toString();
+ long workspaceId = 1L;
+
+ manager.getTransaction().begin();
+
+ NamespaceEntity namespace = new NamespaceEntity("");
+ manager.persist(namespace);
+
+ NodeEntity root = new NodeEntity(0, null, rootUuid, workspaceId, 1, namespace, "root");
+ LargeValueEntity largeValue = LargeValueEntity.create("This is a nonsense string that I am typing.".getBytes(),
+ PropertyType.STRING,
+ false);
+ root.getLargeValues().add(largeValue);
+ manager.persist(root);
+ manager.persist(largeValue);
+
+ final int NUM_CHILDREN = 10;
+ for (int i = 0; i < NUM_CHILDREN; i++) {
+ NodeEntity child = new NodeEntity(0, root, UUID.randomUUID().toString(), workspaceId, 1, namespace, "child" + i);
+ root.addChild(child);
+
+ manager.persist(child);
+ }
+
+
+ manager.getTransaction().commit();
+ manager.close();
+
+ manager = factory.createEntityManager();
+
+ Query query = manager.createNamedQuery("NodeEntity.findByNodeUuid");
+ query.setParameter("workspaceId", workspaceId);
+ query.setParameter("nodeUuidString", rootUuid);
+
+ NodeEntity newRoot = (NodeEntity)query.getSingleResult();
+ assertThat(newRoot, is(notNullValue()));
+ assertThat(newRoot, is(root));
+ assertThat(newRoot.getChildren().size(), is(NUM_CHILDREN));
+
+ for (int i = 0; i < NUM_CHILDREN; i++) {
+ assertThat(newRoot.getChildren().get(i).getChildName(), is("child" + i));
+ // NodeEntity child = newRoot.getChildren().get(i);
+ // System.out.println(child.getChildName() + " " + child.getIndexInParent());
+ }
+
+ root.getLargeValues().size();
+ }
+
+ @Test
+ public void shouldDeleteRecursively() {
+ String rootUuid = UUID.randomUUID().toString();
+ long workspaceId = 1L;
+
+ manager.getTransaction().begin();
+
+ NamespaceEntity namespace = new NamespaceEntity("");
+ manager.persist(namespace);
+
+ NodeEntity root = new NodeEntity(0, null, rootUuid, workspaceId, 1, namespace, "root");
+ manager.persist(root);
+
+ final int DEPTH = 10;
+ NodeEntity parent = root;
+
+ for (int i = 0; i < DEPTH; i++) {
+ NodeEntity child = new NodeEntity(0, parent, UUID.randomUUID().toString(), workspaceId, 1, namespace, "child" + i);
+ root.addChild(child);
+
+ manager.persist(child);
+ parent = child;
+ }
+
+ manager.getTransaction().commit();
+ manager.close();
+
+ manager = factory.createEntityManager();
+ manager.getTransaction().begin();
+
+ SubgraphQuery subgraph = SubgraphQuery.create(context,
+ manager,
+ workspaceId,
+ UUID.fromString(rootUuid),
+ context.getValueFactories().getPathFactory().createRootPath(),
+ 0);
+
+ assertThat(subgraph.getNodeCount(false), is(10));
+
+ subgraph.deleteSubgraph(true);
+ subgraph.close();
+
+ int count = (Integer)manager.createNativeQuery("SELECT count(*) FROM dna_simple_node").getSingleResult();
+ assertThat(count, is(0));
+
+ manager.getTransaction().commit();
+
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\simple\NodeEntityTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleCreateWorkspacesTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleCreateWorkspacesTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleCreateWorkspacesTest.java (revision 0)
@@ -0,0 +1,31 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import org.jboss.dna.connector.store.jpa.JpaConnectorCreateWorkspacesTest;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.TestEnvironment;
+import org.jboss.dna.graph.connector.RepositorySource;
+
+public class SimpleCreateWorkspacesTest extends JpaConnectorCreateWorkspacesTest {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#setUpSource()
+ */
+ @Override
+ protected RepositorySource setUpSource() {
+ predefinedWorkspaces = new String[] {"workspace1", "workspace1a"};
+
+ // Set the connection properties using the environment defined in the POM files ...
+ JpaSource source = TestEnvironment.configureJpaSource("Test Repository", this);
+ source.setModel(JpaSource.Models.SIMPLE.getName());
+
+ // Override the inherited properties, since that's the focus of these tests ...
+ source.setCreatingWorkspacesAllowed(true);
+ source.setPredefinedWorkspaceNames(predefinedWorkspaces);
+ source.setDefaultWorkspaceName(predefinedWorkspaces[0]);
+
+ return source;
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleCreateWorkspacesTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnectorReadableTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnectorReadableTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnectorReadableTest.java (revision 0)
@@ -0,0 +1,81 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.test.ReadableConnectorTest;
+import org.jboss.dna.graph.observe.Observer;
+
+public class SimpleJpaConnectorReadableTest extends ReadableConnectorTest {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#setUpSource()
+ */
+ @Override
+ protected RepositorySource setUpSource() {
+ // Set the connection properties using the environment defined in the POM files ...
+ JpaSource source = new JpaSource();
+
+ source.setModel(JpaSource.Models.SIMPLE.getName());
+ source.setName("SimpleJpaSource");
+ source.setDialect("org.hibernate.dialect.HSQLDialect");
+ source.setDriverClassName("org.hsqldb.jdbcDriver");
+ source.setUsername("sa");
+ source.setPassword("");
+ source.setUrl("jdbc:hsqldb:mem:test");
+ source.setShowSql(false);
+ source.setAutoGenerateSchema("create");
+
+ source.initialize(new RepositoryContext() {
+
+ private final ExecutionContext context = new ExecutionContext();
+
+ public Subgraph getConfiguration( int depth ) {
+ return null;
+ }
+
+ public ExecutionContext getExecutionContext() {
+ return context;
+ }
+
+ public Observer getObserver() {
+ return null;
+ }
+
+ public RepositoryConnectionFactory getRepositoryConnectionFactory() {
+ return null;
+ }
+
+ });
+
+ return source;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
+ */
+ @Override
+ protected void initializeContent( Graph graph ) {
+ String initialPath = "";
+ int depth = 4;
+ int numChildrenPerNode = 4;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ // graph.createWorkspace().named("default");
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode, numPropertiesPerNode, batch, sw, System.out, null);
+ graph.createWorkspace().named("other workspace");
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode, numPropertiesPerNode, batch, sw, System.out, null);
+ graph.useWorkspace("default");
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleJpaConnectorReadableTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnectorWritableTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnectorWritableTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaConnectorWritableTest.java (revision 0)
@@ -0,0 +1,74 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.test.WritableConnectorTest;
+import org.jboss.dna.graph.observe.Observer;
+
+public class SimpleJpaConnectorWritableTest extends WritableConnectorTest {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#setUpSource()
+ */
+ @Override
+ protected RepositorySource setUpSource() {
+ // Set the connection properties using the environment defined in the POM files ...
+ JpaSource source = new JpaSource();
+
+ source.setModel(JpaSource.Models.SIMPLE.getName());
+ source.setName("SimpleJpaSource");
+ source.setDialect("org.hibernate.dialect.HSQLDialect");
+ source.setDriverClassName("org.hsqldb.jdbcDriver");
+ source.setUsername("sa");
+ source.setPassword("");
+ source.setUrl("jdbc:hsqldb:mem:test");
+ source.setShowSql(false);
+ source.setAutoGenerateSchema("create");
+
+ source.initialize(new RepositoryContext() {
+
+ private final ExecutionContext context = new ExecutionContext();
+
+ public Subgraph getConfiguration( int depth ) {
+ return null;
+ }
+
+ public ExecutionContext getExecutionContext() {
+ return context;
+ }
+
+ public Observer getObserver() {
+ return null;
+ }
+
+ public RepositoryConnectionFactory getRepositoryConnectionFactory() {
+ return null;
+ }
+
+ });
+
+ return source;
+ }
+
+ @Override
+ public void shouldCopyNodeWithChildren() {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
+ */
+ @Override
+ protected void initializeContent( Graph graph ) {
+ }
+
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleJpaConnectorWritableTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaSourceTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaSourceTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleJpaSourceTest.java (revision 0)
@@ -0,0 +1,62 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import java.util.concurrent.TimeUnit;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.observe.Observer;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SimpleJpaSourceTest {
+
+ private JpaSource source;
+
+ @Before
+ public void beforeEach() {
+ // Set the connection properties using the environment defined in the POM files ...
+ source = new JpaSource();
+
+ source.setModel(JpaSource.Models.SIMPLE.getName());
+ source.setName("SimpleJpaSource");
+ source.setDialect("org.hibernate.dialect.HSQLDialect");
+ source.setDriverClassName("org.hsqldb.jdbcDriver");
+ source.setUsername("sa");
+ source.setPassword("");
+ source.setUrl("jdbc:hsqldb:.");
+ source.setShowSql(true);
+ source.setAutoGenerateSchema("create");
+
+ source.initialize(new RepositoryContext() {
+
+ private final ExecutionContext context = new ExecutionContext();
+
+ public Subgraph getConfiguration( int depth ) {
+ return null;
+ }
+
+ public ExecutionContext getExecutionContext() {
+ return context;
+ }
+
+ public Observer getObserver() {
+ return null;
+ }
+
+ public RepositoryConnectionFactory getRepositoryConnectionFactory() {
+ return null;
+ }
+
+ });
+ }
+
+ @Test
+ public void shouldCreateLiveConnection() throws InterruptedException {
+ RepositoryConnection connection = source.getConnection();
+ connection.ping(1, TimeUnit.SECONDS);
+ connection.close();
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleJpaSourceTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleNoCreateWorkspaceTest.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleNoCreateWorkspaceTest.java (revision 0)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/model/simple/SimpleNoCreateWorkspaceTest.java (revision 0)
@@ -0,0 +1,30 @@
+package org.jboss.dna.connector.store.jpa.model.simple;
+
+import org.jboss.dna.connector.store.jpa.JpaConnectorNoCreateWorkspaceTest;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.TestEnvironment;
+import org.jboss.dna.graph.connector.RepositorySource;
+
+public class SimpleNoCreateWorkspaceTest extends JpaConnectorNoCreateWorkspaceTest {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.test.AbstractConnectorTest#setUpSource()
+ */
+ @Override
+ protected RepositorySource setUpSource() {
+ predefinedWorkspaces = new String[] {"workspace1", "workspace1a"};
+
+ // Set the connection properties using the environment defined in the POM files ...
+ JpaSource source = TestEnvironment.configureJpaSource("Test Repository", this);
+ source.setModel(JpaSource.Models.SIMPLE.getName());
+
+ // Override the inherited properties, since that's the focus of these tests ...
+ source.setCreatingWorkspacesAllowed(true);
+ source.setPredefinedWorkspaceNames(predefinedWorkspaces);
+ source.setDefaultWorkspaceName(predefinedWorkspaces[0]);
+
+ return source;
+ }
+}
Property changes on: extensions\dna-connector-store-jpa\src\test\java\org\jboss\dna\connector\store\jpa\model\simple\SimpleNoCreateWorkspaceTest.java
___________________________________________________________________
Added: svn:keywords
+ Id Revision
Added: svn:eol-style
+ LF
Index: extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/TestEnvironment.java
===================================================================
--- extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/TestEnvironment.java (revision 1382)
+++ extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/TestEnvironment.java (working copy)
@@ -31,7 +31,7 @@
public static JpaSource configureJpaSource( String sourceName,
Object testCase ) {
Properties properties = new Properties();
- ClassLoader loader = testCase instanceof Class ? ((Class>)testCase).getClassLoader() : testCase.getClass()
+ ClassLoader loader = testCase instanceof Class> ? ((Class>)testCase).getClassLoader() : testCase.getClass()
.getClassLoader();
try {
properties.load(loader.getResourceAsStream("database.properties"));