DNA SVN: r650 - in trunk/extensions/dna-connector-store-jpa/src: main/java/org/jboss/dna/connector/store/jpa/models/common and 5 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-11-26 11:42:57 -0500 (Wed, 26 Nov 2008)
New Revision: 650
Modified:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
Log:
DNA-40 Persistant storage for information not stored in other repository sources
Improved functionality and testing, including some performance testing of creating 100s and 1000s of nodes. (These tests are commented out due to the time required to run them.)
Also made minor improvements to the Graph API. Specifically, added the ability to get the UUID out of a Location, changed the CreateNodeRequest.toString() to be more readable, and corrected the interface returned from Graph.create(...) and Graph.Batch.create(...) methods (previously required two .and() calls).
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -36,7 +36,6 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.UUID;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@@ -47,6 +46,7 @@
import net.jcip.annotations.Immutable;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.util.IoUtil;
+import org.jboss.dna.common.util.Logger;
import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
@@ -73,6 +73,7 @@
import org.jboss.dna.graph.requests.MoveBranchRequest;
import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.requests.ReadNodeRequest;
import org.jboss.dna.graph.requests.ReadPropertyRequest;
import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
import org.jboss.dna.graph.requests.processor.RequestProcessor;
@@ -89,9 +90,11 @@
private final NameFactory nameFactory;
private final Namespaces namespaces;
private final UUID rootNodeUuid;
+ private final String rootNodeUuidString;
private final Serializer serializer;
private final long largeValueMinimumSizeInBytes;
private final boolean compressData;
+ protected final Logger logger;
/**
* @param sourceName
@@ -116,9 +119,12 @@
this.nameFactory = context.getValueFactories().getNameFactory();
this.namespaces = new Namespaces(entityManager);
this.rootNodeUuid = rootNodeUuid;
- this.serializer = new Serializer(context, this, true);
+ this.rootNodeUuidString = this.rootNodeUuid.toString();
+ this.serializer = new Serializer(context, true);
this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
this.compressData = compressData;
+ this.logger = getExecutionContext().getLogger(getClass());
+
// Start the transaction ...
this.entities.getTransaction().begin();
}
@@ -130,6 +136,7 @@
*/
@Override
public void process( CreateNodeRequest request ) {
+ logger.trace(request.toString());
Location actualLocation = null;
String childUuidString = null;
try {
@@ -141,33 +148,8 @@
// We need to look for an existing UUID property in the request,
// so since we have to iterate through the properties, go ahead an serialize them right away ...
- Set<String> largeValueHexHashes = new HashSet<String>();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- OutputStream os = compressData ? new ZipOutputStream(baos) : baos;
- ObjectOutputStream oos = new ObjectOutputStream(os);
- int numProperties = 0;
- try {
- for (Property property : request.properties()) {
- if (property.getName().equals(DnaLexicon.UUID)) {
- childUuidString = stringFactory.create(property.getFirstValue());
- }
- if (serializer.serializeProperty(oos, property, largeValueHexHashes)) ++numProperties;
- }
- } finally {
- oos.close();
- }
- String largeValueHexHashesString = createHexValuesString(largeValueHexHashes);
- if (childUuidString == null) childUuidString = stringFactory.create(UUID.randomUUID());
+ childUuidString = createProperties(null, request.properties());
- // Create the PropertiesEntity ...
- NodeId nodeId = new NodeId(childUuidString);
- PropertiesEntity props = new PropertiesEntity(nodeId);
- props.setData(baos.toByteArray());
- props.setCompressed(compressData);
- props.setPropertyCount(numProperties);
- props.setLargeValueKeys(largeValueHexHashesString);
- entities.persist(props);
-
// Find or create the namespace for the child ...
Name childName = request.named();
String childNsUri = childName.getNamespaceUri();
@@ -177,21 +159,23 @@
// Find the largest SNS index in the existing ChildEntity objects with the same name ...
String childLocalName = childName.getLocalName();
Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
- query.setParameter("uuid", parentUuidString);
+ query.setParameter("parentUuid", parentUuidString);
query.setParameter("ns", nsId);
query.setParameter("childName", childLocalName);
int nextSnsIndex = 1;
try {
- nextSnsIndex = (Integer)query.getSingleResult();
+ Integer result = (Integer)query.getSingleResult();
+ nextSnsIndex = result != null ? result + 1 : 1;
} catch (NoResultException e) {
}
// Find the largest child index in the existing ChildEntity objects ...
query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
- query.setParameter("uuid", parentUuidString);
+ query.setParameter("parentUuid", parentUuidString);
int nextIndexInParent = 1;
try {
- nextIndexInParent = (Integer)query.getSingleResult() + 1;
+ Integer result = (Integer)query.getSingleResult();
+ nextIndexInParent = result != null ? result + 1 : 1;
} catch (NoResultException e) {
}
@@ -199,28 +183,100 @@
NamespaceEntity ns = entities.find(NamespaceEntity.class, nsId);
assert ns != null;
ChildId id = new ChildId(parentUuidString, childUuidString);
- ChildEntity entity = new ChildEntity(id, nextIndexInParent, ns, childLocalName, nextSnsIndex + 1);
+ ChildEntity entity = new ChildEntity(id, nextIndexInParent, ns, childLocalName, nextSnsIndex);
entities.persist(entity);
+ // Look up the actual path, regardless of the supplied path...
+ assert childUuidString != null;
+ assert actual.location.getPath() != null;
+ Path path = pathFactory.create(actual.location.getPath(), childName, nextSnsIndex);
+ actualLocation = new Location(path, UUID.fromString(childUuidString));
+
} catch (Throwable e) { // Includes PathNotFoundException
request.setError(e);
+ logger.trace(e, "Problem " + request);
return;
}
- // Look up the actual path, regardless of the supplied path...
- assert childUuidString != null;
- Path path = getPathForUuid(childUuidString);
- actualLocation = new Location(path, UUID.fromString(childUuidString));
request.setActualLocationOfNode(actualLocation);
}
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadNodeRequest)
+ */
+ @SuppressWarnings( "unchecked" )
+ @Override
+ public void process( ReadNodeRequest request ) {
+ logger.trace(request.toString());
+ Location actualLocation = null;
+ try {
+ Location location = request.at();
+ ActualLocation actual = getActualLocation(location);
+ String parentUuidString = actual.uuid;
+ actualLocation = actual.location;
+ Path path = actualLocation.getPath();
+
+ // Record the UUID as a property, since it's not stored in the serialized properties...
+ request.addProperty(actualLocation.getIdProperty(DnaLexicon.UUID));
+
+ // Find the properties entity for this node ...
+ Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
+ query.setParameter("uuid", parentUuidString);
+ try {
+ PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
+
+ // Deserialize the properties ...
+ boolean compressed = entity.isCompressed();
+ Collection<Property> properties = new LinkedList<Property>();
+ byte[] data = entity.getData();
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ InputStream is = compressed ? new ZipInputStream(bais) : bais;
+ ObjectInputStream ois = new ObjectInputStream(is);
+ try {
+ serializer.deserializeAllProperties(ois, properties, this);
+ for (Property property : properties) {
+ request.addProperty(property);
+ }
+ } finally {
+ ois.close();
+ }
+
+ } catch (NoResultException e) {
+ // No properties, but that's okay...
+ }
+ // Find the children of the supplied node ...
+ query = entities.createNamedQuery("ChildEntity.findAllUnderParent");
+ query.setParameter("parentUuidString", parentUuidString);
+ List<ChildEntity> children = query.getResultList();
+ for (ChildEntity child : children) {
+ String namespaceUri = child.getChildNamespace().getUri();
+ String localName = child.getChildName();
+ Name childName = nameFactory.create(namespaceUri, localName);
+ int sns = child.getSameNameSiblingIndex();
+ Path childPath = pathFactory.create(path, childName, sns);
+ String childUuidString = child.getId().getChildUuidString();
+ Location childLocation = new Location(childPath, UUID.fromString(childUuidString));
+ request.addChild(childLocation);
+ }
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllChildrenRequest)
*/
@SuppressWarnings( "unchecked" )
@Override
public void process( ReadAllChildrenRequest request ) {
+ logger.trace(request.toString());
Location actualLocation = null;
try {
Location location = request.of();
@@ -231,14 +287,13 @@
// Find the children of the supplied node ...
Query query = entities.createNamedQuery("ChildEntity.findAllUnderParent");
- query.setParameter("uuid", parentUuidString);
+ query.setParameter("parentUuidString", parentUuidString);
List<ChildEntity> children = query.getResultList();
for (ChildEntity child : children) {
String namespaceUri = child.getChildNamespace().getUri();
String localName = child.getChildName();
Name childName = nameFactory.create(namespaceUri, localName);
- Integer sns = child.getSameNameSiblingIndex();
- if (sns == null) sns = new Integer(1);
+ int sns = child.getSameNameSiblingIndex();
Path childPath = pathFactory.create(path, childName, sns);
String childUuidString = child.getId().getChildUuidString();
Location childLocation = new Location(childPath, UUID.fromString(childUuidString));
@@ -260,6 +315,7 @@
*/
@Override
public void process( ReadAllPropertiesRequest request ) {
+ logger.trace(request.toString());
Location actualLocation = null;
try {
Location location = request.at();
@@ -284,7 +340,7 @@
InputStream is = compressed ? new ZipInputStream(bais) : bais;
ObjectInputStream ois = new ObjectInputStream(is);
try {
- serializer.deserializeAllProperties(ois, properties);
+ serializer.deserializeAllProperties(ois, properties, this);
for (Property property : properties) {
request.addProperty(property);
}
@@ -307,6 +363,7 @@
*/
@Override
public void process( ReadPropertyRequest request ) {
+ logger.trace(request.toString());
// Small optimization ...
final Name propertyName = request.named();
if (DnaLexicon.UUID.equals(propertyName)) {
@@ -342,7 +399,8 @@
InputStream is = compressed ? new ZipInputStream(bais) : bais;
ObjectInputStream ois = new ObjectInputStream(is);
try {
- serializer.deserializeSomeProperties(ois, properties, propertyName);
+ Serializer.LargeValues skippedLargeValues = Serializer.NO_LARGE_VALUES;
+ serializer.deserializeSomeProperties(ois, properties, this, skippedLargeValues, propertyName);
for (Property property : properties) {
request.setProperty(property); // should be only one property
}
@@ -365,6 +423,7 @@
*/
@Override
public void process( UpdatePropertiesRequest request ) {
+ logger.trace(request.toString());
Location actualLocation = null;
try {
Location location = request.on();
@@ -374,49 +433,60 @@
// Find the properties entity for this node ...
Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
query.setParameter("uuid", actual.uuid);
- PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
+ PropertiesEntity entity = null;
+ try {
+ entity = (PropertiesEntity)query.getSingleResult();
- // Determine which large values are referenced ...
- String largeValueHexKeys = entity.getLargeValueKeys();
- Collection<String> hexKeys = null;
- if (largeValueHexKeys != null) {
- hexKeys = createHexValues(largeValueHexKeys);
- }
+ // Determine which large values are referenced ...
+ Collection<String> hexKeys = null;
+ String largeValueHexKeys = entity.getLargeValueKeys();
+ if (largeValueHexKeys != null) {
+ hexKeys = createHexValues(largeValueHexKeys);
+ }
- // Now serialize the properties and save them ...
- Collection<String> newHexKeys = new HashSet<String>();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- OutputStream os = compressData ? new ZipOutputStream(baos) : baos;
- ObjectOutputStream oos = new ObjectOutputStream(os);
- int numProperties = 0;
- try {
- for (Property property : request.properties()) {
- if (serializer.serializeProperty(oos, property, newHexKeys)) ++numProperties;
+ // Prepare the streams so we can deserialize all existing properties and reserialize the old and updated
+ // properties ...
+ boolean compressed = entity.isCompressed();
+ ByteArrayInputStream bais = new ByteArrayInputStream(entity.getData());
+ InputStream is = compressed ? new ZipInputStream(bais) : bais;
+ ObjectInputStream ois = new ObjectInputStream(is);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream os = compressed ? new ZipOutputStream(baos) : baos;
+ ObjectOutputStream oos = new ObjectOutputStream(os);
+ int numProperties = 0;
+ SkippedLargeValues skipped = new SkippedLargeValues();
+ RecordingLargeValues largeValues = new RecordingLargeValues();
+ try {
+ numProperties = serializer.reserializeProperties(ois, oos, request.properties(), largeValues, skipped);
+ } finally {
+ try {
+ ois.close();
+ } finally {
+ oos.close();
+ }
}
- } finally {
- oos.close();
- }
- largeValueHexKeys = createHexValuesString(newHexKeys);
- entity.setPropertyCount(numProperties);
- entity.setData(baos.toByteArray());
- entity.setCompressed(compressData);
- entity.setLargeValueKeys(largeValueHexKeys);
+ largeValueHexKeys = createHexValuesString(largeValues.writtenKeys);
+ entity.setPropertyCount(numProperties);
+ entity.setData(baos.toByteArray());
+ entity.setCompressed(compressData);
+ entity.setLargeValueKeys(largeValueHexKeys);
- // Update the large values that used to be reference but no longer are ...
- if (hexKeys != null) {
- hexKeys.removeAll(newHexKeys);
- for (String oldHexKey : hexKeys) {
- LargeValueEntity largeValue = entities.find(LargeValueEntity.class, oldHexKey);
- if (largeValue != null) {
- if (largeValue.decrementUsageCount() == 0) {
- entities.remove(entity);
+ // Update the large values that used to be reference but no longer are ...
+ if (hexKeys != null) {
+ for (String oldHexKey : skipped.skippedKeys) {
+ LargeValueEntity largeValue = entities.find(LargeValueEntity.class, oldHexKey);
+ if (largeValue != null) {
+ if (largeValue.decrementUsageCount() == 0) {
+ entities.remove(largeValue);
+ }
}
}
}
+ } catch (NoResultException e) {
+ // there are no properties yet ...
+ createProperties(actual.uuid, request.properties());
}
- } catch (NoResultException e) {
- // there are no properties (probably not expected, but still okay) ...
} catch (Throwable e) { // Includes PathNotFoundException
request.setError(e);
return;
@@ -431,6 +501,7 @@
*/
@Override
public void process( CopyBranchRequest request ) {
+ logger.trace(request.toString());
}
/**
@@ -440,6 +511,7 @@
*/
@Override
public void process( DeleteBranchRequest request ) {
+ logger.trace(request.toString());
}
/**
@@ -449,6 +521,7 @@
*/
@Override
public void process( MoveBranchRequest request ) {
+ logger.trace(request.toString());
Location actualOldLocation = null;
Location actualNewLocation = null;
try {
@@ -485,7 +558,7 @@
String childLocalName = fromEntity.getChildName();
NamespaceEntity ns = fromEntity.getChildNamespace();
Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
- query.setParameter("uuid", toUuidString);
+ query.setParameter("parentUuidString", toUuidString);
query.setParameter("ns", ns.getId());
query.setParameter("childName", childLocalName);
int nextSnsIndex = 1;
@@ -496,7 +569,7 @@
// Find the largest child index in the existing ChildEntity objects ...
query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
- query.setParameter("uuid", toUuidString);
+ query.setParameter("parentUuidString", toUuidString);
int nextIndexInParent = 1;
try {
nextIndexInParent = (Integer)query.getSingleResult() + 1;
@@ -528,6 +601,38 @@
}
+ protected String createProperties( String uuidString,
+ Collection<Property> properties ) throws IOException {
+ RecordingLargeValues largeValues = new RecordingLargeValues();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream os = compressData ? new ZipOutputStream(baos) : baos;
+ ObjectOutputStream oos = new ObjectOutputStream(os);
+ int numProperties = properties.size();
+ try {
+ oos.writeInt(numProperties);
+ for (Property property : properties) {
+ if (uuidString == null && property.getName().equals(DnaLexicon.UUID)) {
+ uuidString = stringFactory.create(property.getFirstValue());
+ }
+ if (serializer.serializeProperty(oos, property, largeValues)) ++numProperties;
+ }
+ } finally {
+ oos.close();
+ }
+ String largeValueHexHashesString = createHexValuesString(largeValues.writtenKeys);
+ if (uuidString == null) uuidString = stringFactory.create(UUID.randomUUID());
+
+ // Create the PropertiesEntity ...
+ NodeId nodeId = new NodeId(uuidString);
+ PropertiesEntity props = new PropertiesEntity(nodeId);
+ props.setData(baos.toByteArray());
+ props.setCompressed(compressData);
+ props.setPropertyCount(numProperties);
+ props.setLargeValueKeys(largeValueHexHashesString);
+ entities.persist(props);
+ return uuidString;
+ }
+
/**
* {@inheritDoc}
*
@@ -571,38 +676,53 @@
// Look for the UUID in the original ...
Property uuidProperty = original.getIdProperty(DnaLexicon.UUID);
- String uuidString = uuidProperty.isEmpty() ? null : stringFactory.create(uuidProperty.getFirstValue());
+ String uuidString = uuidProperty != null && !uuidProperty.isEmpty() ? stringFactory.create(uuidProperty.getFirstValue()) : null;
// If the original location has a UUID, then use that to find the child entity that represents the location ...
if (uuidString != null) {
// The original has a UUID, so use that to find the child entity.
// Then walk up the ancestors and build the path.
+ String nodeUuidString = uuidString;
LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
+ // while (uuidString != null && !uuidString.equals(this.rootNodeUuidString)) {
+ // // Find the parent of the child, along with the child's name and SNS index ...
+ // Query query = entities.createNamedQuery("ChildEntity.findValuesByChildUuid");
+ // query.setParameter("childUuidString", uuidString);
+ // try {
+ // Object[] record = (Object[])query.getSingleResult();
+ // String parentUuidString = (String)record[0];
+ // String uri = (String)record[1];
+ // String localName = (String)record[2];
+ // int sns = (Integer)record[3];
+ // // Now create the path segment and set the next child UUID as the parent of this child ...
+ // Name name = nameFactory.create(uri, localName);
+ // segments.addFirst(pathFactory.createSegment(name, sns));
+ // uuidString = parentUuidString;
+ // } catch (NoResultException e) {
+ // uuidString = null;
+ // }
+ // }
+ // Path fullPath = pathFactory.createAbsolutePath(segments);
+ // return new ActualLocation(new Location(fullPath, uuidProperty), nodeUuidString, null);
ChildEntity entity = null;
- ChildEntity childEntity = null;
- do {
- String childUuid = uuidString;
+ while (uuidString != null && !uuidString.equals(this.rootNodeUuidString)) {
Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
- query.setParameter("childUuidString", childUuid);
+ query.setParameter("childUuidString", uuidString);
try {
// Find the parent of the UUID ...
entity = (ChildEntity)query.getSingleResult();
- if (childEntity == null) childEntity = entity;
String localName = entity.getChildName();
String uri = entity.getChildNamespace().getUri();
- Integer sns = entity.getSameNameSiblingIndex();
+ int sns = entity.getSameNameSiblingIndex();
Name name = nameFactory.create(uri, localName);
- if (sns != null) {
- segments.addFirst(pathFactory.createSegment(name, sns));
- } else {
- segments.addFirst(pathFactory.createSegment(name));
- }
+ segments.addFirst(pathFactory.createSegment(name, sns));
+ uuidString = entity.getId().getParentUuidString();
} catch (NoResultException e) {
- entity = null;
+ uuidString = null;
}
- } while (entity != null);
+ }
Path fullPath = pathFactory.createAbsolutePath(segments);
- return new ActualLocation(new Location(fullPath, uuidProperty), uuidString, childEntity);
+ return new ActualLocation(new Location(fullPath, uuidProperty), nodeUuidString, entity);
}
// There is no UUID, so look for a path ...
@@ -615,9 +735,36 @@
// Walk the child entities, starting at the root, down the to the path ...
if (path.isRoot()) {
- return new ActualLocation(original.with(rootNodeUuid), rootNodeUuid.toString(), null);
+ return new ActualLocation(original.with(rootNodeUuid), rootNodeUuidString, null);
}
- String parentUuid = this.rootNodeUuid.toString();
+ String parentUuid = this.rootNodeUuidString;
+ // String childUuid = null;
+ // for (Path.Segment segment : path) {
+ // Name name = segment.getName();
+ // String localName = name.getLocalName();
+ // String nsUri = name.getNamespaceUri();
+ // int snsIndex = segment.hasIndex() ? segment.getIndex() : 1;
+ //
+ // Query query = entities.createNamedQuery("ChildEntity.findChildUuidByPathSegment");
+ // query.setParameter("parentUuidString", parentUuid);
+ // query.setParameter("nsUri", nsUri);
+ // query.setParameter("childName", localName);
+ // query.setParameter("sns", snsIndex);
+ // try {
+ // childUuid = (String)query.getSingleResult();
+ // } catch (NoResultException e) {
+ // // Unable to complete the path, so prepare the exception by determining the lowest path that exists ...
+ // Path lowest = path;
+ // while (lowest.getLastSegment() != segment) {
+ // lowest = lowest.getParent();
+ // }
+ // lowest = lowest.getParent();
+ // throw new PathNotFoundException(original, lowest);
+ // }
+ // parentUuid = childUuid;
+ // }
+ // return new ActualLocation(original.with(UUID.fromString(childUuid)), childUuid, null);
+
ChildEntity child = null;
for (Path.Segment segment : path) {
child = findByPathSegment(parentUuid, segment);
@@ -630,6 +777,7 @@
lowest = lowest.getParent();
throw new PathNotFoundException(original, lowest);
}
+ parentUuid = child.getId().getChildUuidString();
}
assert child != null;
uuidString = child.getId().getChildUuidString();
@@ -653,6 +801,7 @@
String localName = name.getLocalName();
String nsUri = name.getNamespaceUri();
Integer nsId = namespaces.getId(nsUri, false);
+ int snsIndex = pathSegment.hasIndex() ? pathSegment.getIndex() : 1;
if (nsId == null) {
// The namespace can't be found, then certainly the node won't be found ...
return null;
@@ -661,11 +810,7 @@
query.setParameter("parentUuidString", parentUuid);
query.setParameter("ns", nsId);
query.setParameter("childName", localName);
- if (pathSegment.hasIndex()) {
- query.setParameter("sns", localName);
- } else {
- query.setParameter("sns", null);
- }
+ query.setParameter("sns", snsIndex);
try {
return (ChildEntity)query.getSingleResult();
} catch (NoResultException e) {
@@ -673,38 +818,6 @@
}
}
- /**
- * Build up the path for the node with the supplied UUID.
- *
- * @param uuidString the UUID of the node
- * @return the path to the node; never null
- */
- protected Path getPathForUuid( String uuidString ) {
- ChildEntity entity = null;
- String childUuid = uuidString;
- LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
- do {
- // Find the parent of the UUID ...
- Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
- query.setParameter("childUuidString", childUuid);
- try {
- entity = (ChildEntity)query.getSingleResult();
- String localName = entity.getChildName();
- String uri = entity.getChildNamespace().getUri();
- Integer sns = entity.getSameNameSiblingIndex();
- Name name = nameFactory.create(uri, localName);
- if (sns != null) {
- segments.addFirst(pathFactory.createSegment(name, sns));
- } else {
- segments.addFirst(pathFactory.createSegment(name));
- }
- } catch (NoResultException e) {
- entity = null;
- }
- } while (entity != null);
- return pathFactory.createAbsolutePath(segments);
- }
-
protected String createHexValuesString( Collection<String> hexValues ) {
if (hexValues == null || hexValues.isEmpty()) return null;
StringBuilder sb = new StringBuilder();
@@ -810,6 +923,83 @@
}
}
+ protected class RecordingLargeValues implements LargeValues {
+ protected Collection<String> readKeys = new HashSet<String>();
+ protected Collection<String> writtenKeys = new HashSet<String>();
+
+ RecordingLargeValues() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return BasicRequestProcessor.this.getMinimumSize();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ String key = StringUtil.getHexString(hash);
+ readKeys.add(key);
+ return BasicRequestProcessor.this.read(valueFactories, hash, length);
+ }
+
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException {
+ String key = StringUtil.getHexString(hash);
+ writtenKeys.add(key);
+ BasicRequestProcessor.this.write(hash, length, type, value);
+ }
+ }
+
+ protected class SkippedLargeValues implements LargeValues {
+ protected Collection<String> skippedKeys = new HashSet<String>();
+
+ SkippedLargeValues() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return BasicRequestProcessor.this.getMinimumSize();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ String key = StringUtil.getHexString(hash);
+ skippedKeys.add(key);
+ return null;
+ }
+
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
@Immutable
protected static class ActualLocation {
/** The actual location */
@@ -828,6 +1018,16 @@
this.uuid = uuid;
this.childEntity = childEntity;
}
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return this.location.toString() + " (uuid=" + uuid + ") " + childEntity;
+ }
}
protected static class Namespaces {
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -28,8 +28,8 @@
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
+import javax.persistence.Table;
import org.hibernate.annotations.Index;
-import org.hibernate.annotations.Table;
import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
@@ -42,32 +42,38 @@
* @author Randall Hauch
*/
@Entity
-(a)javax.persistence.Table( name = "DNA_BASIC_CHILDREN" )
-@Table( appliesTo = "DNA_BASIC_CHILDREN", indexes = @Index( name = "CHILDINDEX_INX", columnNames = {"PARENT_UUID", "CHILD_INDEX"} ) )
+@Table( name = "DNA_BASIC_CHILDREN" )
+(a)org.hibernate.annotations.Table( appliesTo = "DNA_BASIC_CHILDREN", indexes = {
+ @Index( name = "CHILDINDEX_INX", columnNames = {"PARENT_UUID", "CHILD_INDEX"} ),
+ @Index( name = "CHILDUUID_INX", columnNames = {"CHILD_UUID"} ),
+ @Index( name = "CHILDNAME_INX", columnNames = {"PARENT_UUID", "CHILD_NAME_NS_ID", "CHILD_NAME_LOCAL", "SNS_INDEX"} )} )
@NamedQueries( {
- @NamedQuery( name = "ChildEntity.findByPathSegment", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName AND child.sameNameSiblingIndex = :sns" ),
- @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid" ),
- @NamedQuery( name = "ChildEntity.findByChildUuid", query = "select child from ChildEntity as child where child.id.childUuidString = :childUuidString" ),
- @NamedQuery( name = "ChildEntity.findMaximumSnsIndex", query = "select max(child.sameNameSiblingIndex) from ChildEntity as child where child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName" ),
- @NamedQuery( name = "ChildEntity.findMaximumChildIndex", query = "select max(child.indexInParent) from ChildEntity as child where child.id.parentUuidString = :parentUuid" )} )
+ @NamedQuery( name = "ChildEntity.findByPathSegment", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuidString AND child.childNamespace.id = :ns AND child.childName = :childName AND child.sameNameSiblingIndex = :sns and child.deleted is null" ),
+ @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuidString and child.deleted is null" ),
+ @NamedQuery( name = "ChildEntity.findByChildUuid", query = "select child from ChildEntity as child where child.id.childUuidString = :childUuidString and child.deleted is null" ),
+ @NamedQuery( name = "ChildEntity.findMaximumSnsIndex", query = "select max(child.sameNameSiblingIndex) from ChildEntity as child where child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName and child.deleted is null" ),
+ @NamedQuery( name = "ChildEntity.findMaximumChildIndex", query = "select max(child.indexInParent) from ChildEntity as child where child.id.parentUuidString = :parentUuid and child.deleted is null" )} )
public class ChildEntity {
@Id
private ChildId id;
@Column( name = "CHILD_INDEX", nullable = false, unique = false )
- private Integer indexInParent;
+ private int indexInParent;
@ManyToOne
@JoinColumn( name = "CHILD_NAME_NS_ID", nullable = false )
private NamespaceEntity childNamespace;
- @Column( name = "CHILD_NAME_LOCAL", nullable = true, unique = false, length = 512 )
+ @Column( name = "CHILD_NAME_LOCAL", nullable = false, unique = false, length = 512 )
private String childName;
- @Column( name = "SNS_INDEX", nullable = true, unique = false )
- private Integer sameNameSiblingIndex;
+ @Column( name = "SNS_INDEX", nullable = false, unique = false )
+ private int sameNameSiblingIndex;
+ @Column( name = "DELETED", nullable = true, unique = false )
+ private Boolean deleted;
+
public ChildEntity() {
}
@@ -110,14 +116,14 @@
/**
* @return indexInParent
*/
- public Integer getIndexInParent() {
+ public int getIndexInParent() {
return indexInParent;
}
/**
* @param index Sets indexInParent to the specified value.
*/
- public void setIndexInParent( Integer index ) {
+ public void setIndexInParent( int index ) {
this.indexInParent = index;
}
@@ -152,18 +158,32 @@
/**
* @return sameNameSiblingIndex
*/
- public Integer getSameNameSiblingIndex() {
+ public int getSameNameSiblingIndex() {
return sameNameSiblingIndex;
}
/**
* @param sameNameSiblingIndex Sets sameNameSiblingIndex to the specified value.
*/
- public void setSameNameSiblingIndex( Integer sameNameSiblingIndex ) {
+ public void setSameNameSiblingIndex( int sameNameSiblingIndex ) {
this.sameNameSiblingIndex = sameNameSiblingIndex;
}
/**
+ * @return deleted
+ */
+ public boolean isDeleted() {
+ return Boolean.TRUE.equals(deleted);
+ }
+
+ /**
+ * @param deleted Sets deleted to the specified value.
+ */
+ public void setDeleted( boolean deleted ) {
+ this.deleted = deleted ? Boolean.TRUE : null;
+ }
+
+ /**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
@@ -205,7 +225,7 @@
sb.append('{').append(childNamespace).append("}:");
}
sb.append(childName);
- if (sameNameSiblingIndex != null && sameNameSiblingIndex.intValue() > 1) {
+ if (sameNameSiblingIndex > 1) {
sb.append('[').append(sameNameSiblingIndex).append(']');
}
if (id != null) {
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -31,6 +31,8 @@
import javax.persistence.NamedQuery;
import javax.persistence.NoResultException;
import javax.persistence.Query;
+import javax.persistence.Table;
+import org.hibernate.annotations.Index;
import org.jboss.dna.common.util.CheckArg;
/**
@@ -39,9 +41,11 @@
*
* @author Randall Hauch
*/
-@Entity( name = "DNA_NAMESPACES" )
-@NamedQueries( {@NamedQuery( name = "NamespaceEntity.findAll", query = "SELECT ns FROM DNA_NAMESPACES AS ns" ),
- @NamedQuery( name = "NamespaceEntity.findByUri", query = "SELECT ns FROM DNA_NAMESPACES AS ns WHERE ns.uri = ?1" )} )
+@Entity
+@Table( name = "DNA_NAMESPACES" )
+(a)org.hibernate.annotations.Table( appliesTo = "DNA_NAMESPACES", indexes = @Index( name = "NS_URI_INX", columnNames = {"URI"} ) )
+@NamedQueries( {@NamedQuery( name = "NamespaceEntity.findAll", query = "select ns from NamespaceEntity as ns" ),
+ @NamedQuery( name = "NamespaceEntity.findByUri", query = "select ns from NamespaceEntity as ns where ns.uri = ?1" )} )
public class NamespaceEntity {
@Id
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -29,12 +29,13 @@
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.jboss.dna.common.SystemFailureException;
import org.jboss.dna.common.util.SecureHash;
-import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.connector.store.jpa.models.basic.LargeValueEntity;
import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
@@ -53,17 +54,16 @@
*/
public class Serializer {
+ public static final LargeValues NO_LARGE_VALUES = new NoLargeValues();
+
private final PropertyFactory propertyFactory;
private final ValueFactories valueFactories;
- private final LargeValues largeValues;
private final boolean excludeUuidProperty;
public Serializer( ExecutionContext context,
- LargeValues largeValues,
boolean excludeUuidProperty ) {
this.propertyFactory = context.getPropertyFactory();
this.valueFactories = context.getValueFactories();
- this.largeValues = largeValues;
this.excludeUuidProperty = excludeUuidProperty;
}
@@ -91,6 +91,25 @@
long length ) throws IOException;
}
+ protected static class NoLargeValues implements LargeValues {
+ public long getMinimumSize() {
+ return Long.MAX_VALUE;
+ }
+
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
/**
* Serialize the properties' values to the object stream.
* <p>
@@ -109,21 +128,23 @@
* @param stream the stream where the properties' values are to be serialized; may not be null
* @param number the number of properties exposed by the supplied <code>properties</code> iterator; must be 0 or positive
* @param properties the iterator over the properties that are to be serialized; may not be null
- * @param largeValueHexHashes the collection into which any large value hashes should be recordeed
+ * @param largeValues the interface to use for writing large values; may not be null
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
- * @see #deserializeAllProperties(ObjectInputStream, Collection)
- * @see #serializeProperty(ObjectOutputStream, Property, Collection)
+ * @see #deserializeAllProperties(ObjectInputStream, Collection, LargeValues)
+ * @see #deserializeSomeProperties(ObjectInputStream, Collection, LargeValues, LargeValues, Name...)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues)
*/
public void serializeProperties( ObjectOutputStream stream,
int number,
Iterable<Property> properties,
- Collection<String> largeValueHexHashes ) throws IOException {
+ LargeValues largeValues ) throws IOException {
assert number >= 0;
assert properties != null;
+ assert largeValues != null;
stream.writeInt(number);
for (Property property : properties) {
if (property == null) continue;
- serializeProperty(stream, property, largeValueHexHashes);
+ serializeProperty(stream, property, largeValues);
}
}
@@ -144,17 +165,18 @@
*
* @param stream the stream where the property's values are to be serialized; may not be null
* @param property the property to be serialized; may not be null
- * @param largeValueHexHashes the collection into which any large value hashes should be recordeed
+ * @param largeValues the interface to use for writing large values; may not be null
* @return true if the property was serialized, or false if it was not
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
- * @see #serializeProperties(ObjectOutputStream, int, Iterable, Collection)
- * @see #deserializePropertyValues(ObjectInputStream, Name, boolean)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues)
+ * @see #deserializePropertyValues(ObjectInputStream, Name, boolean, LargeValues, LargeValues)
*/
public boolean serializeProperty( ObjectOutputStream stream,
Property property,
- Collection<String> largeValueHexHashes ) throws IOException {
+ LargeValues largeValues ) throws IOException {
assert stream != null;
assert property != null;
+ assert largeValues != null;
final Name name = property.getName();
if (this.excludeUuidProperty && DnaLexicon.UUID.equals(name)) return false;
// Write the name ...
@@ -172,7 +194,6 @@
stream.write(hash);
stream.writeLong(stringValue.length());
// Now write to the large objects ...
- largeValueHexHashes.add(StringUtil.getHexString(hash));
largeValues.write(computeHash(stringValue), stringValue.length(), PropertyType.STRING, stringValue);
} else {
stream.writeChar('S');
@@ -258,7 +279,6 @@
}
// If this is a large value and the binary has been released, write it to the large objects ...
if (largeValues != null && hash != null) {
- largeValueHexHashes.add(StringUtil.getHexString(hash));
largeValues.write(hash, length, PropertyType.BINARY, value);
}
} else {
@@ -272,26 +292,94 @@
}
/**
+ * Deserialize the existing properties from the supplied input stream, update the properties, and then serialize the updated
+ * properties to the output stream.
+ *
+ * @param input the stream from which the existing properties are to be deserialized; may not be null
+ * @param output the stream to which the updated properties are to be serialized; may not be null
+ * @param updatedProperties the properties that are being updated (or removed, if there are no values); may not be null
+ * @param largeValues the interface to use for writing large values; may not be null
+ * @param removedLargeValues the interface to use for recording the large values that were removed; may not be null
+ * @return the number of properties
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not be found
+ */
+ public int reserializeProperties( ObjectInputStream input,
+ ObjectOutputStream output,
+ Collection<Property> updatedProperties,
+ LargeValues largeValues,
+ LargeValues removedLargeValues ) throws IOException, ClassNotFoundException {
+ assert input != null;
+ assert output != null;
+ assert updatedProperties != null;
+ assert largeValues != null;
+ // Assemble a set of property names to skip deserializing
+ Set<Name> skipNames = new HashSet<Name>();
+ for (Property property : updatedProperties) {
+ skipNames.add(property.getName());
+ }
+ Map<Name, Property> allProperties = new HashMap<Name, Property>();
+
+ // Read the number of properties ...
+ int count = input.readInt();
+ // Deserialize all of the proeprties ...
+ for (int i = 0; i != count; ++i) {
+ // Read the property name ...
+ String nameStr = (String)input.readObject();
+ Name name = valueFactories.getNameFactory().create(nameStr);
+ assert name != null;
+ if (skipNames.contains(name)) {
+ // Deserialized, but don't materialize ...
+ deserializePropertyValues(input, name, true, largeValues, removedLargeValues);
+ } else {
+ // Now read the property values ...
+ Object[] values = deserializePropertyValues(input, name, false, largeValues, removedLargeValues);
+ // Add the property to the collection ...
+ Property property = propertyFactory.create(name, values);
+ assert property != null;
+ allProperties.put(name, property);
+ }
+ }
+
+ // Add all the updated properties ...
+ for (Property updated : updatedProperties) {
+ if (updated.isEmpty()) {
+ allProperties.remove(updated.getName());
+ } else {
+ allProperties.put(updated.getName(), updated);
+ }
+ }
+
+ // Serialize properties ...
+ int numProperties = allProperties.size();
+ output.writeInt(numProperties);
+ for (Property property : allProperties.values()) {
+ if (property == null) continue;
+ serializeProperty(output, property, largeValues);
+ }
+ return numProperties;
+ }
+
+ /**
* Deserialize the serialized properties on the supplied object stream.
*
* @param stream the stream that contains the serialized properties; may not be null
* @param properties the collection into which each deserialized property is to be placed; may not be null
+ * @param largeValues the interface to use for writing large values; may not be null
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
* @throws ClassNotFoundException if the class for the value's object could not be found
- * @see #deserializePropertyValues(ObjectInputStream, Name, boolean)
- * @see #serializeProperties(ObjectOutputStream, int, Iterable, Collection)
+ * @see #deserializePropertyValues(ObjectInputStream, Name, boolean, LargeValues, LargeValues)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues)
*/
public void deserializeAllProperties( ObjectInputStream stream,
- Collection<Property> properties ) throws IOException, ClassNotFoundException {
- assert propertyFactory != null;
- assert valueFactories != null;
+ Collection<Property> properties,
+ LargeValues largeValues ) throws IOException, ClassNotFoundException {
assert stream != null;
assert properties != null;
- assert largeValues != null;
// Read the number of properties ...
int count = stream.readInt();
for (int i = 0; i != count; ++i) {
- Property property = deserializeProperty(stream);
+ Property property = deserializeProperty(stream, largeValues);
assert property != null;
properties.add(property);
}
@@ -303,13 +391,17 @@
* @param stream the stream that contains the serialized properties; may not be null
* @param properties the collection into which each deserialized property is to be placed; may not be null
* @param names the names of the properties that should be deserialized; should not be null or empty
+ * @param largeValues the interface to use for writing large values; may not be null
+ * @param skippedLargeValues the interface to use for recording the large values that were skipped; may not be null
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
* @throws ClassNotFoundException if the class for the value's object could not be found
- * @see #deserializePropertyValues(ObjectInputStream, Name, boolean)
- * @see #serializeProperties(ObjectOutputStream, int, Iterable, Collection)
+ * @see #deserializePropertyValues(ObjectInputStream, Name, boolean, LargeValues, LargeValues)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, LargeValues)
*/
public void deserializeSomeProperties( ObjectInputStream stream,
Collection<Property> properties,
+ LargeValues largeValues,
+ LargeValues skippedLargeValues,
Name... names ) throws IOException, ClassNotFoundException {
assert stream != null;
assert properties != null;
@@ -337,12 +429,17 @@
Name name = valueFactories.getNameFactory().create(nameStr);
assert name != null;
read = name.equals(nameToRead) || (namesToRead != null && namesToRead.contains(namesToRead));
- // Now read the property values ...
- Object[] values = deserializePropertyValues(stream, name, !read);
- // Add the property to the collection ...
- Property property = propertyFactory.create(name, values);
- assert property != null;
- properties.add(property);
+ if (read) {
+ // Now read the property values ...
+ Object[] values = deserializePropertyValues(stream, name, false, skippedLargeValues, skippedLargeValues);
+ // Add the property to the collection ...
+ Property property = propertyFactory.create(name, values);
+ assert property != null;
+ properties.add(property);
+ } else {
+ // Skip the property ...
+ deserializePropertyValues(stream, name, true, largeValues, skippedLargeValues);
+ }
}
}
@@ -350,19 +447,21 @@
* Deserialize the serialized property on the supplied object stream.
*
* @param stream the stream that contains the serialized properties; may not be null
- * @return the deserialized property values, or an empty list if there are no values
+ * @param largeValues the interface to use for writing large values; may not be null
+ * @return the deserialized property; never null
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
* @throws ClassNotFoundException if the class for the value's object could not be found
- * @see #deserializeAllProperties(ObjectInputStream, Collection)
- * @see #serializeProperty(ObjectOutputStream, Property, Collection)
+ * @see #deserializeAllProperties(ObjectInputStream, Collection, LargeValues)
+ * @see #serializeProperty(ObjectOutputStream, Property, LargeValues)
*/
- public Property deserializeProperty( ObjectInputStream stream ) throws IOException, ClassNotFoundException {
+ public Property deserializeProperty( ObjectInputStream stream,
+ LargeValues largeValues ) throws IOException, ClassNotFoundException {
// Read the name ...
String nameStr = (String)stream.readObject();
Name name = valueFactories.getNameFactory().create(nameStr);
assert name != null;
// Now read the property values ...
- Object[] values = deserializePropertyValues(stream, name, false);
+ Object[] values = deserializePropertyValues(stream, name, false, largeValues, largeValues);
// Add the property to the collection ...
return propertyFactory.create(name, values);
}
@@ -373,15 +472,19 @@
* @param stream the stream that contains the serialized properties; may not be null
* @param propertyName the name of the property being deserialized
* @param skip true if the values don't need to be read, or false if they are to be read
+ * @param largeValues the interface to use for writing large values; may not be null
+ * @param skippedLargeValues the interface to use for recording the large values that were skipped; may not be null
* @return the deserialized property values, or an empty list if there are no values
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
* @throws ClassNotFoundException if the class for the value's object could not be found
- * @see #deserializeAllProperties(ObjectInputStream, Collection)
- * @see #serializeProperty(ObjectOutputStream, Property, Collection)
+ * @see #deserializeAllProperties(ObjectInputStream, Collection, LargeValues)
+ * @see #serializeProperty(ObjectOutputStream, Property, LargeValues)
*/
public Object[] deserializePropertyValues( ObjectInputStream stream,
Name propertyName,
- boolean skip ) throws IOException, ClassNotFoundException {
+ boolean skip,
+ LargeValues largeValues,
+ LargeValues skippedLargeValues ) throws IOException, ClassNotFoundException {
assert stream != null;
assert propertyName != null;
// Read the number of values ...
@@ -478,7 +581,11 @@
stream.read(hash);
// Read the length of the content ...
long length = stream.readLong();
- if (!skip) value = largeValues.read(valueFactories, hash, length);
+ if (skip) {
+ skippedLargeValues.read(valueFactories, hash, length);
+ } else {
+ value = largeValues.read(valueFactories, hash, length);
+ }
break;
default:
// All other objects ...
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -21,20 +21,40 @@
*/
package org.jboss.dna.connector.store.jpa;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChildren;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasNoChildren;
+import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
+import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import java.util.UUID;
+import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.stats.Stopwatch;
import org.jboss.dna.connector.store.jpa.models.basic.BasicModel;
import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
+ * Test the JpaConnection class using a {@link #getModel() model} for the entire set of tests. To run these same methods using a
+ * different {@link Model}, subclass this class and override the {@link #getModel()} method to return the desired Model instance.
+ *
* @author Randall Hauch
*/
public class JpaConnectionTest {
@@ -48,11 +68,12 @@
private UUID rootNodeUuid;
private long largeValueSize;
private boolean compressData;
+ private Graph graph;
@Before
public void beforeEach() throws Exception {
context = new BasicExecutionContext();
- model = new BasicModel();
+ model = getModel();
rootNodeUuid = UUID.randomUUID();
largeValueSize = 2 ^ 10; // 1 kilobyte
compressData = false;
@@ -65,7 +86,7 @@
configurator.setProperty("hibernate.connection.username", "sa");
configurator.setProperty("hibernate.connection.password", "");
configurator.setProperty("hibernate.connection.url", "jdbc:hsqldb:.");
- configurator.setProperty("hibernate.show_sql", "true");
+ configurator.setProperty("hibernate.show_sql", "false");
configurator.setProperty("hibernate.format_sql", "true");
configurator.setProperty("hibernate.use_sql_comments", "true");
configurator.setProperty("hibernate.hbm2ddl.auto", "create");
@@ -75,27 +96,335 @@
// Create the connection ...
cachePolicy = mock(CachePolicy.class);
connection = new JpaConnection("source", cachePolicy, manager, model, rootNodeUuid, largeValueSize, compressData);
+
+ // And create the graph ...
+ graph = Graph.create(connection, context);
}
@After
public void afterEach() throws Exception {
try {
- if (manager != null) manager.close();
+ if (connection != null) connection.close();
} finally {
- manager = null;
- if (factory != null) {
- try {
- factory.close();
- } finally {
- factory = null;
+ try {
+ if (manager != null) manager.close();
+ } finally {
+ manager = null;
+ if (factory != null) {
+ try {
+ factory.close();
+ } finally {
+ factory = null;
+ }
}
}
}
}
+ /**
+ * Override this method in subclasses to create test cases that test other models.
+ *
+ * @return the model that should be used in the test
+ */
+ protected Model getModel() {
+ return new BasicModel();
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Path.Segment child( String name ) {
+ return context.getValueFactories().getPathFactory().createSegment(name);
+ }
+
@Test
- public void shouldAlwaysReadRootNode() {
+ public void shouldAlwaysReadRootNodeByPath() {
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root.getProperty(DnaLexicon.UUID).getFirstValue(), is((Object)rootNodeUuid));
+ assertThat(root.getLocation().getPath(), is(path("/")));
+ assertThat(root.getLocation().getUuid(), is(rootNodeUuid));
+ }
+ @Test
+ public void shouldAlwaysReadRootNodeByUuid() {
+ Location location = new Location(rootNodeUuid);
+ Node root = graph.getNodeAt(location);
+ assertThat(root, is(notNullValue()));
+ assertThat(root.getProperty(DnaLexicon.UUID).getFirstValue(), is((Object)rootNodeUuid));
+ assertThat(root.getLocation().getPath(), is(path("/")));
+ assertThat(root.getLocation().getUuid(), is(rootNodeUuid));
}
+ @Test
+ public void shouldSetPropertyOnRootNode() {
+ graph.set("propA").to("valueA").on("/");
+ // Now look up the node ...
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root, hasProperty(DnaLexicon.UUID, rootNodeUuid));
+ assertThat(root, hasProperty("propA", "valueA"));
+ }
+
+ @Test
+ public void shouldAddChildUnderRootNode() {
+ graph.batch().create("/a").with("propB", "valueB").and("propC", "valueC").execute();
+ // Now look up the root node ...
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root, hasProperty(DnaLexicon.UUID, rootNodeUuid));
+ assertThat(root, hasChild(child("a")));
+
+ // Now look up node A ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ assertThat(nodeA, hasProperty("propB", "valueB"));
+ assertThat(nodeA, hasProperty("propC", "valueC"));
+ assertThat(nodeA, hasNoChildren());
+ }
+
+ @Test
+ public void shouldAddChildrenOnRootNode() {
+ graph.batch().set("propA").to("valueA").on("/").and().create("/a").with("propB", "valueB").and("propC", "valueC").and()
+ .create("/b").with("propD", "valueD").and("propE", "valueE").execute();
+ // Now look up the root node ...
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root, hasProperty(DnaLexicon.UUID, rootNodeUuid));
+ assertThat(root, hasProperty("propA", "valueA"));
+ assertThat(root, hasChildren(child("a"), child("b")));
+
+ // Now look up node A ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ assertThat(nodeA, hasProperty("propB", "valueB"));
+ assertThat(nodeA, hasProperty("propC", "valueC"));
+ assertThat(nodeA, hasNoChildren());
+
+ // Now look up node B ...
+ Node nodeB = graph.getNodeAt("/b");
+ assertThat(nodeB, is(notNullValue()));
+ assertThat(nodeB, hasProperty("propD", "valueD"));
+ assertThat(nodeB, hasProperty("propE", "valueE"));
+ assertThat(nodeB, hasNoChildren());
+
+ // Get the subgraph ...
+ Subgraph subgraph = graph.getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode("."), hasProperty(DnaLexicon.UUID, rootNodeUuid));
+ assertThat(subgraph.getNode("."), hasProperty("propA", "valueA"));
+ assertThat(subgraph.getNode("."), hasChildren(child("a"), child("b")));
+ assertThat(subgraph.getNode("a"), is(notNullValue()));
+ assertThat(subgraph.getNode("a"), hasProperty("propB", "valueB"));
+ assertThat(subgraph.getNode("a"), hasProperty("propC", "valueC"));
+ assertThat(subgraph.getNode("a"), hasNoChildren());
+ assertThat(subgraph.getNode("b"), is(notNullValue()));
+ assertThat(subgraph.getNode("b"), hasProperty("propD", "valueD"));
+ assertThat(subgraph.getNode("b"), hasProperty("propE", "valueE"));
+ assertThat(subgraph.getNode("b"), hasNoChildren());
+ }
+
+ @Test
+ public void shouldStoreManyPropertiesOnANode() {
+ Graph.Create<Graph.Batch> create = graph.batch().create("/a");
+ for (int i = 0; i != 100; ++i) {
+ create = create.with("property" + i, "value" + i);
+ }
+ create.execute();
+ // Now look up all the properties ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ for (int i = 0; i != 100; ++i) {
+ assertThat(nodeA, hasProperty("property" + i, "value" + i));
+ }
+ assertThat(nodeA, hasNoChildren());
+ }
+
+ @Test
+ public void shouldGetOnePropertyOnNode() {
+ Graph.Create<Graph.Batch> create = graph.batch().create("/a");
+ for (int i = 0; i != 100; ++i) {
+ create = create.with("property" + i, "value" + i);
+ }
+ create.execute();
+ // Now get a single property ...
+ Property p = graph.getProperty("property75").on("/a");
+ assertThat(p, is(notNullValue()));
+ assertThat(p.size(), is(1));
+ assertThat(p.isSingle(), is(true));
+ assertThat(p.getFirstValue().toString(), is("value75"));
+ }
+
+ @Test
+ public void shouldCalculateNumberOfNodesInTreeCorrectly() {
+ assertThat(numberNodesInTree(2, 2), is(7));
+ assertThat(numberNodesInTree(2, 3), is(15));
+ assertThat(numberNodesInTree(2, 4), is(31));
+ assertThat(numberNodesInTree(3, 2), is(13));
+ assertThat(numberNodesInTree(3, 3), is(40));
+ assertThat(numberNodesInTree(3, 4), is(121));
+ assertThat(numberNodesInTree(3, 5), is(364));
+ assertThat(numberNodesInTree(3, 6), is(1093));
+ assertThat(numberNodesInTree(3, 7), is(3280));
+ assertThat(numberNodesInTree(3, 8), is(9841));
+ assertThat(numberNodesInTree(3, 9), is(29524));
+ assertThat(numberNodesInTree(4, 2), is(21));
+ assertThat(numberNodesInTree(4, 3), is(85));
+ assertThat(numberNodesInTree(4, 4), is(341));
+ assertThat(numberNodesInTree(4, 5), is(1365));
+ assertThat(numberNodesInTree(4, 6), is(5461));
+ assertThat(numberNodesInTree(4, 7), is(21845));
+ assertThat(numberNodesInTree(5, 3), is(156));
+ assertThat(numberNodesInTree(5, 4), is(781));
+ assertThat(numberNodesInTree(5, 5), is(3906));
+ assertThat(numberNodesInTree(7, 3), is(400));
+ assertThat(numberNodesInTree(7, 4), is(2801));
+ assertThat(numberNodesInTree(7, 5), is(19608));
+ assertThat(numberNodesInTree(8, 3), is(585));
+ assertThat(numberNodesInTree(8, 4), is(4681));
+ assertThat(numberNodesInTree(8, 5), is(37449));
+ assertThat(numberNodesInTree(8, 6), is(299593));
+ assertThat(numberNodesInTree(8, 7), is(2396745));
+ assertThat(numberNodesInTree(10, 2), is(111));
+ assertThat(numberNodesInTree(10, 3), is(1111));
+ assertThat(numberNodesInTree(10, 4), is(11111));
+ assertThat(numberNodesInTree(100, 1), is(101));
+ assertThat(numberNodesInTree(200, 1), is(201));
+ assertThat(numberNodesInTree(200, 2), is(40201));
+ assertThat(numberNodesInTree(200, 3), is(8040201));
+ assertThat(numberNodesInTree(1000, 1), is(1001));
+ assertThat(numberNodesInTree(3000, 1), is(3001));
+ }
+
+ @Test
+ public void shouldCreateDeepBranch() {
+ createTree("", 1, 50, "deep and narrow tree, 1x50", false);
+ }
+
+ // @Test
+ // public void shouldCreateBinaryTree() {
+ // createTree("", 2, 8, "binary tree, 2x8", false);
+ // }
+
+ @Test
+ public void shouldCreate10x2Tree() {
+ createTree("", 10, 2, null, false);
+ }
+
+ // @Test
+ // public void shouldCreate10x3DecimalTree() {
+ // createTree("", 10, 3, null, false);
+ // }
+
+ // @Test
+ // public void shouldCreateWideTree() {
+ // createTree("", 1000, 1, "wide/flat tree, 1000x1", false);
+ // }
+
+ // @Test
+ // public void shouldCreateVeryWideTree() {
+ // createTree("", 3000, 1, "wide/flat tree, 3000x1", false);
+ // }
+
+ // @Test
+ // public void shouldCreate10KDecimalTree() {
+ // createTree("", 10, 4, "10K decimal tree, 10x4", false);
+ // }
+
+ // @Test
+ // public void shouldCreate8x4Tree() {
+ // createTree("", 8, 4, null, false);
+ // }
+
+ protected int createTree( String initialPath,
+ int numberPerDepth,
+ int depth,
+ String description,
+ boolean oneBatch ) {
+ int totalNumber = numberNodesInTree(numberPerDepth, depth) - 1; // this method doesn't create the root
+ if (description == null) description = "" + numberPerDepth + "x" + depth + " tree";
+ boolean printIntermediate = totalNumber > 500;
+ System.out.println(description + " (" + totalNumber + " nodes):");
+ int totalNumberCreated = 0;
+ Graph.Batch batch = oneBatch ? graph.batch() : null;
+ Stopwatch sw = new Stopwatch();
+ if (batch != null) {
+ totalNumberCreated += createChildren(batch, initialPath, "node", numberPerDepth, depth, printIntermediate);
+ sw.start();
+ batch.execute();
+ } else {
+ sw.start();
+ totalNumberCreated += createChildren(null, initialPath, "node", numberPerDepth, depth, printIntermediate);
+ }
+ sw.stop();
+ long totalDurationInMillis = TimeUnit.NANOSECONDS.toMillis(sw.getTotalDuration().longValue());
+ long avgDurationInMillis = totalDurationInMillis / totalNumber;
+ System.out.println(" Total = " + sw.getTotalDuration() + "; avg = " + avgDurationInMillis + " ms");
+
+ // Perform second batch ...
+ batch = graph.batch();
+ totalNumberCreated += createChildren(batch, initialPath, "secondBranch", 2, 2, printIntermediate);
+ sw = new Stopwatch();
+ sw.start();
+ batch.execute();
+ sw.stop();
+ System.out.println(" Final batch total = " + sw.getTotalDuration() + "; avg = " + avgDurationInMillis + " ms");
+ assertThat(totalNumberCreated, is(totalNumber + numberNodesInTree(2, 2) - 1));
+ return totalNumberCreated;
+ }
+
+ protected int createChildren( Graph.Batch useBatch,
+ String parentPath,
+ String nodePrefix,
+ int number,
+ int depthRemaining,
+ boolean printIntermediateStatus ) {
+ int numberCreated = 0;
+ Graph.Batch batch = useBatch;
+ if (batch == null) batch = graph.batch();
+ for (int i = 0; i != number; ++i) {
+ String path = parentPath + "/" + nodePrefix + i;
+ Graph.Create<Graph.Batch> create = batch.create(path);
+ String value = "the quick brown fox jumped over the moon. What? ";
+ for (int j = 0; j != 10; ++j) {
+ // value = value + value;
+ create = create.with("property" + j, value);
+ }
+ create.and();
+ }
+ numberCreated += number;
+ if (useBatch == null) {
+ batch.execute();
+ if (printIntermediateStatus) {
+ System.out.println(" total created ... " + numberCreated);
+ }
+ }
+ if (depthRemaining > 1) {
+ for (int i = 0; i != number; ++i) {
+ String path = parentPath + "/" + nodePrefix + i;
+ numberCreated += createChildren(useBatch, path, nodePrefix, number, depthRemaining - 1, false);
+ if (printIntermediateStatus) {
+ System.out.println(" total created ... " + numberCreated);
+ }
+ }
+ }
+ return numberCreated;
+ }
+
+ protected int numberNodesInTree( int numberPerDepth,
+ int depth ) {
+ assert depth > 0;
+ assert numberPerDepth > 0;
+ int totalNumber = 0;
+ for (int i = 0; i <= depth; ++i) {
+ totalNumber += (int)Math.pow(numberPerDepth, i);
+ }
+ return totalNumber;
+ }
+
}
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -24,7 +24,6 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.hamcrest.core.IsNull.notNullValue;
-import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
@@ -334,13 +333,13 @@
assertThat(child1a.getIndexInParent(), is(1));
assertThat(child1a.getChildName(), is("child1"));
assertThat(child1a.getChildNamespace(), is(ns));
- assertThat(child1a.getSameNameSiblingIndex(), is(nullValue()));
+ assertThat(child1a.getSameNameSiblingIndex(), is(0));
assertThat(child2a.getId(), is(childId2));
assertThat(child2a.getIndexInParent(), is(2));
assertThat(child2a.getChildName(), is("child2"));
assertThat(child2a.getChildNamespace(), is(ns));
- assertThat(child2a.getSameNameSiblingIndex(), is(nullValue()));
+ assertThat(child2a.getSameNameSiblingIndex(), is(0));
assertThat(child3a.getId(), is(childId3));
assertThat(child3a.getIndexInParent(), is(3));
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java 2008-11-26 16:42:57 UTC (rev 650)
@@ -22,6 +22,7 @@
package org.jboss.dna.connector.store.jpa.util;
import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
import java.io.ByteArrayInputStream;
@@ -29,10 +30,12 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -45,6 +48,7 @@
import org.jboss.dna.graph.BasicExecutionContext;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.Name;
import org.jboss.dna.graph.properties.Property;
import org.jboss.dna.graph.properties.PropertyFactory;
import org.jboss.dna.graph.properties.PropertyType;
@@ -62,16 +66,14 @@
private LargeValuesHolder largeValues;
private PropertyFactory propertyFactory;
private ValueFactories valueFactories;
- private Set<String> largeValueHexHashes;
@Before
public void beforeEach() {
context = new BasicExecutionContext();
propertyFactory = context.getPropertyFactory();
valueFactories = context.getValueFactories();
+ serializer = new Serializer(context, false);
largeValues = new LargeValuesHolder();
- largeValueHexHashes = new HashSet<String>();
- serializer = new Serializer(context, largeValues, false);
}
@Test
@@ -79,7 +81,6 @@
Property prop = createProperty("p1", new Long(1));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -87,7 +88,6 @@
Property prop = createProperty("p1", new Integer(1));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -95,7 +95,6 @@
Property prop = createProperty("p1", new Short((short)1));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -103,7 +102,6 @@
Property prop = createProperty("p1", new Float(1.0f));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -111,7 +109,6 @@
Property prop = createProperty("p1", new Double(1.0d));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -119,7 +116,6 @@
Property prop = createProperty("p1", new Boolean(true));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -127,7 +123,6 @@
Property prop = createProperty("p1", valueFactories.getNameFactory().create("something"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -135,7 +130,6 @@
Property prop = createProperty("p1", valueFactories.getPathFactory().create("/a/b/c/something"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -143,12 +137,10 @@
Property prop = createProperty("p1", valueFactories.getDateFactory().createUtc());
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
prop = createProperty("p1", valueFactories.getDateFactory().create());
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -156,7 +148,6 @@
Property prop = createProperty("p1", UUID.randomUUID());
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -164,7 +155,6 @@
Property prop = createProperty("p1", new URI("http://example.com"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -173,7 +163,6 @@
Property prop = createProperty("p1", valueFactories.getReferenceFactory().create(uuid.toString()));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -181,7 +170,6 @@
Property prop = createProperty("p1", valueFactories.getDecimalFactory().create("1.0123455243284347375478525485466895512"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -190,7 +178,6 @@
Property prop = createProperty("p1", valueFactories.getBinaryFactory().create(value));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -199,7 +186,6 @@
Property prop = createProperty("p1", valueFactories.getBinaryFactory().create(value));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(1));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -207,7 +193,6 @@
Property prop = createProperty("p1", "v1");
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -217,38 +202,66 @@
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(1));
assertThat(largeValues.get(value).value, is((Object)value));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
public void shouldSerializeAndDeserializeSmallAndLargeStringProperty() throws Exception {
Property prop1 = createProperty("p1", "v1");
String value = "v234567890123456789012345678901234567890";
- Property prop2 = createProperty("p1", value);
- Property prop3 = createProperty("p1", "v2");
- Property prop4 = createProperty("p1", new String(value)); // make sure it's a different String object
+ Property prop2 = createProperty("p2", value);
+ Property prop3 = createProperty("p3", "v2");
+ Property prop4 = createProperty("p4", new String(value)); // make sure it's a different String object
assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4);
assertThat(largeValues.getCount(), is(1));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
public void shouldSerializeAndDeserializeMixtureOfSmallAndLargeProperties() throws Exception {
Property prop1 = createProperty("p1", "v1");
String value = "v234567890123456789012345678901234567890";
- Property prop2 = createProperty("p1", value);
- Property prop3 = createProperty("p1", "v2");
- Property prop4 = createProperty("p1", new String(value)); // make sure it's a different String object
- Property prop5 = createProperty("p1", valueFactories.getBinaryFactory().create("something"));
+ Property prop2 = createProperty("p2", value);
+ Property prop3 = createProperty("p3", "v2");
+ Property prop4 = createProperty("p4", new String(value)); // make sure it's a different String object
+ Property prop5 = createProperty("p5", valueFactories.getBinaryFactory().create("something"));
String binaryValue = "really really long string that will be converted to a binary value and tested like that";
- Property prop6 = createProperty("p1", valueFactories.getBinaryFactory().create(binaryValue));
+ Property prop6 = createProperty("p6", valueFactories.getBinaryFactory().create(binaryValue));
assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4, prop5, prop6);
assertThat(largeValues.getCount(), is(2));
- assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
+ @Test
+ public void shouldReserializePropertiesWithUpdates() throws Exception {
+ Property prop1 = createProperty("p1", "v1");
+ String value = "v234567890123456789012345678901234567890";
+ Property prop2 = createProperty("p2", value);
+ Property prop3 = createProperty("p3", "v2");
+ Property prop4 = createProperty("p4", new String(value)); // make sure it's a different String object
+ Property prop5 = createProperty("p5", valueFactories.getBinaryFactory().create("something"));
+ String binaryValueStr = "really really long string that will be converted to a binary value and tested like that";
+ Binary binaryValue = valueFactories.getBinaryFactory().create(binaryValueStr);
+ Property prop6 = createProperty("p6", binaryValue);
+
+ Property prop2b = createProperty("p2");
+ Property prop3b = createProperty("p3", "v3");
+ String binaryValueStr2 = binaryValueStr + " but modified";
+ Binary binaryValue2 = valueFactories.getBinaryFactory().create(binaryValueStr2);
+ Property prop6b = createProperty("p6", binaryValue2);
+
+ Property[] initial = new Property[] {prop1, prop2, prop3, prop4, prop5, prop6};
+ Property[] updated = new Property[] {prop2b, prop3b, prop6b};
+ SkippedLargeValues removedLargeValues = new SkippedLargeValues();
+ assertReserializable(serializer, removedLargeValues, initial, updated);
+
+ assertThat(largeValues.getCount(), is(3));
+ assertThat(removedLargeValues.getCount(), is(2)); // p2's value and p6's original value
+ assertThat(largeValues.get(serializer.computeHash(value)), is(notNullValue()));
+ assertThat(largeValues.get(binaryValue2), is(notNullValue()));
+ assertThat(largeValues.get(binaryValue2), is(notNullValue()));
+ assertThat(removedLargeValues.isSkipped(binaryValue), is(true));
+ }
+
protected Property createProperty( String name,
Object... values ) {
return propertyFactory.create(valueFactories.getNameFactory().create(name), values);
@@ -261,7 +274,7 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
- serializer.serializeProperty(oos, property, largeValueHexHashes);
+ serializer.serializeProperty(oos, property, largeValues);
} finally {
oos.close();
}
@@ -272,7 +285,7 @@
ObjectInputStream ois = new ObjectInputStream(bais);
Property copy = null;
try {
- copy = serializer.deserializeProperty(ois);
+ copy = serializer.deserializeProperty(ois, largeValues);
} finally {
ois.close();
}
@@ -288,7 +301,7 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
- serializer.serializeProperties(oos, propertyList.size(), propertyList, largeValueHexHashes);
+ serializer.serializeProperties(oos, propertyList.size(), propertyList, largeValues);
} finally {
oos.close();
}
@@ -298,7 +311,7 @@
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
try {
- serializer.deserializeAllProperties(ois, outputProperties);
+ serializer.deserializeAllProperties(ois, outputProperties, largeValues);
} finally {
ois.close();
}
@@ -308,6 +321,126 @@
assertThat(outputProperties, hasItems(propertyList.toArray(new Property[propertyList.size()])));
}
+ protected void assertReserializable( Serializer serializer,
+ Serializer.LargeValues removedLargeValues,
+ Property[] originalProperties,
+ Property... updatedProperties ) throws IOException, ClassNotFoundException {
+ Collection<Name> propertiesThatStay = new HashSet<Name>();
+ Collection<Name> propertiesThatAreDeleted = new HashSet<Name>();
+ for (Property prop : originalProperties) {
+ propertiesThatStay.add(prop.getName());
+ }
+ for (Property prop : updatedProperties) {
+ if (prop.isEmpty()) {
+ propertiesThatAreDeleted.add(prop.getName());
+ propertiesThatStay.remove(prop.getName());
+ } else {
+ propertiesThatStay.add(prop.getName());
+ }
+ }
+
+ // Serialize the properties one at a time ...
+ Collection<Property> initialProps = Arrays.asList(originalProperties);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ try {
+ serializer.serializeProperties(oos, initialProps.size(), initialProps, largeValues);
+ } finally {
+ oos.close();
+ }
+ byte[] bytes = baos.toByteArray();
+
+ // Now reserialize, updating the properties ...
+ Collection<Property> updatedProps = Arrays.asList(updatedProperties);
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ baos = new ByteArrayOutputStream();
+ oos = new ObjectOutputStream(baos);
+ try {
+ serializer.reserializeProperties(ois, oos, updatedProps, largeValues, removedLargeValues);
+ } finally {
+ oos.close();
+ ois.close();
+ }
+
+ // Deserialize ...
+ List<Property> afterProperties = new ArrayList<Property>();
+ bais = new ByteArrayInputStream(baos.toByteArray());
+ ois = new ObjectInputStream(bais);
+ try {
+ serializer.deserializeAllProperties(ois, afterProperties, largeValues);
+ } finally {
+ ois.close();
+ }
+ Collection<Name> namesAfter = new HashSet<Name>();
+ for (Property prop : afterProperties) {
+ namesAfter.add(prop.getName());
+ }
+
+ // Check the properties match ...
+ assertThat(afterProperties.size(), is(propertiesThatStay.size()));
+ assertThat(namesAfter, is(propertiesThatStay));
+ for (Name deleted : propertiesThatAreDeleted) {
+ assertThat(namesAfter.contains(deleted), is(false));
+ }
+ }
+
+ protected class SkippedLargeValues implements Serializer.LargeValues {
+ private int minimumSize = 20;
+ private Set<String> skippedKeys = new HashSet<String>();
+
+ public SkippedLargeValues() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return minimumSize;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ String key = StringUtil.getHexString(hash);
+ return skippedKeys.add(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#write(byte[], long,
+ * org.jboss.dna.graph.properties.PropertyType, java.lang.Object)
+ */
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isSkipped( Binary binary ) throws UnsupportedEncodingException {
+ String key = StringUtil.getHexString(binary.getHash());
+ return isSkipped(key);
+ }
+
+ public boolean isSkipped( String key ) {
+ return skippedKeys.contains(key);
+ }
+
+ public int getCount() {
+ return skippedKeys.size();
+ }
+ }
+
protected class LargeValuesHolder implements Serializer.LargeValues {
private int minimumSize = 20;
private final Map<String, LargeValue> largeValuesByHexHash = new HashMap<String, LargeValue>();
Modified: trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties 2008-11-26 16:42:23 UTC (rev 649)
+++ trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties 2008-11-26 16:42:57 UTC (rev 650)
@@ -9,6 +9,8 @@
# Set up the default logging to be INFO level, then override specific units
log4j.logger.org.jboss.dna=INFO
+log4j.logger.org.jboss.dna.connector.store.jpa=INFO
+
# Hibernate
log4j.logger.org.hibernate=ERROR
# C3P0
17 years, 1 month
DNA SVN: r649 - in trunk/dna-graph/src: main/java/org/jboss/dna/graph/requests and 1 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-11-26 11:42:23 -0500 (Wed, 26 Nov 2008)
New Revision: 649
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithChildren.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithProperty.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
Log:
DNA-40 Persistant storage for information not stored in other repository sources
Improved functionality and testing, including some performance testing of creating 100s and 1000s of nodes. (These tests are commented out due to the time required to run them.)
Also made minor improvements to the Graph API. Specifically, added the ability to get the UUID out of a Location, changed the CreateNodeRequest.toString() to be more readable, and corrected the interface returned from Graph.create(...) and Graph.Batch.create(...) methods (previously required two .and() calls).
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-11-25 19:31:22 UTC (rev 648)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-11-26 16:42:23 UTC (rev 649)
@@ -89,6 +89,27 @@
return new Graph(sourceName, connectionFactory, context);
}
+ /**
+ * Create a graph instance that uses the supplied {@link RepositoryConnection} and {@link ExecutionContext context}.
+ *
+ * @param connection the connection that should be used
+ * @param context the context in which all executions should be performed
+ * @return the new graph
+ * @throws IllegalArgumentException if the connection or context parameters are null
+ */
+ public static Graph create( final RepositoryConnection connection,
+ ExecutionContext context ) {
+ CheckArg.isNotNull(connection, "connection");
+ final String connectorSourceName = connection.getSourceName();
+ RepositoryConnectionFactory connectionFactory = new RepositoryConnectionFactory() {
+ public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
+ if (connectorSourceName.equals(sourceName)) return connection;
+ return null;
+ }
+ };
+ return new Graph(connectorSourceName, connectionFactory, context);
+ }
+
private final String sourceName;
private final RepositoryConnectionFactory connectionFactory;
private final ExecutionContext context;
@@ -659,6 +680,46 @@
}
/**
+ * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
+ * value(s) and the location of the node onto which the property should be set.
+ *
+ * @param propertyName the property name
+ * @return the interface used to specify the values
+ */
+ public SetValuesTo<On<Conjunction<Graph>>> set( String propertyName ) {
+ Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
+ return set(name);
+ }
+
+ /**
+ * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
+ * value(s) and the location of the node onto which the property should be set.
+ *
+ * @param propertyName the property name
+ * @return the interface used to specify the values
+ */
+ public SetValuesTo<On<Conjunction<Graph>>> set( final Name propertyName ) {
+ return new SetValuesTo<On<Conjunction<Graph>>>() {
+ public On<Conjunction<Graph>> to( Object value ) {
+ return set(getContext().getPropertyFactory().create(propertyName, value));
+ }
+
+ public On<Conjunction<Graph>> to( Object firstValue,
+ Object... otherValues ) {
+ return set(getContext().getPropertyFactory().create(propertyName, firstValue, otherValues));
+ }
+
+ public On<Conjunction<Graph>> to( Iterable<?> values ) {
+ return set(getContext().getPropertyFactory().create(propertyName, values));
+ }
+
+ public On<Conjunction<Graph>> to( Iterator<?> values ) {
+ return set(getContext().getPropertyFactory().create(propertyName, values));
+ }
+ };
+ }
+
+ /**
* Remove properties from the node at the given location.
*
* @param propertyNames the names of the properties to be removed
@@ -1594,12 +1655,12 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( String atPath ) {
+ public Create<Batch> create( String atPath ) {
assertNotExecuted();
Path at = createPath(atPath);
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name);
+ return new CreateAction<Batch>(this, requestQueue, new Location(parent), name);
}
/**
@@ -1614,13 +1675,13 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( String atPath,
- Property property ) {
+ public Create<Batch> create( String atPath,
+ Property property ) {
assertNotExecuted();
Path at = createPath(atPath);
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(property);
+ return new CreateAction<Batch>(this, requestQueue, new Location(parent), name).with(property);
}
/**
@@ -1636,15 +1697,15 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( String atPath,
- Property firstProperty,
- Property... additionalProperties ) {
+ public Create<Batch> create( String atPath,
+ Property firstProperty,
+ Property... additionalProperties ) {
assertNotExecuted();
Path at = createPath(atPath);
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(firstProperty,
- additionalProperties);
+ return new CreateAction<Batch>(this, requestQueue, new Location(parent), name).with(firstProperty,
+ additionalProperties);
}
/**
@@ -1658,12 +1719,12 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( Path at ) {
+ public Create<Batch> create( Path at ) {
assertNotExecuted();
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name);
+ return new CreateAction<Batch>(this, requestQueue, new Location(parent), name);
}
/**
@@ -1678,14 +1739,13 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( Path at,
- Iterable<Property> properties ) {
+ public Create<Batch> create( Path at,
+ Iterable<Property> properties ) {
assertNotExecuted();
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- CreateAction<BatchConjunction> action = new CreateAction<BatchConjunction>(nextRequests, requestQueue,
- new Location(parent), name);
+ CreateAction<Batch> action = new CreateAction<Batch>(this, requestQueue, new Location(parent), name);
for (Property property : properties) {
action.and(property);
}
@@ -1704,13 +1764,13 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( Path at,
- Property property ) {
+ public Create<Batch> create( Path at,
+ Property property ) {
assertNotExecuted();
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(property);
+ return new CreateAction<Batch>(this, requestQueue, new Location(parent), name).with(property);
}
/**
@@ -1726,15 +1786,15 @@
* @return the object that can be used to specify addition properties for the new node to be copied or the location of the
* node where the node is to be created
*/
- public Create<BatchConjunction> create( Path at,
- Property firstProperty,
- Property... additionalProperties ) {
+ public Create<Batch> create( Path at,
+ Property firstProperty,
+ Property... additionalProperties ) {
assertNotExecuted();
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(firstProperty,
- additionalProperties);
+ return new CreateAction<Batch>(this, requestQueue, new Location(parent), name).with(firstProperty,
+ additionalProperties);
}
/**
@@ -1744,16 +1804,16 @@
* @param parent the location of the parent
* @return the object used to start creating a node
*/
- public CreateNodeNamed<BatchConjunction> createUnder( Location parent ) {
+ public CreateNodeNamed<Batch> createUnder( Location parent ) {
CheckArg.isNotNull(parent, "parent");
- return new CreateNodeNamedAction<BatchConjunction>(nextRequests, requestQueue, parent);
+ return new CreateNodeNamedAction<Batch>(this, requestQueue, parent);
}
/**
* Set the properties on a node.
*
* @param properties the properties to set
- * @return the remove request object that should be used to specify the node on which the properties are to be set.
+ * @return the interface that should be used to specify the node on which the properties are to be set.
*/
public On<BatchConjunction> set( final Property... properties ) {
return new On<BatchConjunction>() {
@@ -1787,6 +1847,46 @@
}
/**
+ * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
+ * value(s) and the location of the node onto which the property should be set.
+ *
+ * @param propertyName the property name
+ * @return the interface used to specify the values
+ */
+ public SetValuesTo<On<BatchConjunction>> set( String propertyName ) {
+ Name name = getContext().getValueFactories().getNameFactory().create(propertyName);
+ return set(name);
+ }
+
+ /**
+ * Set a property on a node, starting with the name. The interface returned from this method should be used to specify the
+ * value(s) and the location of the node onto which the property should be set.
+ *
+ * @param propertyName the property name
+ * @return the interface used to specify the values
+ */
+ public SetValuesTo<On<BatchConjunction>> set( final Name propertyName ) {
+ return new SetValuesTo<On<BatchConjunction>>() {
+ public On<BatchConjunction> to( Object value ) {
+ return set(getContext().getPropertyFactory().create(propertyName, value));
+ }
+
+ public On<BatchConjunction> to( Object firstValue,
+ Object... otherValues ) {
+ return set(getContext().getPropertyFactory().create(propertyName, firstValue, otherValues));
+ }
+
+ public On<BatchConjunction> to( Iterable<?> values ) {
+ return set(getContext().getPropertyFactory().create(propertyName, values));
+ }
+
+ public On<BatchConjunction> to( Iterator<?> values ) {
+ return set(getContext().getPropertyFactory().create(propertyName, values));
+ }
+ };
+ }
+
+ /**
* Remove properties from the node at the given location.
*
* @param propertyNames the names of the properties to be removed
@@ -2596,6 +2696,48 @@
}
/**
+ * A component used to set the values on a property.
+ *
+ * @param <Next>
+ * @author Randall Hauch
+ */
+ public interface SetValuesTo<Next> {
+ /**
+ * Set the property value to the given object.
+ *
+ * @param value the property value
+ * @return the interface for additional requests or actions
+ */
+ Next to( Object value );
+
+ /**
+ * Set the property value to the given objects.
+ *
+ * @param firstValue the first property value
+ * @param otherValues the remaining property values
+ * @return the interface for additional requests or actions
+ */
+ Next to( Object firstValue,
+ Object... otherValues );
+
+ /**
+ * Set the property value to the given object.
+ *
+ * @param values the container for the property values
+ * @return the interface for additional requests or actions
+ */
+ Next to( Iterable<?> values );
+
+ /**
+ * Set the property value to the given object.
+ *
+ * @param values the iterator over the property values
+ * @return the interface for additional requests or actions
+ */
+ Next to( Iterator<?> values );
+ }
+
+ /**
* A component that defines a node that is to be created.
*
* @param <Next> The interface that is to be returned to complete the create request
@@ -3572,6 +3714,7 @@
@Override
public Results execute() {
+ this.queue().submit(new CreateNodeRequest(parent, childName, this.properties));
return queue().execute();
}
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-11-25 19:31:22 UTC (rev 648)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-11-26 16:42:23 UTC (rev 649)
@@ -333,6 +333,15 @@
return null;
}
+ public UUID getUuid() {
+ Property property = getIdProperty(DnaLexicon.UUID);
+ if (property != null && !property.isEmpty()) {
+ Object value = property.getFirstValue();
+ if (value instanceof UUID) return (UUID)value;
+ }
+ return null;
+ }
+
/**
* Compare this location to the supplied location, and determine whether the two locations represent the same logical
* location. One location is considered the same as another location when one location is a superset of the other. For
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-25 19:31:22 UTC (rev 648)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-26 16:42:23 UTC (rev 649)
@@ -308,7 +308,9 @@
*/
@Override
public String toString() {
- return "create node \"" + childName + "\" under " + under() + " with properties " + properties();
+ String parent = under() + "/";
+ if (under.hasPath() && under.getPath().isRoot()) parent = "/";
+ return "create node \"" + parent + childName + "\" with properties " + properties();
}
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-11-25 19:31:22 UTC (rev 648)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-11-26 16:42:23 UTC (rev 649)
@@ -80,6 +80,7 @@
private UUID validUuid;
private Property validIdProperty1;
private Property validIdProperty2;
+ private Location validLocation;
private String sourceName;
private MockRepositoryConnection connection;
private LinkedList<Request> executedRequests;
@@ -107,6 +108,7 @@
Name idProperty2Name = createName("id2");
validIdProperty1 = context.getPropertyFactory().create(idProperty1Name, "1");
validIdProperty2 = context.getPropertyFactory().create(idProperty2Name, "2");
+ validLocation = new Location(validPath);
properties = new HashMap<Location, Collection<Property>>();
children = new HashMap<Location, List<Location>>();
@@ -339,6 +341,13 @@
}
@Test
+ public void shouldCreateNodesWithBatch() {
+ graph.batch().create(validPath, validIdProperty1).and().remove("prop").on(validPathString).execute();
+ graph.batch().move(validPath).and(validPath).into(validPathString).and().create(validPath).execute();
+ graph.batch().createUnder(validLocation).nodeNamed("someName").and().delete(validLocation).execute();
+ }
+
+ @Test
public void shouldGetPropertiesOnNode() {
setPropertiesToReadOn(new Location(validPath), validIdProperty1, validIdProperty2);
Collection<Property> props = graph.getProperties().on(validPath);
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithChildren.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithChildren.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithChildren.java 2008-11-26 16:42:23 UTC (rev 649)
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.basic.BasicPathSegment;
+import org.junit.matchers.IsCollectionContaining;
+import org.junit.matchers.TypeSafeMatcher;
+
+/**
+ * @author Randall Hauch
+ */
+public class IsNodeWithChildren extends TypeSafeMatcher<Node> {
+ private final Matcher<Iterable<Path.Segment>> childMatcher;
+
+ public IsNodeWithChildren( Matcher<Iterable<Path.Segment>> childMatcher ) {
+ this.childMatcher = childMatcher;
+ }
+
+ @Override
+ public boolean matchesSafely( Node node ) {
+ List<Location> children = node.getChildren();
+ List<Path.Segment> childSegments = new ArrayList<Path.Segment>(children.size());
+ for (Location child : children) {
+ childSegments.add(child.getPath().getLastSegment());
+ }
+ return childMatcher.matches(childSegments);
+ }
+
+ public void describeTo( Description description ) {
+ description.appendText("a node containing children").appendDescriptionOf(childMatcher);
+ }
+
+ @Factory
+ public static IsNodeWithChildren hasChild( Name name,
+ int sameNameSiblingIndex ) {
+ Path.Segment segment = new BasicPathSegment(name, sameNameSiblingIndex);
+ return new IsNodeWithChildren(IsCollectionContaining.hasItem(segment));
+ }
+
+ @Factory
+ public static IsNodeWithChildren hasChild( Path.Segment child ) {
+ return new IsNodeWithChildren(IsCollectionContaining.hasItem(child));
+ }
+
+ @Factory
+ public static IsNodeWithChildren hasChildren( Path.Segment... childSegments ) {
+ return new IsNodeWithChildren(IsCollectionContaining.hasItems(childSegments));
+ }
+
+ @Factory
+ public static IsNodeWithChildren hasNoChildren() {
+ Path.Segment[] childSegments = new Path.Segment[] {};
+ return new IsNodeWithChildren(IsCollectionContaining.hasItems(childSegments));
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithChildren.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithProperty.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithProperty.java (rev 0)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithProperty.java 2008-11-26 16:42:23 UTC (rev 649)
@@ -0,0 +1,75 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph;
+
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Property;
+import org.junit.matchers.IsCollectionContaining;
+import org.junit.matchers.TypeSafeMatcher;
+
+/**
+ * @author Randall Hauch
+ */
+public class IsNodeWithProperty extends TypeSafeMatcher<Node> {
+ private final String propertyNameStr;
+ private final Name propertyName;
+ private final Matcher<Iterable<Object>> valueMatcher;
+
+ public IsNodeWithProperty( String propertyNameStr,
+ Name propertyName,
+ Matcher<Iterable<Object>> valueMatcher ) {
+ this.propertyNameStr = propertyNameStr;
+ this.propertyName = propertyName;
+ this.valueMatcher = valueMatcher;
+ }
+
+ @Override
+ public boolean matchesSafely( Node node ) {
+ Property prop = propertyNameStr != null ? node.getProperty(propertyNameStr) : node.getProperty(propertyName);
+ if (prop != null) {
+ return valueMatcher.matches(prop);
+ }
+ return false;
+ }
+
+ public void describeTo( Description description ) {
+ Object name = propertyNameStr != null ? propertyNameStr : propertyName;
+ description.appendText("a property \"" + name + "\"containing ").appendDescriptionOf(valueMatcher);
+ }
+
+ @Factory
+ public static IsNodeWithProperty hasProperty( Name name,
+ Object... values ) {
+ return new IsNodeWithProperty(null, name, IsCollectionContaining.hasItems(values));
+ }
+
+ @Factory
+ public static IsNodeWithProperty hasProperty( String name,
+ Object... values ) {
+ return new IsNodeWithProperty(name, null, IsCollectionContaining.hasItems(values));
+ }
+
+}
Property changes on: trunk/dna-graph/src/test/java/org/jboss/dna/graph/IsNodeWithProperty.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
17 years, 1 month
DNA SVN: r648 - in trunk/extensions/dna-connector-svn/src/test/resources/nodeB: nodeB1 and 1 other directories.
by dna-commits@lists.jboss.org
Author: spagop
Date: 2008-11-25 14:31:22 -0500 (Tue, 25 Nov 2008)
New Revision: 648
Added:
trunk/extensions/dna-connector-svn/src/test/resources/nodeB/nodeB1/
trunk/extensions/dna-connector-svn/src/test/resources/nodeB/nodeB1/nodeB1_1/
trunk/extensions/dna-connector-svn/src/test/resources/nodeB/nodeB1/nodeB1_1/test.txt
Log:
test.txt
Added: trunk/extensions/dna-connector-svn/src/test/resources/nodeB/nodeB1/nodeB1_1/test.txt
===================================================================
17 years, 1 month
DNA SVN: r647 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/properties and 13 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-11-24 10:13:59 -0500 (Mon, 24 Nov 2008)
New Revision: 647
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/InvalidRequestException.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Property.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicEmptyProperty.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicMultiValueProperty.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicName.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicSingleValueProperty.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
Log:
DNA-40 Persistant storage for information not stored in other repository sources
More progress on the relational database storage connector.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -493,7 +493,7 @@
Path at = createPath(atPath);
Path parent = at.getParent();
Name child = at.getLastSegment().getName();
- this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0));
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child));
return nextGraph;
}
@@ -514,7 +514,7 @@
Path at = createPath(atPath);
Path parent = at.getParent();
Name child = at.getLastSegment().getName();
- this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0, properties));
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, properties));
return nextGraph;
}
@@ -533,7 +533,7 @@
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name child = at.getLastSegment().getName();
- this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0));
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child));
return nextGraph;
}
@@ -554,7 +554,7 @@
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name child = at.getLastSegment().getName();
- this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0, properties));
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, properties));
return nextGraph;
}
@@ -575,7 +575,7 @@
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name child = at.getLastSegment().getName();
- this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0, properties));
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, properties));
return nextGraph;
}
@@ -594,45 +594,27 @@
final NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
CheckArg.isNotNull(parent, "parent");
return new CreateNode<Conjunction<Graph>>() {
- public Conjunction<Graph> node( String name,
- Property... properties ) {
- return node(name, -1, properties);
- }
-
- public Conjunction<Graph> node( String name,
- Iterator<Property> properties ) {
- return node(name, -1, properties);
- }
-
- public Conjunction<Graph> node( String name,
- Iterable<Property> properties ) {
- return node(name, -1, properties);
- }
-
@SuppressWarnings( "synthetic-access" )
public Conjunction<Graph> node( String name,
- int desiredIndexInParent,
Property... properties ) {
Name child = nameFactory.create(name);
- requestQueue.submit(new CreateNodeRequest(parent, child, desiredIndexInParent, properties));
+ requestQueue.submit(new CreateNodeRequest(parent, child, properties));
return nextGraph;
}
@SuppressWarnings( "synthetic-access" )
public Conjunction<Graph> node( String name,
- int desiredIndexInParent,
Iterator<Property> properties ) {
Name child = nameFactory.create(name);
- requestQueue.submit(new CreateNodeRequest(parent, child, desiredIndexInParent, properties));
+ requestQueue.submit(new CreateNodeRequest(parent, child, properties));
return nextGraph;
}
@SuppressWarnings( "synthetic-access" )
public Conjunction<Graph> node( String name,
- int desiredIndexInParent,
Iterable<Property> properties ) {
Name child = nameFactory.create(name);
- requestQueue.submit(new CreateNodeRequest(parent, child, desiredIndexInParent, properties));
+ requestQueue.submit(new CreateNodeRequest(parent, child, properties));
return nextGraph;
}
};
@@ -1617,7 +1599,7 @@
Path at = createPath(atPath);
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0);
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name);
}
/**
@@ -1638,7 +1620,7 @@
Path at = createPath(atPath);
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(property);
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(property);
}
/**
@@ -1661,7 +1643,7 @@
Path at = createPath(atPath);
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(firstProperty,
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(firstProperty,
additionalProperties);
}
@@ -1681,7 +1663,7 @@
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0);
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name);
}
/**
@@ -1703,7 +1685,7 @@
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
CreateAction<BatchConjunction> action = new CreateAction<BatchConjunction>(nextRequests, requestQueue,
- new Location(parent), name, 0);
+ new Location(parent), name);
for (Property property : properties) {
action.and(property);
}
@@ -1728,7 +1710,7 @@
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(property);
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(property);
}
/**
@@ -1751,7 +1733,7 @@
CheckArg.isNotNull(at, "at");
Path parent = at.getParent();
Name name = at.getLastSegment().getName();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(firstProperty,
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name).with(firstProperty,
additionalProperties);
}
@@ -2649,42 +2631,6 @@
*/
Next node( String nodeName,
Iterable<Property> properties );
-
- /**
- * Specify the name of the node that is to be created.
- *
- * @param nodeName the name of the new node
- * @param sameNameSiblingIndex the desired same-name-sibling index
- * @param properties the properties for the new node
- * @return the next component for making additional requests.
- */
- Next node( String nodeName,
- int sameNameSiblingIndex,
- Property... properties );
-
- /**
- * Specify the name of the node that is to be created.
- *
- * @param nodeName the name of the new node
- * @param sameNameSiblingIndex the desired same-name-sibling index
- * @param properties the properties for the new node
- * @return the next component for making additional requests.
- */
- Next node( String nodeName,
- int sameNameSiblingIndex,
- Iterator<Property> properties );
-
- /**
- * Specify the name of the node that is to be created.
- *
- * @param nodeName the name of the new node
- * @param sameNameSiblingIndex the desired same-name-sibling index
- * @param properties the properties for the new node
- * @return the next component for making additional requests.
- */
- Next node( String nodeName,
- int sameNameSiblingIndex,
- Iterable<Property> properties );
}
/**
@@ -2701,16 +2647,6 @@
* @return the interface used to complete the request
*/
CreateAction<Next> nodeNamed( String nodeName );
-
- /**
- * Specify the name of the node that is to be created.
- *
- * @param nodeName the name of the new node
- * @param sameNameSiblingIndex the desired same-name-sibling index
- * @return the interface used to complete the request
- */
- CreateAction<Next> nodeNamed( String nodeName,
- int sameNameSiblingIndex );
}
/**
@@ -3557,18 +3493,15 @@
static class CreateAction<T> extends AbstractAction<T> implements Create<T> {
private final Location parent;
private final Name childName;
- private final int desiredIndex;
private final List<Property> properties = new LinkedList<Property>();
/*package*/CreateAction( T afterConjunction,
RequestQueue queue,
Location parent,
- Name childName,
- int desiredIndex ) {
+ Name childName ) {
super(afterConjunction, queue);
this.parent = parent;
this.childName = childName;
- this.desiredIndex = desiredIndex;
}
public Create<T> and( UUID uuid ) {
@@ -3633,7 +3566,7 @@
@Override
public T and() {
- this.queue().submit(new CreateNodeRequest(parent, childName, desiredIndex, this.properties));
+ this.queue().submit(new CreateNodeRequest(parent, childName, this.properties));
return super.and();
}
@@ -3658,16 +3591,7 @@
ExecutionContext context = queue().getGraph().getContext();
NameFactory factory = context.getValueFactories().getNameFactory();
Name nameObj = factory.create(name);
- return new CreateAction<T>(afterConjunction(), queue(), parent, nameObj, 0);
+ return new CreateAction<T>(afterConjunction(), queue(), parent, nameObj);
}
-
- public CreateAction<T> nodeNamed( String name,
- int desiredIndex ) {
- ExecutionContext context = queue().getGraph().getContext();
- NameFactory factory = context.getValueFactories().getNameFactory();
- Name nameObj = factory.create(name);
- if (desiredIndex < 0) desiredIndex = 0;
- return new CreateAction<T>(afterConjunction(), queue(), parent, nameObj, desiredIndex);
- }
}
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Property.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Property.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Property.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -107,6 +107,18 @@
boolean isEmpty();
/**
+ * Obtain the property's first value in its natural form. This is equivalent to calling
+ * <code>isEmpty() ? null : iterator().next()</code>
+ *
+ * @return the first value, or null if the property is {@link #isEmpty() empty}
+ * @see Iterable#iterator()
+ * @see #getValues()
+ * @see #getValuesAsArray()
+ * @see #isEmpty()
+ */
+ Object getFirstValue();
+
+ /**
* Obtain the property's values in their natural form. This is equivalent to calling {@link Iterable#iterator() iterator()}.
* <p>
* A valid iterator is returned if the property has {@link #isSingle() single valued} or {@link #isMultiple() multi-valued}.
@@ -116,6 +128,7 @@
* </p>
*
* @return an iterator over the values; never null
+ * @see #getFirstValue()
* @see Iterable#iterator()
* @see #getValuesAsArray()
* @see ValueFactory#create(Iterator)
@@ -133,6 +146,7 @@
* </p>
*
* @return the array of values
+ * @see #getFirstValue()
* @see Iterable#iterator()
* @see #getValues()
* @see ValueFactory#create(Object[])
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicEmptyProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicEmptyProperty.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicEmptyProperty.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -76,7 +76,16 @@
/**
* {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.properties.Property#getFirstValue()
*/
+ public Object getFirstValue() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public Iterator<Object> iterator() {
return SHARED_ITERATOR;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicMultiValueProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicMultiValueProperty.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicMultiValueProperty.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -100,7 +100,16 @@
/**
* {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.properties.Property#getFirstValue()
*/
+ public Object getFirstValue() {
+ return values.get(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public Iterator<Object> iterator() {
return new ReadOnlyIterator(values.iterator());
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicName.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicName.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicName.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -147,9 +147,9 @@
*/
public int compareTo( Name that ) {
if (that == this) return 0;
- int diff = this.getNamespaceUri().compareTo(that.getNamespaceUri());
+ int diff = this.getLocalName().compareTo(that.getLocalName());
if (diff != 0) return diff;
- diff = this.getLocalName().compareTo(that.getLocalName());
+ diff = this.getNamespaceUri().compareTo(that.getNamespaceUri());
return diff;
}
@@ -169,8 +169,9 @@
if (obj == this) return true;
if (obj instanceof Name) {
Name that = (Name)obj;
+ if (!this.getLocalName().equals(that.getLocalName())) return false;
if (!this.getNamespaceUri().equals(that.getNamespaceUri())) return false;
- return this.getLocalName().equals(that.getLocalName());
+ return true;
}
return false;
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicSingleValueProperty.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicSingleValueProperty.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/BasicSingleValueProperty.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -79,7 +79,16 @@
/**
* {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.properties.Property#getFirstValue()
*/
+ public Object getFirstValue() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public Iterator<Object> iterator() {
return new ValueIterator();
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -47,7 +47,6 @@
private final Location under;
private final Name childName;
- private final Integer desiredIndex;
private final List<Property> properties;
private final NodeConflictBehavior conflictBehavior;
private Location actualLocation;
@@ -57,17 +56,14 @@
*
* @param parentLocation the location of the existing parent node, under which the new child should be created
* @param childName the name of the new child to create under the existing parent
- * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
- * is to be appended under the end of the existing children
* @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
* properties} for the new node
* @throws IllegalArgumentException if the location or the child name is null
*/
public CreateNodeRequest( Location parentLocation,
Name childName,
- int desiredIndexInParent,
Property... properties ) {
- this(parentLocation, childName, desiredIndexInParent, DEFAULT_CONFLICT_BEHAVIOR, properties);
+ this(parentLocation, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
}
/**
@@ -75,17 +71,14 @@
*
* @param parentLocation the location of the existing parent node, under which the new child should be created
* @param childName the name of the new child to create under the existing parent
- * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
- * is to be appended under the end of the existing children
* @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
* properties} for the new node
* @throws IllegalArgumentException if the location or the child name is null
*/
public CreateNodeRequest( Location parentLocation,
Name childName,
- int desiredIndexInParent,
Iterable<Property> properties ) {
- this(parentLocation, childName, desiredIndexInParent, DEFAULT_CONFLICT_BEHAVIOR, properties);
+ this(parentLocation, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
}
/**
@@ -93,17 +86,14 @@
*
* @param parentLocation the location of the existing parent node, under which the new child should be created
* @param childName the name of the new child to create under the existing parent
- * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
- * is to be appended under the end of the existing children
* @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
* properties} for the new node
* @throws IllegalArgumentException if the location or the child name is null
*/
public CreateNodeRequest( Location parentLocation,
Name childName,
- int desiredIndexInParent,
Iterator<Property> properties ) {
- this(parentLocation, childName, desiredIndexInParent, DEFAULT_CONFLICT_BEHAVIOR, properties);
+ this(parentLocation, childName, DEFAULT_CONFLICT_BEHAVIOR, properties);
}
/**
@@ -111,8 +101,6 @@
*
* @param parentLocation the location of the existing parent node, under which the new child should be created
* @param childName the name of the new child to create under the existing parent
- * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
- * is to be appended under the end of the existing children
* @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
* properties} for the new node
* @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
@@ -121,7 +109,6 @@
*/
public CreateNodeRequest( Location parentLocation,
Name childName,
- int desiredIndexInParent,
NodeConflictBehavior conflictBehavior,
Property... properties ) {
CheckArg.isNotNull(parentLocation, "parentLocation");
@@ -129,7 +116,6 @@
CheckArg.isNotNull(childName, "childName");
this.under = parentLocation;
this.childName = childName;
- this.desiredIndex = desiredIndexInParent < 1 ? null : new Integer(desiredIndexInParent);
this.conflictBehavior = conflictBehavior;
int number = properties.length + (under.hasIdProperties() ? under.getIdProperties().size() : 0);
List<Property> props = new ArrayList<Property>(number);
@@ -144,8 +130,6 @@
*
* @param parentLocation the location of the existing parent node, under which the new child should be created
* @param childName the name of the new child to create under the existing parent
- * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
- * is to be appended under the end of the existing children
* @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
* properties} for the new node
* @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
@@ -154,7 +138,6 @@
*/
public CreateNodeRequest( Location parentLocation,
Name childName,
- int desiredIndexInParent,
NodeConflictBehavior conflictBehavior,
Iterable<Property> properties ) {
CheckArg.isNotNull(parentLocation, "parentLocation");
@@ -162,7 +145,6 @@
CheckArg.isNotNull(childName, "childName");
this.under = parentLocation;
this.childName = childName;
- this.desiredIndex = desiredIndexInParent < 1 ? null : new Integer(desiredIndexInParent);
this.conflictBehavior = conflictBehavior;
List<Property> props = new LinkedList<Property>();
for (Property property : properties) {
@@ -182,8 +164,6 @@
*
* @param parentLocation the location of the existing parent node, under which the new child should be created
* @param childName the name of the new child to create under the existing parent
- * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
- * is to be appended under the end of the existing children
* @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
* properties} for the new node
* @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
@@ -192,7 +172,6 @@
*/
public CreateNodeRequest( Location parentLocation,
Name childName,
- int desiredIndexInParent,
NodeConflictBehavior conflictBehavior,
Iterator<Property> properties ) {
CheckArg.isNotNull(parentLocation, "parentLocation");
@@ -200,7 +179,6 @@
CheckArg.isNotNull(childName, "childName");
this.under = parentLocation;
this.childName = childName;
- this.desiredIndex = desiredIndexInParent < 1 ? null : new Integer(desiredIndexInParent);
this.conflictBehavior = conflictBehavior;
List<Property> props = new LinkedList<Property>();
while (properties.hasNext()) {
@@ -235,16 +213,6 @@
}
/**
- * Get the desired index (always positive) in the parent for the new child, or null if the new child should be appended under
- * the end of the existing children.
- *
- * @return the desired index, or null if there is no desired index
- */
- public Integer desiredIndex() {
- return desiredIndex;
- }
-
- /**
* {@inheritDoc}
*
* @see java.lang.Iterable#iterator()
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/InvalidRequestException.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/InvalidRequestException.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/InvalidRequestException.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.requests;
+
+/**
+ * Specifies that the request was invalid and could not be completed.
+ *
+ * @author Randall Hauch
+ */
+public class InvalidRequestException extends RuntimeException {
+
+ /**
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ */
+ public InvalidRequestException() {
+ }
+
+ /**
+ * @param message
+ */
+ public InvalidRequestException( String message ) {
+ super(message);
+
+ }
+
+ /**
+ * @param cause
+ */
+ public InvalidRequestException( Throwable cause ) {
+ super(cause);
+
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public InvalidRequestException( String message,
+ Throwable cause ) {
+ super(message, cause);
+
+ }
+
+}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/MoveBranchRequest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -25,6 +25,7 @@
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.properties.Path;
/**
* Instruction that a branch be moved from one location into another.
@@ -114,6 +115,30 @@
}
/**
+ * Determine whether this move request can be determined to have no effect.
+ * <p>
+ * A move is known to have no effect when all of the following conditions are true:
+ * <ul>
+ * <li>the {@link #into() into} location has a {@link Location#hasPath() path} but no {@link Location#hasIdProperties()
+ * identification properties};</li>
+ * <li>the {@link #from() from} location has a {@link Location#getPath() path}; and</li>
+ * <li>the {@link #from() from} location's {@link Path#getParent() parent} is the same as the {@link #into() into} location's
+ * path.</li>
+ * </ul>
+ * If all of these conditions are not true, this method returns false.
+ * </p>
+ *
+ * @return true if this move request really doesn't change the parent of the node, or false if it cannot be determined
+ */
+ public boolean hasNoEffect() {
+ if (into.hasPath() && into.hasIdProperties() == false && from.hasPath()) {
+ return from.getPath().getParent().equals(into.getPath());
+ }
+ // Can't be determined for certain
+ return false;
+ }
+
+ /**
* Sets the actual and complete location of the node being renamed and its new location. This method must be called when
* processing the request, and the actual location must have a {@link Location#getPath() path}.
*
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -162,10 +162,9 @@
protected void assertNextRequestIsCreate( Location parent,
String child,
- int desiredIndex,
Property... properties ) {
Name name = context.getValueFactories().getNameFactory().create(child);
- assertThat(executedRequests.poll(), is((Request)new CreateNodeRequest(parent, name, desiredIndex, properties)));
+ assertThat(executedRequests.poll(), is((Request)new CreateNodeRequest(parent, name, properties)));
}
protected void assertNextRequestReadProperties( Location at,
@@ -310,32 +309,32 @@
public void shouldCreateNode() {
graph.create(validPath);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c");
assertNoMoreRequests();
graph.create(validPath, validIdProperty1);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", validIdProperty1);
assertNoMoreRequests();
graph.create(validPath, validIdProperty1, validIdProperty2);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1, validIdProperty2);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", validIdProperty1, validIdProperty2);
assertNoMoreRequests();
graph.create(validPathString);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c");
assertNoMoreRequests();
graph.create(validPathString, validIdProperty1);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", validIdProperty1);
assertNoMoreRequests();
graph.create(validPathString, validIdProperty1, validIdProperty2);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1, validIdProperty2);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", validIdProperty1, validIdProperty2);
assertNoMoreRequests();
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -439,7 +439,7 @@
assert path != null;
Path parent = path.getParent();
Name child = path.getLastSegment().getName();
- requests.add(new CreateNodeRequest(new Location(parent), child, 0, properties));
+ requests.add(new CreateNodeRequest(new Location(parent), child, properties));
}
public void create( final Path path,
@@ -449,10 +449,10 @@
Name child = path.getLastSegment().getName();
Location location = new Location(parent);
if (firstProperty == null) {
- requests.add(new CreateNodeRequest(location, child, 0));
+ requests.add(new CreateNodeRequest(location, child));
} else {
if (additionalProperties == null || additionalProperties.length == 0) {
- requests.add(new CreateNodeRequest(location, child, 0, firstProperty));
+ requests.add(new CreateNodeRequest(location, child, firstProperty));
} else {
Iterator<Property> iter = new Iterator<Property>() {
private int index = -1;
@@ -473,7 +473,7 @@
throw new UnsupportedOperationException();
}
};
- requests.add(new CreateNodeRequest(location, child, 0, iter));
+ requests.add(new CreateNodeRequest(location, child, iter));
}
}
}
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -651,14 +651,14 @@
// This is not the root node, so we need to create the node ...
final Location parentLocation = new Location(path.getParent());
childName = path.getLastSegment().getName();
- requests.add(new CreateNodeRequest(parentLocation, childName, 0, NodeConflictBehavior.REPLACE,
+ requests.add(new CreateNodeRequest(parentLocation, childName, NodeConflictBehavior.REPLACE,
mergedNode.getProperties()));
}
// Now create all of the children that this federated node knows of ...
for (Location child : mergedNode.getChildren()) {
childName = child.getPath().getLastSegment().getName();
- requests.add(new CreateNodeRequest(location, childName, 0, NodeConflictBehavior.APPEND));
+ requests.add(new CreateNodeRequest(location, childName, NodeConflictBehavior.APPEND));
}
cacheConnection.execute(context, CompositeRequest.with(requests));
}
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -189,9 +189,7 @@
public void process( CreateNodeRequest request ) {
Location locationInSource = projectIntoSource(request.under());
Name child = request.named();
- Integer desiredIndex = request.desiredIndex();
- int index = desiredIndex != null ? desiredIndex.intValue() : 0;
- CreateNodeRequest projected = new CreateNodeRequest(locationInSource, child, index, request.properties());
+ CreateNodeRequest projected = new CreateNodeRequest(locationInSource, child, request.properties());
getConnection().execute(this.getExecutionContext(), projected);
if (projected.hasError()) {
projectError(projected, request.under(), request);
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -46,13 +46,15 @@
private final Model model;
private final UUID rootNodeUuid;
private final long largeValueMinimumSizeInBytes;
+ private final boolean compressData;
/*package*/JpaConnection( String sourceName,
CachePolicy cachePolicy,
EntityManager entityManager,
Model model,
UUID rootNodeUuid,
- long largeValueMinimumSizeInBytes ) {
+ long largeValueMinimumSizeInBytes,
+ boolean compressData ) {
assert sourceName != null;
assert entityManager != null;
assert model != null;
@@ -63,6 +65,7 @@
this.model = model;
this.rootNodeUuid = rootNodeUuid;
this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+ this.compressData = compressData;
}
/**
@@ -122,7 +125,7 @@
public void execute( ExecutionContext context,
Request request ) throws RepositorySourceException {
long size = largeValueMinimumSizeInBytes;
- RequestProcessor proc = model.createRequestProcessor(name, context, entityManager, rootNodeUuid, size);
+ RequestProcessor proc = model.createRequestProcessor(name, context, entityManager, rootNodeUuid, size, compressData);
try {
proc.process(request);
} finally {
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -39,6 +39,8 @@
public static I18n errorSettingContextClassLoader;
public static I18n existingStoreSpecifiesUnknownModel;
public static I18n unableToReadLargeValue;
+ public static I18n unableToMoveRootNode;
+ public static I18n locationShouldHavePathAndOrProperty;
public static I18n basicModelDescription;
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -112,6 +112,7 @@
protected static final String RETRY_LIMIT = "retryLimit";
protected static final String MODEL_NAME = "modelName";
protected static final String LARGE_VALUE_SIZE_IN_BYTES = "largeValueSizeInBytes";
+ protected static final String COMPRESS_DATA = "compressData";
/**
* This source supports events.
@@ -142,6 +143,7 @@
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 boolean DEFAULT_COMPRESS_DATA = true;
/**
* The first serialized version of this source.
@@ -166,6 +168,7 @@
private int retryLimit = DEFAULT_RETRY_LIMIT;
private int cacheTimeToLiveInMilliseconds = DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS * 1000;
private long largeValueSizeInBytes = DEFAULT_LARGE_VALUE_SIZE_IN_BYTES;
+ private boolean compressData = DEFAULT_COMPRESS_DATA;
private final Capabilities capabilities = new Capabilities();
private transient Model model;
private String modelName;
@@ -554,6 +557,20 @@
}
/**
+ * @return compressData
+ */
+ public boolean isCompressData() {
+ return compressData;
+ }
+
+ /**
+ * @param compressData Sets compressData to the specified value.
+ */
+ public void setCompressData( boolean compressData ) {
+ this.compressData = compressData;
+ }
+
+ /**
* {@inheritDoc}
*
* @see org.jboss.dna.graph.connectors.RepositorySource#initialize(org.jboss.dna.graph.connectors.RepositoryContext)
@@ -610,6 +627,7 @@
Integer.toString(getIdleTimeInSecondsBeforeTestingConnections())));
ref.add(new StringRefAddr(CACHE_TIME_TO_LIVE_IN_MILLISECONDS, Integer.toString(getCacheTimeToLiveInMilliseconds())));
ref.add(new StringRefAddr(LARGE_VALUE_SIZE_IN_BYTES, Long.toString(getLargeValueSizeInBytes())));
+ ref.add(new StringRefAddr(COMPRESS_DATA, Boolean.toString(isCompressData())));
if (getModel() != null) {
ref.add(new StringRefAddr(MODEL_NAME, getModel()));
}
@@ -655,6 +673,7 @@
String modelName = values.get(MODEL_NAME);
String retryLimit = values.get(RETRY_LIMIT);
String largeModelSize = values.get(LARGE_VALUE_SIZE_IN_BYTES);
+ String compressData = values.get(COMPRESS_DATA);
// Create the source instance ...
JpaSource source = new JpaSource();
@@ -677,6 +696,7 @@
if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
if (modelName != null) source.setModel(modelName);
if (largeModelSize != null) source.setLargeValueSizeInBytes(Long.parseLong(largeModelSize));
+ if (compressData != null) source.setCompressData(Boolean.parseBoolean(compressData));
return source;
}
return null;
@@ -781,7 +801,7 @@
if (entityManager == null) {
entityManager = entityManagerFactory.createEntityManager();
}
- return new JpaConnection(getName(), cachePolicy, entityManager, model, rootUuid, largeValueSizeInBytes);
+ return new JpaConnection(getName(), cachePolicy, entityManager, model, rootUuid, largeValueSizeInBytes, compressData);
}
/**
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -78,7 +78,8 @@
ExecutionContext context,
EntityManager entityManager,
UUID rootNodeUuid,
- long largeValueMinimumSizeInBytes );
+ long largeValueMinimumSizeInBytes,
+ boolean comparessData );
/**
* Configure the entity class that will be used by JPA to store information in the database.
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -47,15 +47,17 @@
* {@inheritDoc}
*
* @see org.jboss.dna.connector.store.jpa.Model#createRequestProcessor(java.lang.String, org.jboss.dna.graph.ExecutionContext,
- * javax.persistence.EntityManager, java.util.UUID, long)
+ * javax.persistence.EntityManager, java.util.UUID, long, boolean)
*/
@Override
public RequestProcessor createRequestProcessor( String sourceName,
ExecutionContext context,
EntityManager entityManager,
UUID rootNodeUuid,
- long largeValueMinimumSizeInBytes ) {
- return new BasicRequestProcessor(sourceName, context, entityManager, rootNodeUuid, largeValueMinimumSizeInBytes);
+ long largeValueMinimumSizeInBytes,
+ boolean compressData ) {
+ return new BasicRequestProcessor(sourceName, context, entityManager, rootNodeUuid, largeValueMinimumSizeInBytes,
+ compressData);
}
/**
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -22,22 +22,35 @@
package org.jboss.dna.connector.store.jpa.models.basic;
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.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.ZipInputStream;
+import java.util.zip.ZipOutputStream;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.Query;
+import net.jcip.annotations.Immutable;
+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.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
import org.jboss.dna.connector.store.jpa.util.Serializer;
import org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues;
import org.jboss.dna.graph.DnaLexicon;
@@ -56,15 +69,18 @@
import org.jboss.dna.graph.requests.CopyBranchRequest;
import org.jboss.dna.graph.requests.CreateNodeRequest;
import org.jboss.dna.graph.requests.DeleteBranchRequest;
+import org.jboss.dna.graph.requests.InvalidRequestException;
import org.jboss.dna.graph.requests.MoveBranchRequest;
import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.requests.ReadPropertyRequest;
import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
import org.jboss.dna.graph.requests.processor.RequestProcessor;
/**
* @author Randall Hauch
*/
+@NotThreadSafe
public class BasicRequestProcessor extends RequestProcessor implements LargeValues {
private final EntityManager entities;
@@ -75,6 +91,7 @@
private final UUID rootNodeUuid;
private final Serializer serializer;
private final long largeValueMinimumSizeInBytes;
+ private final boolean compressData;
/**
* @param sourceName
@@ -82,12 +99,14 @@
* @param entityManager
* @param rootNodeUuid
* @param largeValueMinimumSizeInBytes
+ * @param compressData
*/
public BasicRequestProcessor( String sourceName,
ExecutionContext context,
EntityManager entityManager,
UUID rootNodeUuid,
- long largeValueMinimumSizeInBytes ) {
+ long largeValueMinimumSizeInBytes,
+ boolean compressData ) {
super(sourceName, context);
assert entityManager != null;
assert rootNodeUuid != null;
@@ -97,8 +116,9 @@
this.nameFactory = context.getValueFactories().getNameFactory();
this.namespaces = new Namespaces(entityManager);
this.rootNodeUuid = rootNodeUuid;
- this.serializer = new Serializer(context, this);
+ this.serializer = new Serializer(context, this, true);
this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+ this.compressData = compressData;
// Start the transaction ...
this.entities.getTransaction().begin();
}
@@ -106,57 +126,91 @@
/**
* {@inheritDoc}
*
- * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CopyBranchRequest)
- */
- @Override
- public void process( CopyBranchRequest request ) {
- }
-
- /**
- * {@inheritDoc}
- *
* @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CreateNodeRequest)
*/
@Override
public void process( CreateNodeRequest request ) {
- // Location actualLocation = null;
- // try {
- // // Create nodes have to be defined via a path ...
- // Location desiredLocation = request.at();
- // Path desiredPath = desiredLocation.getPath();
- // assert desiredPath != null;
- //
- // // // Get the parent
- // // String parentUuidString = getUuidOf(parentLocation);
- // // Location parentActualLocation = getActualLocation(parentLocation, parentUuidString);
- // // Path parentPath = parentActualLocation.getPath();
- // //
- // // // Now see where the child is to be created ...
- // // request.
- //
- // } catch (Throwable e) { // Includes PathNotFoundException
- // request.setError(e);
- // return;
- // }
- // if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
- }
+ Location actualLocation = null;
+ String childUuidString = null;
+ try {
+ // Create nodes have to be defined via a path ...
+ Location parentLocation = request.under();
+ ActualLocation actual = getActualLocation(parentLocation);
+ String parentUuidString = actual.uuid;
+ assert parentUuidString != null;
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.DeleteBranchRequest)
- */
- @Override
- public void process( DeleteBranchRequest request ) {
- }
+ // We need to look for an existing UUID property in the request,
+ // so since we have to iterate through the properties, go ahead an serialize them right away ...
+ Set<String> largeValueHexHashes = new HashSet<String>();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream os = compressData ? new ZipOutputStream(baos) : baos;
+ ObjectOutputStream oos = new ObjectOutputStream(os);
+ int numProperties = 0;
+ try {
+ for (Property property : request.properties()) {
+ if (property.getName().equals(DnaLexicon.UUID)) {
+ childUuidString = stringFactory.create(property.getFirstValue());
+ }
+ if (serializer.serializeProperty(oos, property, largeValueHexHashes)) ++numProperties;
+ }
+ } finally {
+ oos.close();
+ }
+ String largeValueHexHashesString = createHexValuesString(largeValueHexHashes);
+ if (childUuidString == null) childUuidString = stringFactory.create(UUID.randomUUID());
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.MoveBranchRequest)
- */
- @Override
- public void process( MoveBranchRequest request ) {
+ // Create the PropertiesEntity ...
+ NodeId nodeId = new NodeId(childUuidString);
+ PropertiesEntity props = new PropertiesEntity(nodeId);
+ props.setData(baos.toByteArray());
+ props.setCompressed(compressData);
+ props.setPropertyCount(numProperties);
+ props.setLargeValueKeys(largeValueHexHashesString);
+ entities.persist(props);
+
+ // Find or create the namespace for the child ...
+ Name childName = request.named();
+ String childNsUri = childName.getNamespaceUri();
+ Integer nsId = namespaces.getId(childNsUri, true);
+ assert nsId != null;
+
+ // Find the largest SNS index in the existing ChildEntity objects with the same name ...
+ String childLocalName = childName.getLocalName();
+ Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
+ query.setParameter("uuid", parentUuidString);
+ query.setParameter("ns", nsId);
+ query.setParameter("childName", childLocalName);
+ int nextSnsIndex = 1;
+ try {
+ nextSnsIndex = (Integer)query.getSingleResult();
+ } catch (NoResultException e) {
+ }
+
+ // Find the largest child index in the existing ChildEntity objects ...
+ query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
+ query.setParameter("uuid", parentUuidString);
+ int nextIndexInParent = 1;
+ try {
+ nextIndexInParent = (Integer)query.getSingleResult() + 1;
+ } catch (NoResultException e) {
+ }
+
+ // Create the new ChildEntity ...
+ NamespaceEntity ns = entities.find(NamespaceEntity.class, nsId);
+ assert ns != null;
+ ChildId id = new ChildId(parentUuidString, childUuidString);
+ ChildEntity entity = new ChildEntity(id, nextIndexInParent, ns, childLocalName, nextSnsIndex + 1);
+ entities.persist(entity);
+
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ // Look up the actual path, regardless of the supplied path...
+ assert childUuidString != null;
+ Path path = getPathForUuid(childUuidString);
+ actualLocation = new Location(path, UUID.fromString(childUuidString));
+ request.setActualLocationOfNode(actualLocation);
}
/**
@@ -170,8 +224,9 @@
Location actualLocation = null;
try {
Location location = request.of();
- String parentUuidString = getUuidOf(location);
- actualLocation = getActualLocation(location, parentUuidString);
+ ActualLocation actual = getActualLocation(location);
+ String parentUuidString = actual.uuid;
+ actualLocation = actual.location;
Path path = actualLocation.getPath();
// Find the children of the supplied node ...
@@ -208,23 +263,33 @@
Location actualLocation = null;
try {
Location location = request.at();
- String uuidString = getUuidOf(location);
- actualLocation = getActualLocation(location, uuidString);
+ ActualLocation actual = getActualLocation(location);
+ String uuidString = actual.uuid;
+ actualLocation = actual.location;
+ // Record the UUID as a property, since it's not stored in the serialized properties...
+ request.addProperty(actualLocation.getIdProperty(DnaLexicon.UUID));
+
// Find the properties entity for this node ...
Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
query.setParameter("uuid", uuidString);
PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
// Deserialize the properties ...
+ boolean compressed = entity.isCompressed();
int propertyCount = entity.getPropertyCount();
Collection<Property> properties = new ArrayList<Property>(propertyCount);
byte[] data = entity.getData();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
- ObjectInputStream ois = new ObjectInputStream(bais);
- serializer.deserializeProperties(ois, properties);
- for (Property property : properties) {
- request.addProperty(property);
+ InputStream is = compressed ? new ZipInputStream(bais) : bais;
+ ObjectInputStream ois = new ObjectInputStream(is);
+ try {
+ serializer.deserializeAllProperties(ois, properties);
+ for (Property property : properties) {
+ request.addProperty(property);
+ }
+ } finally {
+ ois.close();
}
} catch (NoResultException e) {
// there are no properties (probably not expected, but still okay) ...
@@ -238,15 +303,234 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ // Small optimization ...
+ final Name propertyName = request.named();
+ if (DnaLexicon.UUID.equals(propertyName)) {
+ try {
+ // Just get the UUID ...
+ Location location = request.on();
+ ActualLocation actualLocation = getActualLocation(location);
+ request.setActualLocationOfNode(actualLocation.location);
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ }
+ return;
+ }
+ // Process the one property that's requested ...
+ Location actualLocation = null;
+ try {
+ Location location = request.on();
+ ActualLocation actual = getActualLocation(location);
+ String uuidString = actual.uuid;
+ actualLocation = actual.location;
+
+ // Find the properties entity for this node ...
+ Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
+ query.setParameter("uuid", uuidString);
+ PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
+
+ // Deserialize the stream of properties, but only materialize the one property ...
+ boolean compressed = entity.isCompressed();
+ int propertyCount = entity.getPropertyCount();
+ Collection<Property> properties = new ArrayList<Property>(propertyCount);
+ byte[] data = entity.getData();
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ InputStream is = compressed ? new ZipInputStream(bais) : bais;
+ ObjectInputStream ois = new ObjectInputStream(is);
+ try {
+ serializer.deserializeSomeProperties(ois, properties, propertyName);
+ for (Property property : properties) {
+ request.setProperty(property); // should be only one property
+ }
+ } finally {
+ ois.close();
+ }
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.UpdatePropertiesRequest)
*/
@Override
public void process( UpdatePropertiesRequest request ) {
+ Location actualLocation = null;
+ try {
+ Location location = request.on();
+ ActualLocation actual = getActualLocation(location);
+ actualLocation = actual.location;
+
+ // Find the properties entity for this node ...
+ Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
+ query.setParameter("uuid", actual.uuid);
+ PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
+
+ // Determine which large values are referenced ...
+ String largeValueHexKeys = entity.getLargeValueKeys();
+ Collection<String> hexKeys = null;
+ if (largeValueHexKeys != null) {
+ hexKeys = createHexValues(largeValueHexKeys);
+ }
+
+ // Now serialize the properties and save them ...
+ Collection<String> newHexKeys = new HashSet<String>();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ OutputStream os = compressData ? new ZipOutputStream(baos) : baos;
+ ObjectOutputStream oos = new ObjectOutputStream(os);
+ int numProperties = 0;
+ try {
+ for (Property property : request.properties()) {
+ if (serializer.serializeProperty(oos, property, newHexKeys)) ++numProperties;
+ }
+ } finally {
+ oos.close();
+ }
+ largeValueHexKeys = createHexValuesString(newHexKeys);
+ entity.setPropertyCount(numProperties);
+ entity.setData(baos.toByteArray());
+ entity.setCompressed(compressData);
+ entity.setLargeValueKeys(largeValueHexKeys);
+
+ // Update the large values that used to be reference but no longer are ...
+ if (hexKeys != null) {
+ hexKeys.removeAll(newHexKeys);
+ for (String oldHexKey : hexKeys) {
+ LargeValueEntity largeValue = entities.find(LargeValueEntity.class, oldHexKey);
+ if (largeValue != null) {
+ if (largeValue.decrementUsageCount() == 0) {
+ entities.remove(entity);
+ }
+ }
+ }
+ }
+
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
}
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ Location actualOldLocation = null;
+ Location actualNewLocation = null;
+ try {
+ Location fromLocation = request.from();
+ ActualLocation actualLocation = getActualLocation(fromLocation);
+ String fromUuidString = actualLocation.uuid;
+ actualOldLocation = actualLocation.location;
+
+ // It's not possible to move the root node
+ if (actualOldLocation.getPath().isRoot()) {
+ String msg = JpaConnectorI18n.unableToMoveRootNode.text(getSourceName());
+ throw new InvalidRequestException(msg);
+ }
+
+ // Find the ChildEntity of the existing 'from' node ...
+ ChildEntity fromEntity = actualLocation.childEntity;
+ final String oldParentUuid = fromEntity.getId().getParentUuidString();
+
+ // Find the actual new location ...
+ Location toLocation = request.into();
+ String toUuidString = null;
+ if (request.hasNoEffect()) {
+ actualNewLocation = actualOldLocation;
+ } else {
+ // We have to proceed as normal ...
+ ActualLocation actualIntoLocation = getActualLocation(toLocation);
+ toUuidString = actualIntoLocation.uuid;
+ if (!toUuidString.equals(oldParentUuid)) {
+ // Now we know that the new parent is not the existing parent ...
+ final int oldSnsIndex = fromEntity.getSameNameSiblingIndex();
+ final int oldIndex = fromEntity.getIndexInParent();
+
+ // Find the largest SNS index in the existing ChildEntity objects with the same name ...
+ String childLocalName = fromEntity.getChildName();
+ NamespaceEntity ns = fromEntity.getChildNamespace();
+ Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
+ query.setParameter("uuid", toUuidString);
+ query.setParameter("ns", ns.getId());
+ query.setParameter("childName", childLocalName);
+ int nextSnsIndex = 1;
+ try {
+ nextSnsIndex = (Integer)query.getSingleResult();
+ } catch (NoResultException e) {
+ }
+
+ // Find the largest child index in the existing ChildEntity objects ...
+ query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
+ query.setParameter("uuid", toUuidString);
+ int nextIndexInParent = 1;
+ try {
+ nextIndexInParent = (Integer)query.getSingleResult() + 1;
+ } catch (NoResultException e) {
+ }
+
+ // Move the child entity to be under the new parent ...
+ fromEntity.setId(new ChildId(toUuidString, fromUuidString));
+ fromEntity.setIndexInParent(nextIndexInParent);
+ fromEntity.setSameNameSiblingIndex(nextSnsIndex);
+
+ // And adjust the SNS index and indexes ...
+ adjustSnsIndexesAndIndexesAfterRemoving(oldParentUuid, childLocalName, ns.getId(), oldIndex, oldSnsIndex);
+ }
+ }
+
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ request.setActualLocations(actualOldLocation, actualNewLocation);
+ }
+
+ protected void adjustSnsIndexesAndIndexesAfterRemoving( String uuidParent,
+ String childName,
+ int childNamespaceIndex,
+ int childIndex,
+ int childSnsIndex ) {
+
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see org.jboss.dna.graph.requests.processor.RequestProcessor#close()
*/
@Override
@@ -256,58 +540,100 @@
super.close();
}
- protected Location getActualLocation( Location original,
- String uuidString ) {
- // If the original has a path and a UUID, it is complete already ...
+ /**
+ * Utility method to look up the actual information given a supplied location. This method verifies that the location actually
+ * represents an existing node, or it throws a {@link PathNotFoundException}. In all cases, the resulting information contains
+ * the correct path and the correct UUID.
+ * <p>
+ * Note that this method sometimes performs "unnecessary" work when the location contains both a path to a node and the node's
+ * corresponding UUID. Strictly speaking, this method would need to do very little. However, in such cases, this method does
+ * verify that the information is still correct (ensuring that calls to use the {@link ChildEntity} will be correct). So,
+ * while this work <i>may</i> be unnecessary, it does ensure that the location is consistent and correct (something that is
+ * not unnecessary).
+ * </p>
+ * <p>
+ * There are cases when a request containing a Path and a UUID are no longer correct. The node may have been just moved by
+ * another request (perhaps from a different client), or there may be an error in the component making the request. In these
+ * cases, this method assumes that the path is incorrect (since paths may change) and finds the <i>correct path</i> given the
+ * UUID.
+ * </p>
+ * <p>
+ * This method will also find the path when the location contains just the UUID.
+ * </p>
+ *
+ * @param original the original location; may not be null
+ * @return the actual location, which includes the verified location and additional information needed by this method that may
+ * be usable after this method is called
+ * @throws PathNotFoundException if the location does not represent a location that could be found
+ */
+ protected ActualLocation getActualLocation( Location original ) throws PathNotFoundException {
+ assert original != null;
+
+ // Look for the UUID in the original ...
+ Property uuidProperty = original.getIdProperty(DnaLexicon.UUID);
+ String uuidString = uuidProperty.isEmpty() ? null : stringFactory.create(uuidProperty.getFirstValue());
+
+ // If the original location has a UUID, then use that to find the child entity that represents the location ...
+ if (uuidString != null) {
+ // The original has a UUID, so use that to find the child entity.
+ // Then walk up the ancestors and build the path.
+ LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
+ ChildEntity entity = null;
+ ChildEntity childEntity = null;
+ do {
+ String childUuid = uuidString;
+ Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
+ query.setParameter("childUuidString", childUuid);
+ try {
+ // Find the parent of the UUID ...
+ entity = (ChildEntity)query.getSingleResult();
+ if (childEntity == null) childEntity = entity;
+ String localName = entity.getChildName();
+ String uri = entity.getChildNamespace().getUri();
+ Integer sns = entity.getSameNameSiblingIndex();
+ Name name = nameFactory.create(uri, localName);
+ if (sns != null) {
+ segments.addFirst(pathFactory.createSegment(name, sns));
+ } else {
+ segments.addFirst(pathFactory.createSegment(name));
+ }
+ } catch (NoResultException e) {
+ entity = null;
+ }
+ } while (entity != null);
+ Path fullPath = pathFactory.createAbsolutePath(segments);
+ return new ActualLocation(new Location(fullPath, uuidProperty), uuidString, childEntity);
+ }
+
+ // There is no UUID, so look for a path ...
Path path = original.getPath();
- if (path != null) {
- if (original.getIdProperty(DnaLexicon.UUID) != null) return original;
- return original.with(UUID.fromString(uuidString));
+ if (path == null) {
+ String propName = DnaLexicon.UUID.getString(getExecutionContext().getNamespaceRegistry());
+ String msg = JpaConnectorI18n.locationShouldHavePathAndOrProperty.text(getSourceName(), propName);
+ throw new PathNotFoundException(original, pathFactory.createRootPath(), msg);
}
- // There is no path, so find it by UUID ...
- path = getPathForUuid(uuidString);
- return new Location(path, UUID.fromString(uuidString));
- }
- protected String getUuidOf( Location location ) throws PathNotFoundException {
- String uuidString = null;
- if (location.hasIdProperties()) {
- // Look for the UUID ...
- Property uuidProperty = location.getIdProperty(DnaLexicon.UUID);
- if (uuidProperty != null && !uuidProperty.isEmpty()) {
- uuidString = stringFactory.create(uuidProperty.iterator().next());
- }
+ // Walk the child entities, starting at the root, down the to the path ...
+ if (path.isRoot()) {
+ return new ActualLocation(original.with(rootNodeUuid), rootNodeUuid.toString(), null);
}
- if (uuidString == null) {
- // Look up the node by using the path to walk down the children, starting at the root ...
- Path path = location.getPath();
- if (path == null) {
- // Location does not have path or DnaLexicon.UUID id property
- }
- assert path != null;
- if (path.isRoot()) {
- uuidString = rootNodeUuid.toString();
- } else {
- String parentUuid = this.rootNodeUuid.toString();
- ChildEntity child = null;
- for (Path.Segment segment : path) {
- child = findByPathSegment(parentUuid, segment);
- if (child == null) {
- // Determine the lowest path that exists ...
- Path lowest = path;
- while (lowest.getLastSegment() != segment) {
- lowest = lowest.getParent();
- }
- lowest = lowest.getParent();
- throw new PathNotFoundException(location, lowest);
- }
+ String parentUuid = this.rootNodeUuid.toString();
+ ChildEntity child = null;
+ for (Path.Segment segment : path) {
+ child = findByPathSegment(parentUuid, segment);
+ if (child == null) {
+ // Unable to complete the path, so prepare the exception by determining the lowest path that exists ...
+ Path lowest = path;
+ while (lowest.getLastSegment() != segment) {
+ lowest = lowest.getParent();
}
- assert child != null;
- uuidString = child.getId().getChildUuidString();
+ lowest = lowest.getParent();
+ throw new PathNotFoundException(original, lowest);
}
}
- assert uuidString != null;
- return uuidString;
+ assert child != null;
+ uuidString = child.getId().getChildUuidString();
+ return new ActualLocation(original.with(UUID.fromString(uuidString)), uuidString, child);
}
/**
@@ -351,7 +677,7 @@
* Build up the path for the node with the supplied UUID.
*
* @param uuidString the UUID of the node
- * @return the path to the node
+ * @return the path to the node; never null
*/
protected Path getPathForUuid( String uuidString ) {
ChildEntity entity = null;
@@ -359,7 +685,7 @@
LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
do {
// Find the parent of the UUID ...
- Query query = entities.createNamedQuery("ChildEntity.findParentByUuid");
+ Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
query.setParameter("childUuidString", childUuid);
try {
entity = (ChildEntity)query.getSingleResult();
@@ -379,6 +705,25 @@
return pathFactory.createAbsolutePath(segments);
}
+ protected String createHexValuesString( Collection<String> hexValues ) {
+ if (hexValues == null || hexValues.isEmpty()) return null;
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String hexValue : hexValues) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ sb.append(hexValue);
+ }
+ return sb.toString();
+ }
+
+ protected Collection<String> createHexValues( String hexValuesString ) {
+ return Arrays.asList(hexValuesString.split(","));
+ }
+
/**
* {@inheritDoc}
*
@@ -426,21 +771,38 @@
entity.setLength(length);
entity.setType(type);
ValueFactories factories = getExecutionContext().getValueFactories();
+ byte[] bytes = null;
switch (type) {
case BINARY:
Binary binary = factories.getBinaryFactory().create(value);
+ InputStream stream = null;
try {
binary.acquire();
- entity.setData(binary.getBytes());
+ stream = binary.getStream();
+ if (compressData) stream = new ZipInputStream(stream);
+ bytes = IoUtil.readBytes(stream);
} finally {
- binary.release();
+ try {
+ if (stream != null) stream.close();
+ } finally {
+ binary.release();
+ }
}
break;
default:
String str = factories.getStringFactory().create(value);
- entity.setData(str.getBytes());
+ bytes = str.getBytes();
+ if (compressData) {
+ InputStream strStream = new ZipInputStream(new ByteArrayInputStream(bytes));
+ try {
+ bytes = IoUtil.readBytes(strStream);
+ } finally {
+ strStream.close();
+ }
+ }
break;
}
+ entity.setData(bytes);
entities.persist(entity);
} else {
// There is already an existing value, so we'll reuse it and increment the usage count ...
@@ -448,6 +810,26 @@
}
}
+ @Immutable
+ protected static class ActualLocation {
+ /** The actual location */
+ protected final Location location;
+ /** The string-form of the UUID, supplied as a convenience. */
+ protected final String uuid;
+ /** The ChildEntity that represents the location, which may be null if the location represents the root node */
+ protected final ChildEntity childEntity;
+
+ protected ActualLocation( Location location,
+ String uuid,
+ ChildEntity childEntity ) {
+ assert location != null;
+ assert uuid != null;
+ this.location = location;
+ this.uuid = uuid;
+ this.childEntity = childEntity;
+ }
+ }
+
protected static class Namespaces {
private final EntityManager entityManager;
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -46,7 +46,10 @@
@Table( appliesTo = "DNA_BASIC_CHILDREN", indexes = @Index( name = "CHILDINDEX_INX", columnNames = {"PARENT_UUID", "CHILD_INDEX"} ) )
@NamedQueries( {
@NamedQuery( name = "ChildEntity.findByPathSegment", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName AND child.sameNameSiblingIndex = :sns" ),
- @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid" )} )
+ @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid" ),
+ @NamedQuery( name = "ChildEntity.findByChildUuid", query = "select child from ChildEntity as child where child.id.childUuidString = :childUuidString" ),
+ @NamedQuery( name = "ChildEntity.findMaximumSnsIndex", query = "select max(child.sameNameSiblingIndex) from ChildEntity as child where child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName" ),
+ @NamedQuery( name = "ChildEntity.findMaximumChildIndex", query = "select max(child.indexInParent) from ChildEntity as child where child.id.parentUuidString = :parentUuid" )} )
public class ChildEntity {
@Id
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -153,6 +153,11 @@
this.usageCount++;
}
+ public int decrementUsageCount() {
+ if (this.usageCount == 0) return 0;
+ return --this.usageCount;
+ }
+
/**
* @return compressed
*/
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -59,6 +59,9 @@
@Column( name = "COMPRESSED", nullable = true )
private Boolean compressed;
+ @Column( name = "LRG_VL_KEYS", nullable = true )
+ private String largeValueKeys;
+
public PropertiesEntity() {
}
@@ -131,6 +134,20 @@
}
/**
+ * @return largeValueKeys
+ */
+ public String getLargeValueKeys() {
+ return largeValueKeys;
+ }
+
+ /**
+ * @param largeValueKeys Sets largeValueKeys to the specified value.
+ */
+ public void setLargeValueKeys( String largeValueKeys ) {
+ this.largeValueKeys = largeValueKeys;
+ }
+
+ /**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -49,6 +49,10 @@
public NodeId() {
}
+ public NodeId( String uuidString ) {
+ this.uuidString = uuidString;
+ }
+
public NodeId( UUID uuid ) {
setUuid(uuid);
}
Modified: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -29,10 +29,14 @@
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import java.util.UUID;
import org.jboss.dna.common.SystemFailureException;
import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.connector.store.jpa.models.basic.LargeValueEntity;
+import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.properties.Binary;
import org.jboss.dna.graph.properties.DateTime;
@@ -52,12 +56,15 @@
private final PropertyFactory propertyFactory;
private final ValueFactories valueFactories;
private final LargeValues largeValues;
+ private final boolean excludeUuidProperty;
public Serializer( ExecutionContext context,
- LargeValues largeValues ) {
+ LargeValues largeValues,
+ boolean excludeUuidProperty ) {
this.propertyFactory = context.getPropertyFactory();
this.valueFactories = context.getValueFactories();
this.largeValues = largeValues;
+ this.excludeUuidProperty = excludeUuidProperty;
}
/**
@@ -102,19 +109,21 @@
* @param stream the stream where the properties' values are to be serialized; may not be null
* @param number the number of properties exposed by the supplied <code>properties</code> iterator; must be 0 or positive
* @param properties the iterator over the properties that are to be serialized; may not be null
+ * @param largeValueHexHashes the collection into which any large value hashes should be recordeed
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
- * @see #deserializeProperties(ObjectInputStream, Collection)
- * @see #serializeProperty(ObjectOutputStream, Property)
+ * @see #deserializeAllProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property, Collection)
*/
public void serializeProperties( ObjectOutputStream stream,
int number,
- Iterable<Property> properties ) throws IOException {
+ Iterable<Property> properties,
+ Collection<String> largeValueHexHashes ) throws IOException {
assert number >= 0;
assert properties != null;
stream.writeInt(number);
for (Property property : properties) {
if (property == null) continue;
- serializeProperty(stream, property);
+ serializeProperty(stream, property, largeValueHexHashes);
}
}
@@ -135,16 +144,21 @@
*
* @param stream the stream where the property's values are to be serialized; may not be null
* @param property the property to be serialized; may not be null
+ * @param largeValueHexHashes the collection into which any large value hashes should be recordeed
+ * @return true if the property was serialized, or false if it was not
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
- * @see #serializeProperties(ObjectOutputStream, int, Iterable)
- * @see #deserializeProperty(ObjectInputStream)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, Collection)
+ * @see #deserializePropertyValues(ObjectInputStream, Name, boolean)
*/
- public void serializeProperty( ObjectOutputStream stream,
- Property property ) throws IOException {
+ public boolean serializeProperty( ObjectOutputStream stream,
+ Property property,
+ Collection<String> largeValueHexHashes ) throws IOException {
assert stream != null;
assert property != null;
+ final Name name = property.getName();
+ if (this.excludeUuidProperty && DnaLexicon.UUID.equals(name)) return false;
// Write the name ...
- stream.writeObject(property.getName().getString());
+ stream.writeObject(name.getString());
// Write the number of values ...
stream.writeInt(property.size());
for (Object value : property) {
@@ -158,6 +172,7 @@
stream.write(hash);
stream.writeLong(stringValue.length());
// Now write to the large objects ...
+ largeValueHexHashes.add(StringUtil.getHexString(hash));
largeValues.write(computeHash(stringValue), stringValue.length(), PropertyType.STRING, stringValue);
} else {
stream.writeChar('S');
@@ -243,6 +258,7 @@
}
// If this is a large value and the binary has been released, write it to the large objects ...
if (largeValues != null && hash != null) {
+ largeValueHexHashes.add(StringUtil.getHexString(hash));
largeValues.write(hash, length, PropertyType.BINARY, value);
}
} else {
@@ -252,6 +268,7 @@
}
}
stream.flush();
+ return true;
}
/**
@@ -261,11 +278,11 @@
* @param properties the collection into which each deserialized property is to be placed; may not be null
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
* @throws ClassNotFoundException if the class for the value's object could not be found
- * @see #deserializeProperty(ObjectInputStream)
- * @see #serializeProperties(ObjectOutputStream, int, Iterable)
+ * @see #deserializePropertyValues(ObjectInputStream, Name, boolean)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, Collection)
*/
- public void deserializeProperties( ObjectInputStream stream,
- Collection<Property> properties ) throws IOException, ClassNotFoundException {
+ public void deserializeAllProperties( ObjectInputStream stream,
+ Collection<Property> properties ) throws IOException, ClassNotFoundException {
assert propertyFactory != null;
assert valueFactories != null;
assert stream != null;
@@ -281,105 +298,177 @@
}
/**
+ * Deserialize the serialized properties on the supplied object stream.
+ *
+ * @param stream the stream that contains the serialized properties; may not be null
+ * @param properties the collection into which each deserialized property is to be placed; may not be null
+ * @param names the names of the properties that should be deserialized; should not be null or empty
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not be found
+ * @see #deserializePropertyValues(ObjectInputStream, Name, boolean)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable, Collection)
+ */
+ public void deserializeSomeProperties( ObjectInputStream stream,
+ Collection<Property> properties,
+ Name... names ) throws IOException, ClassNotFoundException {
+ assert stream != null;
+ assert properties != null;
+ assert names != null;
+ assert names.length > 0;
+ Name nameToRead = null;
+ Set<Name> namesToRead = null;
+ if (names.length == 1) {
+ nameToRead = names[0];
+ } else {
+ namesToRead = new HashSet<Name>();
+ for (Name name : names) {
+ if (name != null) namesToRead.add(name);
+ }
+ }
+
+ // Read the number of properties ...
+ boolean read = false;
+ int count = stream.readInt();
+
+ // Now, read the properties (or skip the ones that we're not supposed to read) ...
+ for (int i = 0; i != count; ++i) {
+ // Read the name ...
+ String nameStr = (String)stream.readObject();
+ Name name = valueFactories.getNameFactory().create(nameStr);
+ assert name != null;
+ read = name.equals(nameToRead) || (namesToRead != null && namesToRead.contains(namesToRead));
+ // Now read the property values ...
+ Object[] values = deserializePropertyValues(stream, name, !read);
+ // Add the property to the collection ...
+ Property property = propertyFactory.create(name, values);
+ assert property != null;
+ properties.add(property);
+ }
+ }
+
+ /**
* Deserialize the serialized property on the supplied object stream.
*
* @param stream the stream that contains the serialized properties; may not be null
- * @return the deserialized property; never null
+ * @return the deserialized property values, or an empty list if there are no values
* @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
* @throws ClassNotFoundException if the class for the value's object could not be found
- * @see #deserializeProperties(ObjectInputStream, Collection)
- * @see #serializeProperty(ObjectOutputStream, Property)
+ * @see #deserializeAllProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property, Collection)
*/
public Property deserializeProperty( ObjectInputStream stream ) throws IOException, ClassNotFoundException {
- assert propertyFactory != null;
- assert valueFactories != null;
- assert stream != null;
- assert largeValues != null;
// Read the name ...
String nameStr = (String)stream.readObject();
Name name = valueFactories.getNameFactory().create(nameStr);
assert name != null;
+ // Now read the property values ...
+ Object[] values = deserializePropertyValues(stream, name, false);
+ // Add the property to the collection ...
+ return propertyFactory.create(name, values);
+ }
+
+ /**
+ * Deserialize the serialized property on the supplied object stream.
+ *
+ * @param stream the stream that contains the serialized properties; may not be null
+ * @param propertyName the name of the property being deserialized
+ * @param skip true if the values don't need to be read, or false if they are to be read
+ * @return the deserialized property values, or an empty list if there are no values
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not be found
+ * @see #deserializeAllProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property, Collection)
+ */
+ public Object[] deserializePropertyValues( ObjectInputStream stream,
+ Name propertyName,
+ boolean skip ) throws IOException, ClassNotFoundException {
+ assert stream != null;
+ assert propertyName != null;
// Read the number of values ...
int size = stream.readInt();
- Object[] values = new Object[size];
+ Object[] values = skip ? null : new Object[size];
for (int i = 0; i != size; ++i) {
Object value = null;
// Read the type of value ...
char type = stream.readChar();
switch (type) {
case 'S':
- // String
- value = valueFactories.getStringFactory().create((String)stream.readObject());
+ String stringValue = (String)stream.readObject();
+ if (!skip) value = valueFactories.getStringFactory().create(stringValue);
break;
case 'b':
- // boolean
- value = valueFactories.getBooleanFactory().create(stream.readBoolean());
+ boolean booleanValue = stream.readBoolean();
+ if (!skip) value = valueFactories.getBooleanFactory().create(booleanValue);
break;
case 'i':
- // integer
- value = valueFactories.getLongFactory().create(stream.readInt());
+ int intValue = stream.readInt();
+ if (!skip) value = valueFactories.getLongFactory().create(intValue);
break;
case 'l':
- // long
- value = valueFactories.getLongFactory().create(stream.readLong());
+ long longValue = stream.readLong();
+ if (!skip) value = valueFactories.getLongFactory().create(longValue);
break;
case 's':
- // short
- value = valueFactories.getLongFactory().create(stream.readShort());
+ short shortValue = stream.readShort();
+ if (!skip) value = valueFactories.getLongFactory().create(shortValue);
break;
case 'f':
- // float
- value = valueFactories.getDoubleFactory().create(stream.readFloat());
+ float floatValue = stream.readFloat();
+ if (!skip) value = valueFactories.getDoubleFactory().create(floatValue);
break;
case 'd':
- // double
- value = valueFactories.getDoubleFactory().create(stream.readDouble());
+ double doubleValue = stream.readDouble();
+ if (!skip) value = valueFactories.getDoubleFactory().create(doubleValue);
break;
case 'c':
- // double
- value = valueFactories.getStringFactory().create("" + stream.readChar());
+ // char
+ String charValue = "" + stream.readChar();
+ if (!skip) value = valueFactories.getStringFactory().create(charValue);
break;
case 'U':
// UUID
long msb = stream.readLong();
long lsb = stream.readLong();
- UUID uuid = new UUID(msb, lsb);
- value = valueFactories.getUuidFactory().create(uuid);
+ if (!skip) {
+ UUID uuid = new UUID(msb, lsb);
+ value = valueFactories.getUuidFactory().create(uuid);
+ }
break;
case 'I':
// URI
String uriStr = (String)stream.readObject();
- value = valueFactories.getUriFactory().create(uriStr);
+ if (!skip) value = valueFactories.getUriFactory().create(uriStr);
break;
case 'N':
// Name
String nameValueStr = (String)stream.readObject();
- value = valueFactories.getNameFactory().create(nameValueStr);
+ if (!skip) value = valueFactories.getNameFactory().create(nameValueStr);
break;
case 'P':
// Path
String pathStr = (String)stream.readObject();
- value = valueFactories.getPathFactory().create(pathStr);
+ if (!skip) value = valueFactories.getPathFactory().create(pathStr);
break;
case 'T':
// DateTime
String dateTimeStr = (String)stream.readObject();
- value = valueFactories.getDateFactory().create(dateTimeStr);
+ if (!skip) value = valueFactories.getDateFactory().create(dateTimeStr);
break;
case 'D':
// BigDecimal
Object bigDecimal = stream.readObject();
- value = valueFactories.getDecimalFactory().create(bigDecimal);
+ if (!skip) value = valueFactories.getDecimalFactory().create(bigDecimal);
break;
case 'R':
// Reference
- value = valueFactories.getReferenceFactory().create((String)stream.readObject());
+ String refValue = (String)stream.readObject();
+ if (!skip) value = valueFactories.getReferenceFactory().create(refValue);
break;
case 'B':
// Binary
// Read the length of the content ...
long binaryLength = stream.readLong();
- value = valueFactories.getBinaryFactory().create(stream, binaryLength);
+ if (!skip) value = valueFactories.getBinaryFactory().create(stream, binaryLength);
break;
case 'L':
// Large object ...
@@ -389,19 +478,17 @@
stream.read(hash);
// Read the length of the content ...
long length = stream.readLong();
- value = largeValues.read(valueFactories, hash, length);
+ if (!skip) value = largeValues.read(valueFactories, hash, length);
break;
default:
// All other objects ...
Object object = stream.readObject();
- value = valueFactories.getObjectFactory().create(object);
+ if (!skip) value = valueFactories.getObjectFactory().create(object);
break;
}
- assert value != null;
- values[i] = value;
+ if (value != null) values[i] = value;
}
- // Add the property to the collection ...
- return propertyFactory.create(name, values);
+ return values;
}
public byte[] computeHash( String value ) {
Modified: trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties 2008-11-24 15:13:59 UTC (rev 647)
@@ -29,5 +29,8 @@
errorSettingContextClassLoader = Error while setting the current context class loader for JAP repository source {0} to classloader name {1}
existingStoreSpecifiesUnknownModel = The JPA repository source {0} uses a model that is not known: {1}
unableToReadLargeValue = Unable to read from {0} the large property with hash = {1}
+unableToMoveRootNode = Unable to move the root node to another location in {0}
+locationShouldHavePathAndOrProperty = The source {0} is unable to find a node without a path or a {1} property
+
basicModelDescription = Database model that stores node properties as opaque records and children as transparent records. Large property values are stored separately.
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -47,6 +47,7 @@
private CachePolicy cachePolicy;
private UUID rootNodeUuid;
private long largeValueSize;
+ private boolean compressData;
@Before
public void beforeEach() throws Exception {
@@ -54,6 +55,7 @@
model = new BasicModel();
rootNodeUuid = UUID.randomUUID();
largeValueSize = 2 ^ 10; // 1 kilobyte
+ compressData = false;
// Connect to the database ...
Ejb3Configuration configurator = new Ejb3Configuration();
@@ -72,7 +74,7 @@
// Create the connection ...
cachePolicy = mock(CachePolicy.class);
- connection = new JpaConnection("source", cachePolicy, manager, model, rootNodeUuid, largeValueSize);
+ connection = new JpaConnection("source", cachePolicy, manager, model, rootNodeUuid, largeValueSize, compressData);
}
@After
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -107,7 +107,8 @@
ExecutionContext context,
EntityManager entityManager,
UUID rootNodeUuid,
- long largeValueMinimumSizeInBytes ) {
+ long largeValueMinimumSizeInBytes,
+ boolean compressData ) {
return requestProcessor;
}
}
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -127,7 +127,7 @@
EntityManager manager = mock(EntityManager.class);
EntityTransaction txn = mock(EntityTransaction.class);
stub(manager.getTransaction()).toReturn(txn);
- RequestProcessor proc = model.createRequestProcessor("test source", context, manager, UUID.randomUUID(), 100);
+ RequestProcessor proc = model.createRequestProcessor("test source", context, manager, UUID.randomUUID(), 100, false);
assertThat(proc, is(notNullValue()));
}
Modified: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java 2008-11-23 09:57:49 UTC (rev 646)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java 2008-11-24 15:13:59 UTC (rev 647)
@@ -34,8 +34,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.SecureHash;
@@ -60,6 +62,7 @@
private LargeValuesHolder largeValues;
private PropertyFactory propertyFactory;
private ValueFactories valueFactories;
+ private Set<String> largeValueHexHashes;
@Before
public void beforeEach() {
@@ -67,7 +70,8 @@
propertyFactory = context.getPropertyFactory();
valueFactories = context.getValueFactories();
largeValues = new LargeValuesHolder();
- serializer = new Serializer(context, largeValues);
+ largeValueHexHashes = new HashSet<String>();
+ serializer = new Serializer(context, largeValues, false);
}
@Test
@@ -75,6 +79,7 @@
Property prop = createProperty("p1", new Long(1));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -82,6 +87,7 @@
Property prop = createProperty("p1", new Integer(1));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -89,6 +95,7 @@
Property prop = createProperty("p1", new Short((short)1));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -96,6 +103,7 @@
Property prop = createProperty("p1", new Float(1.0f));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -103,6 +111,7 @@
Property prop = createProperty("p1", new Double(1.0d));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -110,6 +119,7 @@
Property prop = createProperty("p1", new Boolean(true));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -117,6 +127,7 @@
Property prop = createProperty("p1", valueFactories.getNameFactory().create("something"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -124,6 +135,7 @@
Property prop = createProperty("p1", valueFactories.getPathFactory().create("/a/b/c/something"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -131,10 +143,12 @@
Property prop = createProperty("p1", valueFactories.getDateFactory().createUtc());
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
prop = createProperty("p1", valueFactories.getDateFactory().create());
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -142,6 +156,7 @@
Property prop = createProperty("p1", UUID.randomUUID());
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -149,6 +164,7 @@
Property prop = createProperty("p1", new URI("http://example.com"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -157,6 +173,7 @@
Property prop = createProperty("p1", valueFactories.getReferenceFactory().create(uuid.toString()));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -164,6 +181,7 @@
Property prop = createProperty("p1", valueFactories.getDecimalFactory().create("1.0123455243284347375478525485466895512"));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -172,6 +190,7 @@
Property prop = createProperty("p1", valueFactories.getBinaryFactory().create(value));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -180,6 +199,7 @@
Property prop = createProperty("p1", valueFactories.getBinaryFactory().create(value));
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(1));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -187,6 +207,7 @@
Property prop = createProperty("p1", "v1");
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(0));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -196,6 +217,7 @@
assertSerializableAndDeserializable(serializer, prop);
assertThat(largeValues.getCount(), is(1));
assertThat(largeValues.get(value).value, is((Object)value));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -208,6 +230,7 @@
assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4);
assertThat(largeValues.getCount(), is(1));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
@Test
@@ -223,6 +246,7 @@
assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4, prop5, prop6);
assertThat(largeValues.getCount(), is(2));
+ assertThat(largeValueHexHashes.size(), is(largeValues.getCount()));
}
protected Property createProperty( String name,
@@ -237,7 +261,7 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
- serializer.serializeProperty(oos, property);
+ serializer.serializeProperty(oos, property, largeValueHexHashes);
} finally {
oos.close();
}
@@ -264,7 +288,7 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
- serializer.serializeProperties(oos, propertyList.size(), propertyList);
+ serializer.serializeProperties(oos, propertyList.size(), propertyList, largeValueHexHashes);
} finally {
oos.close();
}
@@ -274,7 +298,7 @@
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
try {
- serializer.deserializeProperties(ois, outputProperties);
+ serializer.deserializeAllProperties(ois, outputProperties);
} finally {
ois.close();
}
17 years, 1 month
DNA SVN: r646 - trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3.
by dna-commits@lists.jboss.org
Author: spagop
Date: 2008-11-23 04:57:49 -0500 (Sun, 23 Nov 2008)
New Revision: 646
Modified:
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java
Log:
support for swimlane and tasknode parsing
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java 2008-11-23 09:57:49 UTC (rev 646)
@@ -28,7 +28,7 @@
* @author Serge Pagop
*/
public class JPDL3StartStateMetadata {
- private String name ="";
+ private String name = "";
private List<JPDL3TransitionMetadata> transitions = new ArrayList<JPDL3TransitionMetadata>();
public String getName() {
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java 2008-11-23 09:57:49 UTC (rev 646)
@@ -31,7 +31,7 @@
/**
* The name.
*/
- private String name="";
+ private String name = "";
/**
* The JPDL3AssignmentMetadata
@@ -41,12 +41,12 @@
/**
* The actor id expression.
*/
- private String actorIdExpression ="";
+ private String actorIdExpression = "";
/**
* The pooledActorsExpression.
*/
- private String pooledActorsExpression="";
+ private String pooledActorsExpression = "";
/**
* Get the name of the specific swimlane.
@@ -74,13 +74,13 @@
public JPDL3AssignmentMetadata getAssignment() {
return this.assignment;
}
-
+
/**
* Set the delegated instance, the assignment.
*
* @param assignment - the delegated instance.
*/
- public void setAssignment(JPDL3AssignmentMetadata assignment) {
+ public void setAssignment( JPDL3AssignmentMetadata assignment ) {
this.assignment = assignment;
}
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java 2008-11-23 09:57:49 UTC (rev 646)
@@ -22,25 +22,26 @@
package org.jboss.dna.sequencer.jpdl3;
/**
- * @author sp
- *
+ * This represent a task for a human.
+ *
+ * @author Serge Pagop
*/
public class JPDL3TaskMetadata {
-
+
/**
* The name.
*/
- private String name="";
-
+ private String name = "";
+
/**
* The dueDate.
*/
- private String dueDate="";
+ private String dueDate = "";
/**
* The swimlane.
*/
- private String swimlane="";
+ private String swimlane = "";
/**
* @param name
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java 2008-11-23 09:57:49 UTC (rev 646)
@@ -25,16 +25,17 @@
import java.util.List;
/**
+ * This a task node.
+ *
* @author Serge Pagop.
- *
*/
public class JPDL3TaskNodeMetadata {
-
+
/**
* The name.
*/
- private String name="";
-
+ private String name = "";
+
/**
* The tasks.
*/
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java 2008-11-23 09:57:49 UTC (rev 646)
@@ -22,12 +22,14 @@
package org.jboss.dna.sequencer.jpdl3;
/**
- * @author Serge Pagop (serge.pagop(a)googlemail.com)
+ * The transition.
+ *
+ * @author Serge Pagop
*/
public class JPDL3TransitionMetadata {
- private String name="";
- private String to="";
+ private String name = "";
+ private String to = "";
/**
* @return the name
17 years, 1 month
DNA SVN: r645 - trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3.
by dna-commits@lists.jboss.org
Author: spagop
Date: 2008-11-22 14:50:22 -0500 (Sat, 22 Nov 2008)
New Revision: 645
Modified:
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3EndStateMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java
Log:
support for swimlane and tasknode parsing
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3EndStateMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3EndStateMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3EndStateMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
@@ -22,10 +22,11 @@
package org.jboss.dna.sequencer.jpdl3;
/**
- * @author Serge Pagop (serge.pagop(a)googlemail.com)
+ * @author Serge Pagop
*/
public class JPDL3EndStateMetadata {
- private String name;
+
+ private String name = "";
/**
* @return the endName
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3StartStateMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
@@ -28,7 +28,7 @@
* @author Serge Pagop
*/
public class JPDL3StartStateMetadata {
- private String name;
+ private String name ="";
private List<JPDL3TransitionMetadata> transitions = new ArrayList<JPDL3TransitionMetadata>();
public String getName() {
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
@@ -31,7 +31,7 @@
/**
* The name.
*/
- private String name;
+ private String name="";
/**
* The JPDL3AssignmentMetadata
@@ -41,12 +41,12 @@
/**
* The actor id expression.
*/
- private String actorIdExpression;
+ private String actorIdExpression ="";
/**
* The pooledActorsExpression.
*/
- private String pooledActorsExpression;
+ private String pooledActorsExpression="";
/**
* Get the name of the specific swimlane.
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
@@ -30,17 +30,17 @@
/**
* The name.
*/
- private String name;
+ private String name="";
/**
* The dueDate.
*/
- private String dueDate;
+ private String dueDate="";
/**
* The swimlane.
*/
- private String swimlane;
+ private String swimlane="";
/**
* @param name
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
@@ -33,7 +33,7 @@
/**
* The name.
*/
- private String name;
+ private String name="";
/**
* The tasks.
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TransitionMetadata.java 2008-11-22 19:50:22 UTC (rev 645)
@@ -26,8 +26,8 @@
*/
public class JPDL3TransitionMetadata {
- private String name;
- private String to;
+ private String name="";
+ private String to="";
/**
* @return the name
17 years, 1 month
DNA SVN: r644 - in trunk/extensions/dna-sequencer-jbpm-jpdl/src: test/java/org/jboss/dna/sequencer/jpdl3 and 1 other directories.
by dna-commits@lists.jboss.org
Author: spagop
Date: 2008-11-22 14:45:51 -0500 (Sat, 22 Nov 2008)
New Revision: 644
Added:
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3AssignmentMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataConstants.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/gpd.xml
trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processimage.jpg
Modified:
trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3Metadata.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataTest.java
trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processdefinition.xml
Log:
support for swimlane and tasknode parsing
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3AssignmentMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3AssignmentMetadata.java (rev 0)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3AssignmentMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -0,0 +1,96 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.jpdl3;
+
+/**
+ * @author Serge Pagop
+ */
+public class JPDL3AssignmentMetadata {
+
+ /**
+ * The full qualified class name.
+ */
+ private String fqClassName = "";
+
+ /**
+ * The expression.
+ */
+ private String expression = "";
+
+ /**
+ * The config type.
+ */
+ private String configType = "";
+
+ /**
+ * Get the full qualified name of the class delegation.
+ *
+ * @return the fqClassName.
+ */
+ public String getFqClassName() {
+ return this.fqClassName;
+ }
+
+ /**
+ * Set the full qualified name of the class delegation.
+ *
+ * @param fqClassName Sets fqClassName to the specified value.
+ */
+ public void setFqClassName( String fqClassName ) {
+ this.fqClassName = fqClassName;
+ }
+
+ /**
+ * Get the assignment expression for the jpdl identity component.
+ *
+ * @return the expression.
+ */
+ public String getExpression() {
+ return this.expression;
+ }
+
+ /**
+ * Set the expression.
+ *
+ * @param expression Sets expression to the specified value.
+ */
+ public void setExpression( String expression ) {
+ this.expression = expression;
+ }
+
+ /**
+ * Get the configType.
+ *
+ * @return configType
+ */
+ public String getConfigType() {
+ return this.configType;
+ }
+
+ /**
+ * @param configType Sets configType to the specified value.
+ */
+ public void setConfigType( String configType ) {
+ this.configType = configType;
+ }
+
+}
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3Metadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3Metadata.java 2008-11-20 19:15:17 UTC (rev 643)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3Metadata.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -24,11 +24,19 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.node.EndState;
import org.jbpm.graph.node.StartState;
+import org.jbpm.graph.node.TaskNode;
+import org.jbpm.instantiation.Delegation;
+import org.jbpm.taskmgmt.def.Swimlane;
+import org.jbpm.taskmgmt.def.Task;
+import org.jbpm.taskmgmt.def.TaskMgmtDefinition;
+import static org.jboss.dna.sequencer.jpdl3.JPDL3MetadataConstants.*;
/**
* The jBPM Process definition language meta data.
@@ -36,11 +44,32 @@
* @author Serge Pagop
*/
public class JPDL3Metadata {
+
+ /**
+ * The process definition name.
+ */
private String pdName;
+
+ /**
+ * The start node of the process definition.
+ */
private JPDL3StartStateMetadata jPDL3StartStateMetadata;
+
+ /**
+ * The end node of the process definition.
+ */
private JPDL3EndStateMetadata jPDL3EndStateMetadata;
-
+ /**
+ * The swimlanes of the process definitions
+ */
+ List<JPDL3SwimlaneMetadata> swimlanes = new ArrayList<JPDL3SwimlaneMetadata>();
+
+ /**
+ * The task nodes of the process definitions.
+ */
+ private List<JPDL3TaskNodeMetadata> taskNodes = new ArrayList<JPDL3TaskNodeMetadata>();
+
private JPDL3Metadata() {
// prevent construction
}
@@ -54,12 +83,45 @@
@SuppressWarnings( {"unchecked", "cast"} )
public static JPDL3Metadata instance( InputStream stream ) {
ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(stream);
+ List<JPDL3SwimlaneMetadata> swimlaneContainer = new ArrayList<JPDL3SwimlaneMetadata>();
+ List<JPDL3TaskNodeMetadata> taskNodeContainer = new ArrayList<JPDL3TaskNodeMetadata>();
+
if (processDefinition != null) {
JPDL3Metadata jplMetadata = new JPDL3Metadata();
if (processDefinition.getName() != null) {
jplMetadata.setPdName(processDefinition.getName());
}
+
+ TaskMgmtDefinition taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
+ if (taskMgmtDefinition != null) {
+ // Get the swimlanes of the process definition, if there is one.
+ Map<String, Swimlane> mapOfSwimlanes = taskMgmtDefinition.getSwimlanes();
+ Set<String> swimlaneKeys = mapOfSwimlanes.keySet();
+ for (String swimlaneKey : swimlaneKeys) {
+ Swimlane swimlane = mapOfSwimlanes.get(swimlaneKey);
+ JPDL3SwimlaneMetadata jPDL3SwimlaneMetadata = new JPDL3SwimlaneMetadata();
+ jPDL3SwimlaneMetadata.setName(swimlane.getName());
+ if (swimlane.getActorIdExpression() != null) jPDL3SwimlaneMetadata.setActorIdExpression(swimlane.getActorIdExpression());
+ if (swimlane.getPooledActorsExpression() != null) jPDL3SwimlaneMetadata.setPooledActorsExpression(swimlane.getPooledActorsExpression());
+ Delegation delegation = swimlane.getAssignmentDelegation();
+ if (delegation != null) {
+ JPDL3AssignmentMetadata jPDL3AssignmentMetadata = new JPDL3AssignmentMetadata();
+ // full qualified class name.
+ jPDL3AssignmentMetadata.setFqClassName(delegation.getClassName());
+ // config type
+ if (delegation.getConfigType() != null) jPDL3AssignmentMetadata.setConfigType(delegation.getConfigType());
+ // expression assignment
+ if (EXPRESSION_ASSIGNMENT_HANLDER_DELEGATION_CN.equals(delegation.getClassName())) jPDL3AssignmentMetadata.setExpression(delegation.getConfiguration());
+ jPDL3SwimlaneMetadata.setAssignment(jPDL3AssignmentMetadata);
+ }
+ swimlaneContainer.add(jPDL3SwimlaneMetadata);
+ // with expression
+ }
+ }
+ jplMetadata.setSwimlanes(swimlaneContainer);
+
List<Node> nodes = (List<Node>)processDefinition.getNodes();
+
for (Node node : nodes) {
if (node instanceof StartState) {
StartState startState = (StartState)node;
@@ -91,6 +153,56 @@
}
jplMetadata.setEndStateMetadata(jPDL3EndStateMetadata);
}
+
+ // TaskNode
+ if (node instanceof TaskNode) {
+ TaskNode taskNode = (TaskNode)node;
+ JPDL3TaskNodeMetadata jPDL3TaskNodeMetadata = new JPDL3TaskNodeMetadata();
+
+ if(taskNode.getName() != null) {
+ jPDL3TaskNodeMetadata.setName(taskNode.getName());
+ }
+
+ Map<String, Task> tasks = taskNode.getTasksMap();
+ List<JPDL3TaskMetadata> taskList = new ArrayList<JPDL3TaskMetadata>();
+
+ if(!tasks.isEmpty()) {
+ Set<String> keys = tasks.keySet();
+ for (String key : keys) {
+ Task task = tasks.get(key);
+ JPDL3TaskMetadata jPDL3TaskMetadata = new JPDL3TaskMetadata();
+ if(task.getName() != null)
+ jPDL3TaskMetadata.setName(task.getName());
+ if(task.getDueDate() != null)
+ jPDL3TaskMetadata.setDueDate(task.getDueDate());
+ taskList.add(jPDL3TaskMetadata);
+
+ if(task.getSwimlane() != null) {
+ Swimlane swimlane = task.getSwimlane();
+ jPDL3TaskMetadata.setSwimlane(swimlane.getName());
+ }
+ }
+ }
+ jPDL3TaskNodeMetadata.setTasks(taskList);
+
+ // transitions
+ List<JPDL3TransitionMetadata> transitions = new ArrayList<JPDL3TransitionMetadata>();
+ for (Transition transition : (List<Transition>)taskNode.getLeavingTransitions()) {
+ JPDL3TransitionMetadata jPDL3TransitionMetadata = new JPDL3TransitionMetadata();
+ if (transition.getName() != null) {
+ jPDL3TransitionMetadata.setName(transition.getName());
+ }
+ Node toNode = transition.getTo();
+ if (toNode != null) {
+ jPDL3TransitionMetadata.setTo(toNode.getName());
+ }
+ transitions.add(jPDL3TransitionMetadata);
+ }
+ jPDL3TaskNodeMetadata.setTransitions(transitions);
+
+ taskNodeContainer.add(jPDL3TaskNodeMetadata);
+ jplMetadata.setTaskNodes(taskNodeContainer);
+ }
}
return jplMetadata;
}
@@ -119,18 +231,16 @@
* @return the jPDL3StartStateMetadata.
*/
public JPDL3StartStateMetadata getStartStateMetadata() {
- return jPDL3StartStateMetadata;
+ return this.jPDL3StartStateMetadata;
}
-
+
/**
- *
* @return the jPDL3EndStateMetadata.
*/
public JPDL3EndStateMetadata getEndStateMetadata() {
- return jPDL3EndStateMetadata;
+ return this.jPDL3EndStateMetadata;
}
-
-
+
/**
* @param jPDL3StartStateMetadata the jPDL3StartStateMetadata to set
*/
@@ -144,4 +254,36 @@
public void setEndStateMetadata( JPDL3EndStateMetadata jPDL3EndStateMetadata ) {
this.jPDL3EndStateMetadata = jPDL3EndStateMetadata;
}
+
+ /**
+ * Get a list of all swimlane of the process definition
+ *
+ * @return a list of all swimlane of the process definition. this can also be a empty list.
+ */
+ public List<JPDL3SwimlaneMetadata> getSwimlanes() {
+ return this.swimlanes;
+ }
+
+ /**
+ * Set a list with some swimlanes for the process definition.
+ *
+ * @param swimlanes - the swimlanes.
+ */
+ public void setSwimlanes( List<JPDL3SwimlaneMetadata> swimlanes ) {
+ this.swimlanes = swimlanes;
+ }
+
+ /**
+ * @return the task nodes
+ */
+ public List<JPDL3TaskNodeMetadata> getTaskNodes() {
+ return this.taskNodes;
+ }
+
+ /**
+ * @param taskNodes Sets taskNodes to the specified value.
+ */
+ public void setTaskNodes( List<JPDL3TaskNodeMetadata> taskNodes ) {
+ this.taskNodes = taskNodes;
+ }
}
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataConstants.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataConstants.java (rev 0)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataConstants.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -0,0 +1,29 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.jpdl3;
+
+/**
+ * @author Serge Pagop
+ */
+public class JPDL3MetadataConstants {
+ public static final String EXPRESSION_ASSIGNMENT_HANLDER_DELEGATION_CN = "org.jbpm.identity.assignment.ExpressionAssignmentHandler";
+}
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java (rev 0)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3SwimlaneMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -0,0 +1,115 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.jpdl3;
+
+/**
+ * Metdata class represent a swimlane in a jpdl xml file.
+ *
+ * @author Serge Pagop
+ */
+public class JPDL3SwimlaneMetadata {
+
+ /**
+ * The name.
+ */
+ private String name;
+
+ /**
+ * The JPDL3AssignmentMetadata
+ */
+ private JPDL3AssignmentMetadata assignment;
+
+ /**
+ * The actor id expression.
+ */
+ private String actorIdExpression;
+
+ /**
+ * The pooledActorsExpression.
+ */
+ private String pooledActorsExpression;
+
+ /**
+ * Get the name of the specific swimlane.
+ *
+ * @return name of the swimlane.
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Set the name of the swimlane.
+ *
+ * @param name - the name of the swimlane.
+ */
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ /**
+ * Get the delegated instance, the assignment.
+ *
+ * @return assignment - the delegated instance.
+ */
+ public JPDL3AssignmentMetadata getAssignment() {
+ return this.assignment;
+ }
+
+ /**
+ * Set the delegated instance, the assignment.
+ *
+ * @param assignment - the delegated instance.
+ */
+ public void setAssignment(JPDL3AssignmentMetadata assignment) {
+ this.assignment = assignment;
+ }
+
+ /**
+ * @param actorIdExpression
+ */
+ public void setActorIdExpression( String actorIdExpression ) {
+ this.actorIdExpression = actorIdExpression;
+ }
+
+ /**
+ * @return actorIdExpression
+ */
+ public String getActorIdExpression() {
+ return actorIdExpression;
+ }
+
+ /**
+ * @param pooledActorsExpression
+ */
+ public void setPooledActorsExpression( String pooledActorsExpression ) {
+ this.pooledActorsExpression = pooledActorsExpression;
+ }
+
+ /**
+ * @return pooledActorsExpression
+ */
+ public String getPooledActorsExpression() {
+ return pooledActorsExpression;
+ }
+
+}
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java (rev 0)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -0,0 +1,87 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.jpdl3;
+
+/**
+ * @author sp
+ *
+ */
+public class JPDL3TaskMetadata {
+
+ /**
+ * The name.
+ */
+ private String name;
+
+ /**
+ * The dueDate.
+ */
+ private String dueDate;
+
+ /**
+ * The swimlane.
+ */
+ private String swimlane;
+
+ /**
+ * @param name
+ */
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param dueDate
+ */
+ public void setDueDate( String dueDate ) {
+ this.dueDate = dueDate;
+ }
+
+ /**
+ * @return dueDate
+ */
+ public String getDueDate() {
+ return dueDate;
+ }
+
+ /**
+ * @return swimlane.
+ */
+ public String getSwimlane() {
+ return this.swimlane;
+ }
+
+ /**
+ * @param swimlane Sets swimlane to the specified value.
+ */
+ public void setSwimlane( String swimlane ) {
+ this.swimlane = swimlane;
+ }
+
+}
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java (rev 0)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/main/java/org/jboss/dna/sequencer/jpdl3/JPDL3TaskNodeMetadata.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -0,0 +1,90 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.sequencer.jpdl3;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Serge Pagop.
+ *
+ */
+public class JPDL3TaskNodeMetadata {
+
+ /**
+ * The name.
+ */
+ private String name;
+
+ /**
+ * The tasks.
+ */
+ private List<JPDL3TaskMetadata> tasks = new ArrayList<JPDL3TaskMetadata>();
+
+ /**
+ * The transition.
+ */
+ private List<JPDL3TransitionMetadata> transitions = new ArrayList<JPDL3TransitionMetadata>();
+
+ /**
+ * @param name
+ */
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param tasks
+ */
+ public void setTasks( List<JPDL3TaskMetadata> tasks ) {
+ this.tasks = tasks;
+ }
+
+ /**
+ * @return tasks
+ */
+ public List<JPDL3TaskMetadata> getTasks() {
+ return tasks;
+ }
+
+ /**
+ * @param transitions
+ */
+ public void setTransitions( List<JPDL3TransitionMetadata> transitions ) {
+ this.transitions = transitions;
+ }
+
+ /**
+ * @return transitions
+ */
+ public List<JPDL3TransitionMetadata> getTransitions() {
+ return transitions;
+ }
+
+}
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataTest.java
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataTest.java 2008-11-20 19:15:17 UTC (rev 643)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/java/org/jboss/dna/sequencer/jpdl3/JPDL3MetadataTest.java 2008-11-22 19:45:51 UTC (rev 644)
@@ -21,13 +21,14 @@
*/
package org.jboss.dna.sequencer.jpdl3;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
import java.io.InputStream;
import java.util.List;
-import static org.junit.Assert.*;
-import org.jboss.dna.sequencer.jpdl3.JPDL3EndStateMetadata;
-import org.jboss.dna.sequencer.jpdl3.JPDL3Metadata;
-import org.jboss.dna.sequencer.jpdl3.JPDL3StartStateMetadata;
-import org.jboss.dna.sequencer.jpdl3.JPDL3TransitionMetadata;
import org.jbpm.util.ClassLoaderUtil;
import org.junit.After;
import org.junit.Before;
@@ -55,11 +56,39 @@
}
@Test
+ public void shouldHaveOneOrMoreSwimlaneNode() {
+ List<JPDL3SwimlaneMetadata> swimlanes = metadata.getSwimlanes();
+ assertThat(swimlanes.isEmpty(), is(false));
+ for (JPDL3SwimlaneMetadata swimlane : swimlanes) {
+ if (swimlane.getName().equals("SL1")) {
+ assertThat(swimlane.getAssignment(), is(notNullValue()));
+ assertThat(swimlane.getAssignment().getFqClassName(), is("com.sample.assigned.Task1Handler"));
+ assertThat(swimlane.getAssignment().getConfigType(), is("constructor"));
+ } else if (swimlane.getName().equals("SL2")) {
+ assertThat(swimlane.getAssignment(), is(notNullValue()));
+ assertNotNull(swimlane.getAssignment());
+ assertThat(swimlane.getAssignment().getFqClassName(), is("com.sample.assigned.Task2Handler"));
+ } else if (swimlane.getName().equals("SL3")) {
+ assertThat(swimlane.getAssignment(), is(notNullValue()));
+ assertThat(swimlane.getAssignment().getFqClassName(),
+ is("org.jbpm.identity.assignment.ExpressionAssignmentHandler"));
+ assertThat(swimlane.getAssignment().getExpression(), is("<expression>group(group1)</expression>"));
+ } else if (swimlane.getName().equals("SL4")) {
+ assertThat(swimlane.getAssignment(), is(nullValue()));
+ assertThat(swimlane.getActorIdExpression(), is("bobthebuilder"));
+ } else if (swimlane.getName().equals("SL5")) {
+ assertThat(swimlane.getAssignment(), is(nullValue()));
+ assertThat(swimlane.getPooledActorsExpression(), is("hippies,hells angles"));
+ }
+ }
+ }
+
+ @Test
public void shouldHaveStartState() {
JPDL3StartStateMetadata jPDL3StartStateMetadata = metadata.getStartStateMetadata();
assertNotNull(jPDL3StartStateMetadata);
assertEquals("S0", jPDL3StartStateMetadata.getName());
- //Transitions
+ // Transitions
List<JPDL3TransitionMetadata> transitions = jPDL3StartStateMetadata.getTransitions();
for (JPDL3TransitionMetadata jPDL3TransitionMetadata : transitions) {
assertEquals("Tr01_S01", jPDL3TransitionMetadata.getName());
@@ -74,6 +103,46 @@
assertEquals("S1", jPDL3EndStateMetadata.getName());
}
+ @Test
+ public void shouldHaveATaskNode() {
+ List<JPDL3TaskNodeMetadata> taskNodes = metadata.getTaskNodes();
+ assertThat(taskNodes.size() > 0, is(true));
+ for (JPDL3TaskNodeMetadata jPDL3TaskNodeMetadata : taskNodes) {
+
+ // task node 1
+ if (jPDL3TaskNodeMetadata.getName().equals("Phase01")) {
+ List<JPDL3TaskMetadata> tasks = jPDL3TaskNodeMetadata.getTasks();
+ assertThat(tasks.size() == 2, is(true));
+ for (JPDL3TaskMetadata jPDL3TaskMetadata : tasks) {
+ if (jPDL3TaskMetadata.getName().equals("Task01_Phase01")) {
+ String swimlane = jPDL3TaskMetadata.getSwimlane();
+ assertThat(swimlane, is("SL1"));
+ }
+ }
+ }
+
+ List<JPDL3TransitionMetadata> transitions = jPDL3TaskNodeMetadata.getTransitions();
+
+ for (JPDL3TransitionMetadata transitionMetadata : transitions) {
+ String transitionName = transitionMetadata.getName();
+ if (transitionName.equals("Tr01_Phase01")) {
+ assertThat(transitionMetadata.getTo(), is("Phase02"));
+ } else if (transitionName.equals("Tr01_Phase02")) {
+ assertThat(transitionMetadata.getTo(), is("Phase03"));
+ } else if (transitionName.equals("Tr01_Phase03")) {
+ assertThat(transitionMetadata.getTo(), is("Phase04"));
+ } else if (transitionName.equals("Tr01_Phase04")) {
+ assertThat(transitionMetadata.getTo(), is("Phase05"));
+ } else if (transitionName.equals("Tr01_Phase05")) {
+ assertThat(transitionMetadata.getTo(), is("Phase06"));
+ } else if (transitionName.equals("Tr01_Phase06")) {
+ assertThat(transitionMetadata.getTo(), is("S1"));
+ }
+
+ }
+ }
+ }
+
public static JPDL3Metadata createJPDLMetadata() {
InputStream stream = ClassLoaderUtil.getStream("processdefinition.xml");
JPDL3Metadata metadata = JPDL3Metadata.instance(stream);
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/gpd.xml
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/gpd.xml (rev 0)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/gpd.xml 2008-11-22 19:45:51 UTC (rev 644)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<root-container name="Pd-Name" width="718" height="434">
+ <node name="S0" x="22" y="19" width="132" height="36">
+ <edge>
+ <label x="-27" y="-14"/>
+ </edge>
+ </node>
+ <node name="Phase01" x="298" y="12" width="132" height="36">
+ <edge>
+ <label x="-36" y="-14"/>
+ </edge>
+ </node>
+ <node name="Phase02" x="571" y="11" width="132" height="36">
+ <edge>
+ <label x="5" y="-10"/>
+ </edge>
+ </node>
+ <node name="Phase03" x="570" y="126" width="132" height="36">
+ <edge>
+ <label x="-30" y="-18"/>
+ </edge>
+ </node>
+ <node name="Phase04" x="299" y="122" width="132" height="36">
+ <edge>
+ <label x="-36" y="-18"/>
+ </edge>
+ </node>
+ <node name="Phase05" x="28" y="120" width="132" height="36">
+ <edge>
+ <label x="5" y="-10"/>
+ </edge>
+ </node>
+ <node name="S1" x="306" y="223" width="132" height="36"/>
+ <node name="Phase06" x="30" y="227" width="132" height="36">
+ <edge>
+ <label x="-55" y="-18"/>
+ </edge>
+ </node>
+</root-container>
Modified: trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processdefinition.xml
===================================================================
--- trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processdefinition.xml 2008-11-20 19:15:17 UTC (rev 643)
+++ trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processdefinition.xml 2008-11-22 19:45:51 UTC (rev 644)
@@ -1,43 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
-<process-definition xmlns="urn:jbpm.org:jpdl-3.2"
- name="Pd-Name">
-
- <!-- Process roles -->
- <swimlane name="SL1">
- <assignment class="com.sample.assigned.Task1Handler" />
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~
+ ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ ~ indicated by the @author tags or express copyright attribution
+ ~ statements applied by the authors. All third-party contributions are
+ ~ distributed under license by Red Hat Middleware LLC.
+ ~
+ ~ This copyrighted material is made available to anyone wishing to use, modify,
+ ~ copy, or redistribute it subject to the terms and conditions of the GNU
+ ~ Lesser General Public License, as published by the Free Software Foundation.
+ ~
+ ~ This program 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 distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+-->
+<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="Pd-Name">
+
+ <!-- roles in this process definition-->
+ <swimlane name="SL1">
+ <assignment class="com.sample.assigned.Task1Handler" config-type="constructor"/>
+ </swimlane>
+
+ <swimlane name="SL2">
+ <assignment class="com.sample.assigned.Task2Handler" />
+ </swimlane>
+
+ <swimlane name="SL3">
+ <assignment expression="group(group1)" />
+ </swimlane>
+
+ <swimlane name="SL4">
+ <assignment actor-id="bobthebuilder" />
+ </swimlane>
+
+ <swimlane name="SL5">
+ <assignment pooled-actors='hippies,hells angles' />
</swimlane>
- <swimlane name="SL2">
- <assignment class="com.sample.assigned.Task2Handler" />
- </swimlane>
- <start-state name="S0">
- <transition name="Tr01_S01" to="Phase01" />
- </start-state>
-
- <task-node name="Phase01">
- <task name="Task01_Phase01" swimlane="SL1"/>
- <transition name="Tr01_Phase01" to="Phase02" />
+ <!-- Start of the process -->
+ <start-state name="S0">
+ <transition to="Phase01" name="Tr01_S01"></transition>
+ </start-state>
+
+ <task-node name="Phase01">
+ <task name="Task01_Phase01" swimlane="SL1" />
+ <task name="Task01_Phase011" swimlane="SL1" />
+ <transition to="Phase02" name="Tr01_Phase01"></transition>
+ </task-node>
+
+ <task-node name="Phase02">
+ <task name="Task01_Phase02" swimlane="SL2" />
+ <transition to="Phase03" name="Tr01_Phase02"></transition>
+ </task-node>
+
+ <task-node name="Phase03">
+ <task name="Task_01_Phase03">
+ <assignment class="com.sample.assigned.Task3Handler" />
+ </task>
+ <transition to="Phase04" name="Tr01_Phase03"></transition>
+ </task-node>
+
+ <task-node name="Phase04">
+ <task name="Task_01_Phase04" swimlane="SL4"/>
+ <transition to="Phase05" name="Tr01_Phase04"></transition>
+ </task-node>
+
+ <task-node name="Phase05">
+ <task name="Task_01_Phase05" swimlane="SL5"/>
+ <transition name="Tr01_Phase05" to="Phase06" />
</task-node>
- <task-node name="Phase02">
- <task name="Task01_Phase02" swimlane="SL2"/>
- <transition name="Tr01_Phase02" to="Phase03" />
- </task-node>
-
- <task-node name="Phase03">
- <task name="Task_01_Phase03">
- <assignment class="com.sample.assigned.Task3Handler" />
- </task>
- <transition name="Tr01_Phase03" to="Phase04" />
- </task-node>
-
- <task-node name="Phase04">
- <task name="Task_01_Phase04">
- <assignment class="com.sample.assigned.Task4Hanlder" />
- </task>
- <transition name="Tr01_Phase04" to="S1" />
- </task-node>
-
- <end-state name="S1" />
-
+ <task-node name="Phase06">
+ <task name="Task_01_Phase06">
+ <assignment class="com.sample.assigned.Task6Hanlder" />
+ </task>
+ <transition name="Tr01_Phase06" to="S1" />
+ </task-node>
+
+ <!-- End node in the process definition -->
+ <end-state name="S1" />
+
</process-definition>
\ No newline at end of file
Added: trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processimage.jpg
===================================================================
(Binary files differ)
Property changes on: trunk/extensions/dna-sequencer-jbpm-jpdl/src/test/resources/processimage.jpg
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
17 years, 1 month
DNA SVN: r643 - in trunk: dna-graph/src/main/java/org/jboss/dna/graph/properties and 7 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-11-20 14:15:17 -0500 (Thu, 20 Nov 2008)
New Revision: 643
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Path.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepository.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepositorySource.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java
trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java
Log:
DNA-254 CreateNodeRequest does not allow passing in location of existing parent
I've changed the CreateNodeRequest to take the Location of a parent plus the Name of a child. All the connectors have been fixed, and all unit tests pass (and I've verified they're all still working correctly). Also, the implementation of the Graph has been changed to use the new form, but rather than change the interface (to allow passing in the Location of the parent), I've added new methods. This means that existing usages of the Graph API will not break and will work as before.
NOTE that this DOES change/correct the API, and that connectors built against the 0.3 release will no longer compile and will need to be changed.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -480,17 +480,30 @@
/**
* Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
+ * <p>
+ * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+ * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+ * parent or new node.
+ * </p>
*
* @param atPath the path to the node that is to be created.
* @return an object that may be used to start another request
*/
public Conjunction<Graph> create( String atPath ) {
- this.requestQueue.submit(new CreateNodeRequest(new Location(createPath(atPath))));
+ Path at = createPath(atPath);
+ Path parent = at.getParent();
+ Name child = at.getLastSegment().getName();
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0));
return nextGraph;
}
/**
* Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
+ * <p>
+ * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+ * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+ * parent or new node.
+ * </p>
*
* @param atPath the path to the node that is to be created.
* @param properties the properties for the new node
@@ -498,23 +511,39 @@
*/
public Conjunction<Graph> create( String atPath,
Property... properties ) {
- this.requestQueue.submit(new CreateNodeRequest(new Location(createPath(atPath)), properties));
+ Path at = createPath(atPath);
+ Path parent = at.getParent();
+ Name child = at.getLastSegment().getName();
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0, properties));
return nextGraph;
}
/**
* Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
+ * <p>
+ * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+ * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+ * parent or new node.
+ * </p>
*
* @param at the path to the node that is to be created.
* @return an object that may be used to start another request
*/
public Conjunction<Graph> create( Path at ) {
- this.requestQueue.submit(new CreateNodeRequest(new Location(at)));
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name child = at.getLastSegment().getName();
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0));
return nextGraph;
}
/**
* Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
+ * <p>
+ * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+ * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+ * parent or new node.
+ * </p>
*
* @param at the path to the node that is to be created.
* @param properties the properties for the new node
@@ -522,12 +551,20 @@
*/
public Conjunction<Graph> create( Path at,
Property... properties ) {
- this.requestQueue.submit(new CreateNodeRequest(new Location(at), properties));
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name child = at.getLastSegment().getName();
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0, properties));
return nextGraph;
}
/**
* Begin the request to create a node located at the supplied path. This request is submitted to the repository immediately.
+ * <p>
+ * If you have the {@link Location} of the parent (for the new node) from a previous request, it is better and more efficient
+ * to use {@link #createUnder(Location)}. However, this method work just as well if all you have is the {@link Path} to the
+ * parent or new node.
+ * </p>
*
* @param at the path to the node that is to be created.
* @param properties the properties for the new node
@@ -535,11 +572,73 @@
*/
public Conjunction<Graph> create( Path at,
Iterable<Property> properties ) {
- this.requestQueue.submit(new CreateNodeRequest(new Location(at), properties));
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name child = at.getLastSegment().getName();
+ this.requestQueue.submit(new CreateNodeRequest(new Location(parent), child, 0, properties));
return nextGraph;
}
/**
+ * Begin the request to create a node under the existing parent node at the supplied location. Use this method if you are
+ * creating a node when you have the {@link Location} of a parent from a previous request.
+ * <p>
+ * Like all other methods on the {@link Graph}, the copy request will be performed immediately when the <code>node(...)</code>
+ * method is called on the returned object
+ * </p>
+ *
+ * @param parent the location of the parent
+ * @return the object used to start creating a node
+ */
+ public CreateNode<Conjunction<Graph>> createUnder( final Location parent ) {
+ final NameFactory nameFactory = getContext().getValueFactories().getNameFactory();
+ CheckArg.isNotNull(parent, "parent");
+ return new CreateNode<Conjunction<Graph>>() {
+ public Conjunction<Graph> node( String name,
+ Property... properties ) {
+ return node(name, -1, properties);
+ }
+
+ public Conjunction<Graph> node( String name,
+ Iterator<Property> properties ) {
+ return node(name, -1, properties);
+ }
+
+ public Conjunction<Graph> node( String name,
+ Iterable<Property> properties ) {
+ return node(name, -1, properties);
+ }
+
+ @SuppressWarnings( "synthetic-access" )
+ public Conjunction<Graph> node( String name,
+ int desiredIndexInParent,
+ Property... properties ) {
+ Name child = nameFactory.create(name);
+ requestQueue.submit(new CreateNodeRequest(parent, child, desiredIndexInParent, properties));
+ return nextGraph;
+ }
+
+ @SuppressWarnings( "synthetic-access" )
+ public Conjunction<Graph> node( String name,
+ int desiredIndexInParent,
+ Iterator<Property> properties ) {
+ Name child = nameFactory.create(name);
+ requestQueue.submit(new CreateNodeRequest(parent, child, desiredIndexInParent, properties));
+ return nextGraph;
+ }
+
+ @SuppressWarnings( "synthetic-access" )
+ public Conjunction<Graph> node( String name,
+ int desiredIndexInParent,
+ Iterable<Property> properties ) {
+ Name child = nameFactory.create(name);
+ requestQueue.submit(new CreateNodeRequest(parent, child, desiredIndexInParent, properties));
+ return nextGraph;
+ }
+ };
+ }
+
+ /**
* Set the properties on a node.
*
* @param properties the properties to set
@@ -1515,7 +1614,10 @@
*/
public Create<BatchConjunction> create( String atPath ) {
assertNotExecuted();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(createPath(atPath)));
+ Path at = createPath(atPath);
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0);
}
/**
@@ -1533,7 +1635,10 @@
public Create<BatchConjunction> create( String atPath,
Property property ) {
assertNotExecuted();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(createPath(atPath))).with(property);
+ Path at = createPath(atPath);
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(property);
}
/**
@@ -1553,8 +1658,11 @@
Property firstProperty,
Property... additionalProperties ) {
assertNotExecuted();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(createPath(atPath))).with(firstProperty,
- additionalProperties);
+ Path at = createPath(atPath);
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(firstProperty,
+ additionalProperties);
}
/**
@@ -1570,7 +1678,10 @@
*/
public Create<BatchConjunction> create( Path at ) {
assertNotExecuted();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(at));
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0);
}
/**
@@ -1588,8 +1699,11 @@
public Create<BatchConjunction> create( Path at,
Iterable<Property> properties ) {
assertNotExecuted();
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
CreateAction<BatchConjunction> action = new CreateAction<BatchConjunction>(nextRequests, requestQueue,
- new Location(at));
+ new Location(parent), name, 0);
for (Property property : properties) {
action.and(property);
}
@@ -1611,7 +1725,10 @@
public Create<BatchConjunction> create( Path at,
Property property ) {
assertNotExecuted();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(at)).with(property);
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(property);
}
/**
@@ -1631,11 +1748,26 @@
Property firstProperty,
Property... additionalProperties ) {
assertNotExecuted();
- return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(at)).with(firstProperty,
- additionalProperties);
+ CheckArg.isNotNull(at, "at");
+ Path parent = at.getParent();
+ Name name = at.getLastSegment().getName();
+ return new CreateAction<BatchConjunction>(nextRequests, requestQueue, new Location(parent), name, 0).with(firstProperty,
+ additionalProperties);
}
/**
+ * Begin the request to create a node under the existing parent node at the supplied location. This request is submitted
+ * to the repository after the returned components are completed.
+ *
+ * @param parent the location of the parent
+ * @return the object used to start creating a node
+ */
+ public CreateNodeNamed<BatchConjunction> createUnder( Location parent ) {
+ CheckArg.isNotNull(parent, "parent");
+ return new CreateNodeNamedAction<BatchConjunction>(nextRequests, requestQueue, parent);
+ }
+
+ /**
* Set the properties on a node.
*
* @param properties the properties to set
@@ -2482,6 +2614,106 @@
}
/**
+ * A component that defines a node that is to be created.
+ *
+ * @param <Next> The interface that is to be returned to complete the create request
+ * @author Randall Hauch
+ */
+ public interface CreateNode<Next> {
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param properties the properties for the new node
+ * @return the next component for making additional requests.
+ */
+ Next node( String nodeName,
+ Property... properties );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param properties the properties for the new node
+ * @return the next component for making additional requests.
+ */
+ Next node( String nodeName,
+ Iterator<Property> properties );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param properties the properties for the new node
+ * @return the next component for making additional requests.
+ */
+ Next node( String nodeName,
+ Iterable<Property> properties );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param sameNameSiblingIndex the desired same-name-sibling index
+ * @param properties the properties for the new node
+ * @return the next component for making additional requests.
+ */
+ Next node( String nodeName,
+ int sameNameSiblingIndex,
+ Property... properties );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param sameNameSiblingIndex the desired same-name-sibling index
+ * @param properties the properties for the new node
+ * @return the next component for making additional requests.
+ */
+ Next node( String nodeName,
+ int sameNameSiblingIndex,
+ Iterator<Property> properties );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param sameNameSiblingIndex the desired same-name-sibling index
+ * @param properties the properties for the new node
+ * @return the next component for making additional requests.
+ */
+ Next node( String nodeName,
+ int sameNameSiblingIndex,
+ Iterable<Property> properties );
+ }
+
+ /**
+ * A component that defines a node that is to be created.
+ *
+ * @param <Next> The interface that is to be returned to complete the create request
+ * @author Randall Hauch
+ */
+ public interface CreateNodeNamed<Next> {
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @return the interface used to complete the request
+ */
+ CreateAction<Next> nodeNamed( String nodeName );
+
+ /**
+ * Specify the name of the node that is to be created.
+ *
+ * @param nodeName the name of the new node
+ * @param sameNameSiblingIndex the desired same-name-sibling index
+ * @return the interface used to complete the request
+ */
+ CreateAction<Next> nodeNamed( String nodeName,
+ int sameNameSiblingIndex );
+ }
+
+ /**
* A component that defines the location into which a node should be copied or moved.
*
* @param <Next> The interface that is to be returned when this request is completed
@@ -3114,6 +3346,10 @@
return this.queue;
}
+ /*package*/T afterConjunction() {
+ return this.afterConjunction;
+ }
+
public T and() {
return this.afterConjunction;
}
@@ -3319,14 +3555,20 @@
@NotThreadSafe
static class CreateAction<T> extends AbstractAction<T> implements Create<T> {
- private final Location at;
+ private final Location parent;
+ private final Name childName;
+ private final int desiredIndex;
private final List<Property> properties = new LinkedList<Property>();
/*package*/CreateAction( T afterConjunction,
RequestQueue queue,
- Location at ) {
+ Location parent,
+ Name childName,
+ int desiredIndex ) {
super(afterConjunction, queue);
- this.at = at;
+ this.parent = parent;
+ this.childName = childName;
+ this.desiredIndex = desiredIndex;
}
public Create<T> and( UUID uuid ) {
@@ -3391,7 +3633,7 @@
@Override
public T and() {
- this.queue().submit(new CreateNodeRequest(this.at, this.properties));
+ this.queue().submit(new CreateNodeRequest(parent, childName, desiredIndex, this.properties));
return super.and();
}
@@ -3401,4 +3643,31 @@
}
}
+ @NotThreadSafe
+ static class CreateNodeNamedAction<T> extends AbstractAction<T> implements CreateNodeNamed<T> {
+ private final Location parent;
+
+ /*package*/CreateNodeNamedAction( T afterConjunction,
+ RequestQueue queue,
+ Location parent ) {
+ super(afterConjunction, queue);
+ this.parent = parent;
+ }
+
+ public CreateAction<T> nodeNamed( String name ) {
+ ExecutionContext context = queue().getGraph().getContext();
+ NameFactory factory = context.getValueFactories().getNameFactory();
+ Name nameObj = factory.create(name);
+ return new CreateAction<T>(afterConjunction(), queue(), parent, nameObj, 0);
+ }
+
+ public CreateAction<T> nodeNamed( String name,
+ int desiredIndex ) {
+ ExecutionContext context = queue().getGraph().getContext();
+ NameFactory factory = context.getValueFactories().getNameFactory();
+ Name nameObj = factory.create(name);
+ if (desiredIndex < 0) desiredIndex = 0;
+ return new CreateAction<T>(afterConjunction(), queue(), parent, nameObj, desiredIndex);
+ }
+ }
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Path.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Path.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/Path.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -157,7 +157,7 @@
public Name getName();
/**
- * Get the index for this segment, which will be {@link Path#NO_INDEX 0} if this segment has no specific index.
+ * Get the index for this segment, which will be {@link Path#NO_INDEX -1} if this segment has no specific index.
*
* @return the index
*/
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -7,7 +7,7 @@
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
+ * the License, or (under your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -31,10 +31,11 @@
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.properties.Name;
import org.jboss.dna.graph.properties.Property;
/**
- * Instruction to create the node at the specified location. This command will create the node and set the initial properties.
+ * Instruction to create the node under the specified location. This command will create the node and set the initial properties.
*
* @author Randall Hauch
*/
@@ -44,107 +45,132 @@
public static final NodeConflictBehavior DEFAULT_CONFLICT_BEHAVIOR = NodeConflictBehavior.APPEND;
- private final Location at;
+ private final Location under;
+ private final Name childName;
+ private final Integer desiredIndex;
private final List<Property> properties;
private final NodeConflictBehavior conflictBehavior;
private Location actualLocation;
/**
- * Create a request to create a node with the given properties at the supplied location.
+ * Create a request to create a node with the given properties under the supplied location.
*
- * @param at the location of the node to be read
- * @param properties the properties of the new node, which should not include the location's
- * {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null or has no {@link Location#getPath() path}
+ * @param parentLocation the location of the existing parent node, under which the new child should be created
+ * @param childName the name of the new child to create under the existing parent
+ * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
+ * is to be appended under the end of the existing children
+ * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+ * properties} for the new node
+ * @throws IllegalArgumentException if the location or the child name is null
*/
- public CreateNodeRequest( Location at,
+ public CreateNodeRequest( Location parentLocation,
+ Name childName,
+ int desiredIndexInParent,
Property... properties ) {
- this(at, DEFAULT_CONFLICT_BEHAVIOR, properties);
+ this(parentLocation, childName, desiredIndexInParent, DEFAULT_CONFLICT_BEHAVIOR, properties);
}
/**
- * Create a request to create a node with the given properties at the supplied location.
+ * Create a request to create a node with the given properties under the supplied location.
*
- * @param at the location of the node to be read
- * @param properties the properties of the new node, which should not include the location's
- * {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null or has no {@link Location#getPath() path}
+ * @param parentLocation the location of the existing parent node, under which the new child should be created
+ * @param childName the name of the new child to create under the existing parent
+ * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
+ * is to be appended under the end of the existing children
+ * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+ * properties} for the new node
+ * @throws IllegalArgumentException if the location or the child name is null
*/
- public CreateNodeRequest( Location at,
+ public CreateNodeRequest( Location parentLocation,
+ Name childName,
+ int desiredIndexInParent,
Iterable<Property> properties ) {
- this(at, DEFAULT_CONFLICT_BEHAVIOR, properties);
+ this(parentLocation, childName, desiredIndexInParent, DEFAULT_CONFLICT_BEHAVIOR, properties);
}
/**
- * Create a request to create a node with the given properties at the supplied location.
+ * Create a request to create a node with the given properties under the supplied location.
*
- * @param at the location of the node to be read
- * @param properties the properties of the new node, which should not include the location's
- * {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null or has no {@link Location#getPath() path}
+ * @param parentLocation the location of the existing parent node, under which the new child should be created
+ * @param childName the name of the new child to create under the existing parent
+ * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
+ * is to be appended under the end of the existing children
+ * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+ * properties} for the new node
+ * @throws IllegalArgumentException if the location or the child name is null
*/
- public CreateNodeRequest( Location at,
+ public CreateNodeRequest( Location parentLocation,
+ Name childName,
+ int desiredIndexInParent,
Iterator<Property> properties ) {
- this(at, DEFAULT_CONFLICT_BEHAVIOR, properties);
+ this(parentLocation, childName, desiredIndexInParent, DEFAULT_CONFLICT_BEHAVIOR, properties);
}
/**
- * Create a request to create a node with the given properties at the supplied location.
+ * Create a request to create a node with the given properties under the supplied location.
*
- * @param at the location of the node to be read
- * @param properties the properties of the new node, which should not include the location's
- * {@link Location#getIdProperties() identification properties}
- * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+ * @param parentLocation the location of the existing parent node, under which the new child should be created
+ * @param childName the name of the new child to create under the existing parent
+ * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
+ * is to be appended under the end of the existing children
+ * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+ * properties} for the new node
+ * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
* location
- * @throws IllegalArgumentException if the location or the conflict behavior is null, or if the location does not have a
- * {@link Location#getPath() path}
+ * @throws IllegalArgumentException if the location, the child name, or the conflict behavior is null
*/
- public CreateNodeRequest( Location at,
+ public CreateNodeRequest( Location parentLocation,
+ Name childName,
+ int desiredIndexInParent,
NodeConflictBehavior conflictBehavior,
Property... properties ) {
- CheckArg.isNotNull(at, "at");
+ CheckArg.isNotNull(parentLocation, "parentLocation");
CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
- CheckArg.isNotNull(at.getPath(), "at.getPath()");
- this.at = at;
+ CheckArg.isNotNull(childName, "childName");
+ this.under = parentLocation;
+ this.childName = childName;
+ this.desiredIndex = desiredIndexInParent < 1 ? null : new Integer(desiredIndexInParent);
this.conflictBehavior = conflictBehavior;
- int number = properties.length + (at.hasIdProperties() ? at.getIdProperties().size() : 0);
+ int number = properties.length + (under.hasIdProperties() ? under.getIdProperties().size() : 0);
List<Property> props = new ArrayList<Property>(number);
for (Property property : properties) {
if (property != null) props.add(property);
}
- // Add in the location properties ...
- if (at.hasIdProperties()) {
- for (Property property : at.getIdProperties()) {
- if (property != null) props.add(property);
- }
- }
this.properties = Collections.unmodifiableList(props);
}
/**
- * Create a request to create a node with the given properties at the supplied location.
+ * Create a request to create a node with the given properties under the supplied location.
*
- * @param at the location of the node to be read
- * @param properties the properties of the new node, which should not include the location's
- * {@link Location#getIdProperties() identification properties}
- * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+ * @param parentLocation the location of the existing parent node, under which the new child should be created
+ * @param childName the name of the new child to create under the existing parent
+ * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
+ * is to be appended under the end of the existing children
+ * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+ * properties} for the new node
+ * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
* location
- * @throws IllegalArgumentException if the location or the conflict behavior is null
+ * @throws IllegalArgumentException if the location, the child name, or the conflict behavior is null
*/
- public CreateNodeRequest( Location at,
+ public CreateNodeRequest( Location parentLocation,
+ Name childName,
+ int desiredIndexInParent,
NodeConflictBehavior conflictBehavior,
Iterable<Property> properties ) {
- CheckArg.isNotNull(at, "at");
+ CheckArg.isNotNull(parentLocation, "parentLocation");
CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
- this.at = at;
+ CheckArg.isNotNull(childName, "childName");
+ this.under = parentLocation;
+ this.childName = childName;
+ this.desiredIndex = desiredIndexInParent < 1 ? null : new Integer(desiredIndexInParent);
this.conflictBehavior = conflictBehavior;
List<Property> props = new LinkedList<Property>();
for (Property property : properties) {
if (property != null) props.add(property);
}
// Add in the location properties ...
- if (at.hasIdProperties()) {
- for (Property property : at.getIdProperties()) {
+ if (under.hasIdProperties()) {
+ for (Property property : under.getIdProperties()) {
if (property != null) props.add(property);
}
}
@@ -152,21 +178,29 @@
}
/**
- * Create a request to create a node with the given properties at the supplied location.
+ * Create a request to create a node with the given properties under the supplied location.
*
- * @param at the location of the node to be read
- * @param properties the properties of the new node, which should not include the location's
- * {@link Location#getIdProperties() identification properties}
- * @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
+ * @param parentLocation the location of the existing parent node, under which the new child should be created
+ * @param childName the name of the new child to create under the existing parent
+ * @param desiredIndexInParent the desired index in the parent under which the new child should be created, or 0 if the child
+ * is to be appended under the end of the existing children
+ * @param properties the properties of the new node, which should include any {@link Location#getIdProperties() identification
+ * properties} for the new node
+ * @param conflictBehavior the expected behavior if an equivalently-named child already exists under the <code>into</code>
* location
- * @throws IllegalArgumentException if the location or the conflict behavior is null
+ * @throws IllegalArgumentException if the location, the child name, or the conflict behavior is null
*/
- public CreateNodeRequest( Location at,
+ public CreateNodeRequest( Location parentLocation,
+ Name childName,
+ int desiredIndexInParent,
NodeConflictBehavior conflictBehavior,
Iterator<Property> properties ) {
- CheckArg.isNotNull(at, "at");
+ CheckArg.isNotNull(parentLocation, "parentLocation");
CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
- this.at = at;
+ CheckArg.isNotNull(childName, "childName");
+ this.under = parentLocation;
+ this.childName = childName;
+ this.desiredIndex = desiredIndexInParent < 1 ? null : new Integer(desiredIndexInParent);
this.conflictBehavior = conflictBehavior;
List<Property> props = new LinkedList<Property>();
while (properties.hasNext()) {
@@ -174,8 +208,8 @@
if (property != null) props.add(property);
}
// Add in the location properties ...
- if (at.hasIdProperties()) {
- for (Property property : at.getIdProperties()) {
+ if (under.hasIdProperties()) {
+ for (Property property : under.getIdProperties()) {
if (property != null) props.add(property);
}
}
@@ -183,15 +217,34 @@
}
/**
- * Get the location defining the node that is to be created.
+ * Get the location defining the parent of the new node that is to be created.
*
- * @return the location of the node; never null
+ * @return the location of the parent node; never null
*/
- public Location at() {
- return at;
+ public Location under() {
+ return under;
}
/**
+ * Get the name for the new child.
+ *
+ * @return the child's name; never null
+ */
+ public Name named() {
+ return childName;
+ }
+
+ /**
+ * Get the desired index (always positive) in the parent for the new child, or null if the new child should be appended under
+ * the end of the existing children.
+ *
+ * @return the desired index, or null if there is no desired index
+ */
+ public Integer desiredIndex() {
+ return desiredIndex;
+ }
+
+ /**
* {@inheritDoc}
*
* @see java.lang.Iterable#iterator()
@@ -201,7 +254,7 @@
}
/**
- * Get the properties for the node. If the node's {@link #at() location} has identification properties, the resulting
+ * Get the properties for the node. If the node's {@link #under() location} has identification properties, the resulting
* properties will include the {@link Location#getIdProperties() identification properties}.
*
* @return the collection of properties; never null
@@ -211,7 +264,8 @@
}
/**
- * Get the expected behavior when copying the branch and the {@link #at() destination} already has a node with the same name.
+ * Get the expected behavior when copying the branch and the {@link #under() destination} already has a node with the same
+ * name.
*
* @return the behavior specification
*/
@@ -233,18 +287,23 @@
* Sets the actual and complete location of the node being created. This method must be called when processing the request,
* and the actual location must have a {@link Location#getPath() path}.
*
- * @param actual the actual location of the node being created, or null if the {@link #at() current location} should be used
+ * @param actual the actual location of the node being created, or null if the {@link #under() current location} should be
+ * used
* @throws IllegalArgumentException if the actual location does not represent the {@link Location#isSame(Location) same
- * location} as the {@link #at() current location}, or if the actual location does not have a path.
+ * location} as the {@link #under() current location}, or if the actual location does not have a path.
*/
public void setActualLocationOfNode( Location actual ) {
- if (!at.isSame(actual, false)) { // not same if actual is null
- throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, at));
+ CheckArg.isNotNull(actual, "actual");
+ if (!under.isSame(actual, false)) { // not same if actual is null
}
assert actual != null;
if (!actual.hasPath()) {
throw new IllegalArgumentException(GraphI18n.actualLocationMustHavePath.text(actual));
}
+ assert actual.hasPath();
+ if (under.hasPath() && !under.getPath().equals(actual.getPath().getParent())) {
+ throw new IllegalArgumentException(GraphI18n.actualLocationIsNotSameAsInputLocation.text(actual, under));
+ }
this.actualLocation = actual;
}
@@ -266,7 +325,7 @@
public boolean equals( Object obj ) {
if (this.getClass().isInstance(obj)) {
CreateNodeRequest that = (CreateNodeRequest)obj;
- if (!this.at().equals(that.at())) return false;
+ if (!this.under().equals(that.under())) return false;
if (!this.conflictBehavior().equals(that.conflictBehavior())) return false;
if (!this.properties().equals(that.properties())) return false;
return true;
@@ -281,7 +340,7 @@
*/
@Override
public String toString() {
- return "create node at " + at() + " with properties " + properties();
+ return "create node \"" + childName + "\" under " + under() + " with properties " + properties();
}
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -114,7 +114,9 @@
assertThat(nextCommand, is(instanceOf(CreateNodeRequest.class)));
CreateNodeRequest createNode = (CreateNodeRequest)nextCommand;
Path expectedPath = context.getValueFactories().getPathFactory().create(path);
- assertThat(createNode.at().getPath(), is(expectedPath));
+ Path parentPath = createNode.under().getPath();
+ assertThat(parentPath, is(expectedPath.getParent()));
+ assertThat(createNode.named(), is(expectedPath.getLastSegment().getName()));
Map<Name, Property> propertiesByName = new HashMap<Name, Property>();
for (Property prop : createNode.properties()) {
propertiesByName.put(prop.getName(), prop);
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphTest.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -160,9 +160,12 @@
assertThat(executedRequests.poll(), is((Request)new DeleteBranchRequest(at)));
}
- protected void assertNextRequestIsCreate( Location at,
+ protected void assertNextRequestIsCreate( Location parent,
+ String child,
+ int desiredIndex,
Property... properties ) {
- assertThat(executedRequests.poll(), is((Request)new CreateNodeRequest(at, properties)));
+ Name name = context.getValueFactories().getNameFactory().create(child);
+ assertThat(executedRequests.poll(), is((Request)new CreateNodeRequest(parent, name, desiredIndex, properties)));
}
protected void assertNextRequestReadProperties( Location at,
@@ -307,32 +310,32 @@
public void shouldCreateNode() {
graph.create(validPath);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath));
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0);
assertNoMoreRequests();
graph.create(validPath, validIdProperty1);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath), validIdProperty1);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1);
assertNoMoreRequests();
graph.create(validPath, validIdProperty1, validIdProperty2);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath), validIdProperty1, validIdProperty2);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1, validIdProperty2);
assertNoMoreRequests();
graph.create(validPathString);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath));
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0);
assertNoMoreRequests();
graph.create(validPathString, validIdProperty1);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath), validIdProperty1);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1);
assertNoMoreRequests();
graph.create(validPathString, validIdProperty1, validIdProperty2);
assertThat(numberOfExecutions, is(1));
- assertNextRequestIsCreate(new Location(validPath), validIdProperty1, validIdProperty2);
+ assertNextRequestIsCreate(new Location(validPath.getParent()), "c", 0, validIdProperty1, validIdProperty2);
assertNoMoreRequests();
}
@@ -775,8 +778,11 @@
@Override
public void process( CreateNodeRequest request ) {
- // Just update the actual location
- request.setActualLocationOfNode(actualLocationOf(request.at()));
+ // Just update the actual location ...
+ Location parent = actualLocationOf(request.under()); // just make sure it has a path ...
+ Name name = request.named();
+ Path childPath = context.getValueFactories().getPathFactory().create(parent.getPath(), name);
+ request.setActualLocationOfNode(new Location(childPath));
}
@Override
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepository.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepository.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepository.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -209,6 +209,18 @@
return this;
}
+ public List<Path> getChildren( ExecutionContext context,
+ Path path ) {
+ List<Path> children = new LinkedList<Path>();
+ for (Path nodePath : data.keySet()) {
+ if (nodePath.isRoot()) continue;
+ if (nodePath.getParent().equals(path)) {
+ children.add(nodePath);
+ }
+ }
+ return children;
+ }
+
/**
* @param data new new map of property data
*/
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepositorySource.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepositorySource.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/SimpleRepositorySource.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -290,20 +290,54 @@
@Override
public void process( CreateNodeRequest request ) {
- Path targetPath = request.at().getPath();
+ Path parentPath = request.under().getPath();
ExecutionContext context = getExecutionContext();
- repository.create(context, targetPath.getString(context.getNamespaceRegistry()));
- Map<Name, Property> properties = repository.getData().get(targetPath);
+ Path targetPath = context.getValueFactories().getPathFactory().create(parentPath, request.named());
+ final Name childName = request.named();
+ Map<Name, Property> properties = null;
+ // Create the path for the new node, but we need to look for existing SNS ...
+ switch (request.conflictBehavior()) {
+ case DO_NOT_REPLACE:
+ case APPEND:
+ // Need to look for existing SNS ...
+ int lastSns = 0;
+ for (Path child : repository.getChildren(context, parentPath)) {
+ Path.Segment segment = child.getLastSegment();
+ if (segment.getName().equals(childName)) {
+ if (segment.hasIndex()) lastSns = segment.getIndex();
+ else ++lastSns;
+ }
+ targetPath = context.getValueFactories().getPathFactory().create(parentPath,
+ request.named(),
+ ++lastSns);
+ }
+ repository.create(context, targetPath.getString(context.getNamespaceRegistry()));
+ break;
+ case REPLACE:
+ // Remove any existing node with the desired target path ...
+ repository.delete(context, targetPath.getString(context.getNamespaceRegistry()));
+ repository.create(context, targetPath.getString(context.getNamespaceRegistry()));
+ break;
+ case UPDATE:
+ // Get the existing properties (if there are any) and merge new properties,
+ // using the desired target path ...
+ properties = repository.getData().get(targetPath);
+ if (properties == null) {
+ repository.create(context, targetPath.getString(context.getNamespaceRegistry()));
+ }
+ break;
+ }
+ if (properties == null) properties = repository.getData().get(targetPath);
assert properties != null;
// Set the UUID if the request has one ...
- Property uuidProperty = request.at().getIdProperty(DnaLexicon.UUID);
+ Property uuidProperty = request.under().getIdProperty(DnaLexicon.UUID);
if (uuidProperty != null) {
properties.put(uuidProperty.getName(), uuidProperty);
- request.setActualLocationOfNode(request.at());
} else {
uuidProperty = properties.get(DnaLexicon.UUID);
- request.setActualLocationOfNode(request.at().with(uuidProperty));
}
+ Location actual = new Location(targetPath, uuidProperty);
+ request.setActualLocationOfNode(actual);
for (Property property : request.properties()) {
if (property != null) properties.put(property.getName(), property);
}
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlHandlerTest.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -399,7 +399,9 @@
}
// Now get the next request and compare the expected and actual ...
CreateNodeRequest request = requests.remove();
- assertThat(request.at().getPath(), is(expectedPath));
+ Path parentPath = request.under().getPath();
+ assertThat(parentPath, is(expectedPath.getParent()));
+ assertThat(request.named(), is(expectedPath.getLastSegment().getName()));
for (Property actual : request.properties()) {
Property expected = expectedProperties.remove(actual.getName());
assertThat("unexpected property: " + actual, expected, is(notNullValue()));
@@ -434,18 +436,23 @@
public void create( Path path,
List<Property> properties ) {
- requests.add(new CreateNodeRequest(new Location(path), properties));
+ assert path != null;
+ Path parent = path.getParent();
+ Name child = path.getLastSegment().getName();
+ requests.add(new CreateNodeRequest(new Location(parent), child, 0, properties));
}
public void create( final Path path,
final Property firstProperty,
final Property... additionalProperties ) {
- Location location = new Location(path);
+ Path parent = path.getParent();
+ Name child = path.getLastSegment().getName();
+ Location location = new Location(parent);
if (firstProperty == null) {
- requests.add(new CreateNodeRequest(location));
+ requests.add(new CreateNodeRequest(location, child, 0));
} else {
if (additionalProperties == null || additionalProperties.length == 0) {
- requests.add(new CreateNodeRequest(location, firstProperty));
+ requests.add(new CreateNodeRequest(location, child, 0, firstProperty));
} else {
Iterator<Property> iter = new Iterator<Property>() {
private int index = -1;
@@ -466,7 +473,7 @@
throw new UnsupportedOperationException();
}
};
- requests.add(new CreateNodeRequest(location, iter));
+ requests.add(new CreateNodeRequest(location, child, 0, iter));
}
}
}
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -47,12 +47,14 @@
import org.jboss.dna.graph.DnaLexicon;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.NodeConflictBehavior;
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connectors.RepositoryConnection;
import org.jboss.dna.graph.connectors.RepositoryConnectionFactory;
import org.jboss.dna.graph.connectors.RepositorySource;
import org.jboss.dna.graph.connectors.RepositorySourceException;
import org.jboss.dna.graph.properties.DateTime;
+import org.jboss.dna.graph.properties.Name;
import org.jboss.dna.graph.properties.Path;
import org.jboss.dna.graph.properties.PathFactory;
import org.jboss.dna.graph.properties.PathNotFoundException;
@@ -640,12 +642,23 @@
protected void updateCache( FederatedNode mergedNode ) throws RepositorySourceException {
final ExecutionContext context = getExecutionContext();
final RepositoryConnection cacheConnection = getConnectionToCache();
- final Location path = mergedNode.at();
+ final Location location = mergedNode.at();
+ final Path path = location.getPath();
+ assert path != null;
+ List<Request> requests = new ArrayList<Request>();
+ Name childName = null;
+ if (!path.isRoot()) {
+ // This is not the root node, so we need to create the node ...
+ final Location parentLocation = new Location(path.getParent());
+ childName = path.getLastSegment().getName();
+ requests.add(new CreateNodeRequest(parentLocation, childName, 0, NodeConflictBehavior.REPLACE,
+ mergedNode.getProperties()));
+ }
- List<Request> requests = new ArrayList<Request>();
- requests.add(new CreateNodeRequest(path, mergedNode.getProperties()));
+ // Now create all of the children that this federated node knows of ...
for (Location child : mergedNode.getChildren()) {
- requests.add(new CreateNodeRequest(child));
+ childName = child.getPath().getLastSegment().getName();
+ requests.add(new CreateNodeRequest(location, childName, 0, NodeConflictBehavior.APPEND));
}
cacheConnection.execute(context, CompositeRequest.with(requests));
}
Modified: trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java
===================================================================
--- trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/SingleProjectionCommandExecutor.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -32,6 +32,7 @@
import org.jboss.dna.graph.connectors.RepositorySource;
import org.jboss.dna.graph.connectors.RepositorySourceException;
import org.jboss.dna.graph.properties.DateTime;
+import org.jboss.dna.graph.properties.Name;
import org.jboss.dna.graph.properties.Path;
import org.jboss.dna.graph.properties.PathFactory;
import org.jboss.dna.graph.properties.PathNotFoundException;
@@ -186,11 +187,14 @@
*/
@Override
public void process( CreateNodeRequest request ) {
- Location locationInSource = projectIntoSource(request.at());
- CreateNodeRequest projected = new CreateNodeRequest(locationInSource, request.properties());
+ Location locationInSource = projectIntoSource(request.under());
+ Name child = request.named();
+ Integer desiredIndex = request.desiredIndex();
+ int index = desiredIndex != null ? desiredIndex.intValue() : 0;
+ CreateNodeRequest projected = new CreateNodeRequest(locationInSource, child, index, request.properties());
getConnection().execute(this.getExecutionContext(), projected);
if (projected.hasError()) {
- projectError(projected, request.at(), request);
+ projectError(projected, request.under(), request);
return;
}
}
Modified: trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/extensions/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -382,30 +382,26 @@
@Override
public void process( CreateNodeRequest request ) {
- Path path = request.at().getPath();
- CheckArg.isNotNull(path, "request.at().getPath()");
+ Path parent = request.under().getPath();
+ CheckArg.isNotNull(parent, "request.under().getPath()");
Node node = null;
- if (!path.isRoot()) {
- Path parent = path.getParent();
- // Look up the parent node, which must exist ...
- Node parentNode = getNode(parent);
- if (parentNode == null) {
- Path lowestExisting = getLowestExistingPath(parent);
- throw new PathNotFoundException(request.at(), lowestExisting,
- InMemoryConnectorI18n.nodeDoesNotExist.text(parent));
+ // Look up the parent node, which must exist ...
+ Node parentNode = getNode(parent);
+ if (parentNode == null) {
+ Path lowestExisting = getLowestExistingPath(parent);
+ throw new PathNotFoundException(request.under(), lowestExisting,
+ InMemoryConnectorI18n.nodeDoesNotExist.text(parent));
+ }
+ UUID uuid = null;
+ for (Property property : request.properties()) {
+ if (property.getName().equals(DnaLexicon.UUID)) {
+ uuid = getExecutionContext().getValueFactories().getUuidFactory().create(property.getValues().next());
+ break;
}
- UUID uuid = null;
- for (Property property : request.properties()) {
- if (property.getName().equals(DnaLexicon.UUID)) {
- uuid = getExecutionContext().getValueFactories().getUuidFactory().create(property.getValues().next());
- break;
- }
- }
- node = createNode(getExecutionContext(), parentNode, path.getLastSegment().getName(), uuid);
- path = getExecutionContext().getValueFactories().getPathFactory().create(parent, node.getName());
- } else {
- node = getRoot();
}
+ node = createNode(getExecutionContext(), parentNode, request.named(), uuid);
+ assert node != null;
+ Path path = getExecutionContext().getValueFactories().getPathFactory().create(parent, node.getName());
// Now add the properties to the supplied node ...
for (Property property : request.properties()) {
Name propName = property.getName();
@@ -417,7 +413,6 @@
node.getProperties().put(propName, property);
}
}
- assert node != null;
request.setActualLocationOfNode(new Location(path, node.getUuid()));
}
Modified: trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java
===================================================================
--- trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java 2008-11-19 20:26:40 UTC (rev 642)
+++ trunk/extensions/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java 2008-11-20 19:15:17 UTC (rev 643)
@@ -195,18 +195,14 @@
@Override
public void process( CreateNodeRequest request ) {
- Path path = request.at().getPath();
- Path parent = path.getParent();
+ Path parent = request.under().getPath();
// Look up the parent node, which must exist ...
Node<Name, Object> parentNode = getNode(context, parent);
// Update the children to account for same-name siblings.
// This not only updates the FQN of the child nodes, but it also sets the property that stores the
// the array of Path.Segment for the children (since the cache doesn't maintain order).
- Path.Segment newSegment = updateChildList(parentNode,
- path.getLastSegment().getName(),
- getExecutionContext(),
- true);
+ Path.Segment newSegment = updateChildList(parentNode, request.named(), getExecutionContext(), true);
Node<Name, Object> node = parentNode.addChild(Fqn.fromElements(newSegment));
assert checkChildren(parentNode);
17 years, 1 month
DNA SVN: r642 - trunk/extensions/dna-connector-store-jpa.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-11-19 15:26:40 -0500 (Wed, 19 Nov 2008)
New Revision: 642
Added:
trunk/extensions/dna-connector-store-jpa/.classpath
trunk/extensions/dna-connector-store-jpa/.project
Log:
DNA-40 Added the Eclipse project files for this project
Added: trunk/extensions/dna-connector-store-jpa/.classpath
===================================================================
--- trunk/extensions/dna-connector-store-jpa/.classpath (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/.classpath 2008-11-19 20:26:40 UTC (rev 642)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="src" path="src/main/resources"/>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
Added: trunk/extensions/dna-connector-store-jpa/.project
===================================================================
--- trunk/extensions/dna-connector-store-jpa/.project (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/.project 2008-11-19 20:26:40 UTC (rev 642)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>dna-connector-store-jpa</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ </natures>
+</projectDescription>
17 years, 1 month
DNA SVN: r641 - in trunk: dna-common/src/main/java/org/jboss/dna/common/util and 38 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2008-11-19 14:34:05 -0500 (Wed, 19 Nov 2008)
New Revision: 641
Added:
trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java
trunk/extensions/dna-connector-store-jpa/
trunk/extensions/dna-connector-store-jpa/pom.xml
trunk/extensions/dna-connector-store-jpa/src/
trunk/extensions/dna-connector-store-jpa/src/main/
trunk/extensions/dna-connector-store-jpa/src/main/java/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java
trunk/extensions/dna-connector-store-jpa/src/main/resources/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/
trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
trunk/extensions/dna-connector-store-jpa/src/test/
trunk/extensions/dna-connector-store-jpa/src/test/java/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java
trunk/extensions/dna-connector-store-jpa/src/test/resources/
trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java
trunk/pom.xml
Log:
DNA-40 Persistant storage for information not stored in other repository sources
Started work on this new connector, which uses Java Persistence Architecture (JPA) to work with EJB3 entities stored in a relational database, using Hibernate for the JPA implementation. (Note that some Hibernate extensions are also used.)
The connector is capable of using one of several models (or schemas). The initial model is called Basic, but the connector supports having multiple models (which can be exist in separate packages) and allowing the user to choose via a JavaBean property in the JpaSource object. (Note that the kind of model used in a particular database instance is recorded in the database, and may not be changed. In this case, the JpaSource's model selection is changed to reflect what is in the database. Changing a database's model would require migration, which is not currently supported, but could be provided by a copy utility that operates on a graph by generically copying the content from one source to another.)
Each model is also responsible for creating the RequestProcessor implementation, so the JpaSource and JpaConnection implementations can work with any model.
The Basic model stores for each node a single record containing the node ID (which is a UUID string) and a blob for the serialized properties. Large The Basic model stores for each node a single record containing the node ID (which is a UUID string) and a blob for the serialized properties. Large The Basic model stores for each node a single record containing the node ID (which is a UUID string) and a blob for the serialized properties. Large The Basic model stores for each node a single record containing the node ID (which is a UUID string) and a blob for the serialized properties. Large The Basic model stores for each node a single record containing the node ID (which is a UUID string) and a blob for the serialized properties. Large The Basic model stores for each node a single record containing the node ID (which is a UUID string) andxistsThe Basic model stores for each node a single record containing the node ID (which is a UUID string) and a blob for the serialized prop!
erties. Large Th current implementation is probably close to 75% complete. The Basic entities are mostly completed (minor changes are anticipated), and all of the source infrastructure has been completed. Most of the outstanding work is in the BasicRequestProcessor implementation.
Added: trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java (rev 0)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/SecureHash.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,104 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.common.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author Randall Hauch
+ */
+public class SecureHash {
+
+ /**
+ * Commonly-used hashing algorithms.
+ */
+ public enum Algorithm {
+ MD2("MD2", "The MD2 message digest algorithm as defined in RFC 1319"),
+ MD5("MD5", "The MD5 message digest algorithm as defined in RFC 1321"),
+ SHA_1("SHA-1", "The Secure Hash Algorithm, as defined in Secure Hash Standard, NIST FIPS 180-1"),
+ SHA_256(
+ "SHA-256",
+ "New hash algorithms for which the draft Federal Information Processing Standard 180-2, "
+ + "Secure Hash Standard (SHS) is now available. SHA-256 is a 256-bit hash function intended to provide 128 bits of "
+ + "security against collision attacks."),
+ SHA_384(
+ "SHA-384",
+ "New hash algorithms for which the draft Federal Information Processing Standard 180-2, "
+ + "Secure Hash Standard (SHS) is now available. A 384-bit hash may be obtained by truncating the SHA-512 output."),
+ SHA_512(
+ "SHA-512",
+ "New hash algorithms for which the draft Federal Information Processing Standard 180-2, "
+ + "Secure Hash Standard (SHS) is now available. SHA-512 is a 512-bit hash function intended to provide 256 bits of security.");
+ private String name;
+ private String description;
+
+ private Algorithm( String name,
+ String description ) {
+ this.name = name;
+ this.description = description;
+ }
+
+ public String digestName() {
+ return this.name;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ @Override
+ public String toString() {
+ return digestName();
+ }
+ }
+
+ /**
+ * Get the hash of the supplied content, using the supplied digest algorithm.
+ *
+ * @param algorithm the hashing function algorithm that should be used
+ * @param content the content to be hashed; may not be null
+ * @return the hash of the contents as a byte array
+ * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
+ * @throws IllegalArgumentException if the algorithm is null
+ */
+ public static byte[] getHash( Algorithm algorithm,
+ byte[] content ) throws NoSuchAlgorithmException {
+ CheckArg.isNotNull(algorithm, "algorithm");
+ return getHash(algorithm.digestName(), content);
+ }
+
+ /**
+ * Get the hash of the supplied content, using the digest identified by the supplied name.
+ *
+ * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
+ * @param content the content to be hashed; may not be null
+ * @return the hash of the contents as a byte array
+ * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
+ */
+ public static byte[] getHash( String digestName,
+ byte[] content ) throws NoSuchAlgorithmException {
+ MessageDigest digest = MessageDigest.getInstance(digestName);
+ assert digest != null;
+ return digest.digest(content);
+ }
+}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-11-18 13:00:30 UTC (rev 640)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -494,4 +494,21 @@
return this;
}
+ /**
+ * Create a copy of this location that adds the supplied UUID as an identification property. The new identification property
+ * will replace any existing identification property with the same name on the original.
+ *
+ * @param uuid the new UUID, which may be null
+ * @return the new location, or this location if the new identification property is null or empty
+ */
+ public Location with( UUID uuid ) {
+ if (uuid == null) return this;
+ Property newProperty = new BasicSingleValueProperty(DnaLexicon.UUID, uuid);
+ if (this.hasIdProperties()) {
+ Property existing = this.getIdProperty(DnaLexicon.UUID);
+ if (existing != null && existing.equals(newProperty)) return this;
+ }
+ return new Location(this, newProperty);
+ }
+
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java 2008-11-18 13:00:30 UTC (rev 640)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/InMemoryBinary.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -23,7 +23,6 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -31,6 +30,7 @@
import org.jboss.dna.common.util.Base64;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.common.util.SecureHash;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.properties.Binary;
import org.jboss.dna.graph.properties.ValueComparators;
@@ -44,7 +44,7 @@
public class InMemoryBinary implements Binary {
protected static final Set<String> ALGORITHMS_NOT_FOUND_AND_LOGGED = new CopyOnWriteArraySet<String>();
- private static final String SHA1DIGEST_NAME = "SHA-1";
+ private static final SecureHash.Algorithm ALGORITHM = SecureHash.Algorithm.SHA_1;
private static final byte[] NO_HASH = new byte[] {};
/**
@@ -75,12 +75,12 @@
*/
public byte[] getHash() {
if (sha1hash == null) {
- // Omnipotent, so doesn't matter if we recompute in concurrent threads ...
+ // Idempotent, so doesn't matter if we recompute in concurrent threads ...
try {
- sha1hash = getHash(SHA1DIGEST_NAME);
+ sha1hash = SecureHash.getHash(ALGORITHM, bytes);
} catch (NoSuchAlgorithmException e) {
- if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(SHA1DIGEST_NAME)) {
- Logger.getLogger(getClass()).error(e, GraphI18n.messageDigestNotFound, SHA1DIGEST_NAME);
+ if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(ALGORITHM.digestName())) {
+ Logger.getLogger(getClass()).error(e, GraphI18n.messageDigestNotFound, ALGORITHM.digestName());
}
sha1hash = NO_HASH;
}
@@ -89,19 +89,6 @@
}
/**
- * Get the hash of the contents, using the digest identified by the supplied name.
- *
- * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
- * @return the hash of the contents as a byte array
- * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
- */
- protected byte[] getHash( String digestName ) throws NoSuchAlgorithmException {
- MessageDigest digest = MessageDigest.getInstance(digestName);
- assert digest != null;
- return digest.digest(bytes);
- }
-
- /**
* {@inheritDoc}
*/
public byte[] getBytes() {
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-18 13:00:30 UTC (rev 640)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/CreateNodeRequest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -55,7 +55,7 @@
* @param at the location of the node to be read
* @param properties the properties of the new node, which should not include the location's
* {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null
+ * @throws IllegalArgumentException if the location is null or has no {@link Location#getPath() path}
*/
public CreateNodeRequest( Location at,
Property... properties ) {
@@ -68,7 +68,7 @@
* @param at the location of the node to be read
* @param properties the properties of the new node, which should not include the location's
* {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null
+ * @throws IllegalArgumentException if the location is null or has no {@link Location#getPath() path}
*/
public CreateNodeRequest( Location at,
Iterable<Property> properties ) {
@@ -81,7 +81,7 @@
* @param at the location of the node to be read
* @param properties the properties of the new node, which should not include the location's
* {@link Location#getIdProperties() identification properties}
- * @throws IllegalArgumentException if the location is null
+ * @throws IllegalArgumentException if the location is null or has no {@link Location#getPath() path}
*/
public CreateNodeRequest( Location at,
Iterator<Property> properties ) {
@@ -96,13 +96,15 @@
* {@link Location#getIdProperties() identification properties}
* @param conflictBehavior the expected behavior if an equivalently-named child already exists at the <code>into</code>
* location
- * @throws IllegalArgumentException if the location or the conflict behavior is null
+ * @throws IllegalArgumentException if the location or the conflict behavior is null, or if the location does not have a
+ * {@link Location#getPath() path}
*/
public CreateNodeRequest( Location at,
NodeConflictBehavior conflictBehavior,
Property... properties ) {
CheckArg.isNotNull(at, "at");
CheckArg.isNotNull(conflictBehavior, "conflictBehavior");
+ CheckArg.isNotNull(at.getPath(), "at.getPath()");
this.at = at;
this.conflictBehavior = conflictBehavior;
int number = properties.length + (at.hasIdProperties() ? at.getIdProperties().size() : 0);
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java 2008-11-18 13:00:30 UTC (rev 640)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/properties/basic/InMemoryBinaryTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -21,16 +21,15 @@
*/
package org.jboss.dna.graph.properties.basic;
+import static org.hamcrest.core.Is.is;
import static org.jboss.dna.graph.properties.basic.BinaryContains.hasContent;
import static org.jboss.dna.graph.properties.basic.BinaryContains.hasNoContent;
-import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.io.InputStream;
import org.jboss.dna.common.util.IoUtil;
import org.jboss.dna.common.util.StringUtil;
import org.jboss.dna.graph.properties.Binary;
-import org.jboss.dna.graph.properties.basic.InMemoryBinary;
import org.junit.Before;
import org.junit.Test;
Property changes on: trunk/extensions/dna-connector-store-jpa
___________________________________________________________________
Name: svn:ignore
+ target
Added: trunk/extensions/dna-connector-store-jpa/pom.xml
===================================================================
--- trunk/extensions/dna-connector-store-jpa/pom.xml (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/pom.xml 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna</artifactId>
+ <version>0.4-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <!-- The groupId and version values are inherited from parent -->
+ <artifactId>dna-connector-store-jpa</artifactId>
+ <packaging>jar</packaging>
+ <name>JBoss DNA Connector to JPA Persistence Store</name>
+ <description>JBoss DNA Connector that persists graph content using JPA.</description>
+ <url>http://labs.jboss.org/dna</url>
+ <!--
+ Define the dependencies. Note that all version and scopes default to those defined in the dependencyManagement section of the
+ parent pom.
+ -->
+ <dependencies>
+ <!--
+ JBoss DNA
+ -->
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ </dependency>
+ <!--
+ JPA via Hibernate Entity Manager
+ -->
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>3.4.0.GA</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-c3p0</artifactId>
+ <version>3.3.1.GA</version>
+ </dependency>
+ <!--
+ HSQLDB
+ -->
+ <dependency>
+ <groupId>hsqldb</groupId>
+ <artifactId>hsqldb</artifactId>
+ <version>1.8.0.2</version>
+ </dependency>
+ <!--
+ Testing (note the scope)
+ -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for testing)
+ -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <!--
+ Java Concurrency in Practice annotations
+ -->
+ <dependency>
+ <groupId>net.jcip</groupId>
+ <artifactId>jcip-annotations</artifactId>
+ </dependency>
+ </dependencies>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
\ No newline at end of file
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnection.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,141 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import javax.persistence.EntityManager;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+import org.jboss.dna.graph.connectors.RepositorySourceListener;
+import org.jboss.dna.graph.requests.Request;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnection implements RepositoryConnection {
+
+ private final String name;
+ private final CachePolicy cachePolicy;
+ private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new CopyOnWriteArrayList<RepositorySourceListener>();
+ private final EntityManager entityManager;
+ private final Model model;
+ private final UUID rootNodeUuid;
+ private final long largeValueMinimumSizeInBytes;
+
+ /*package*/JpaConnection( String sourceName,
+ CachePolicy cachePolicy,
+ EntityManager entityManager,
+ Model model,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes ) {
+ assert sourceName != null;
+ assert entityManager != null;
+ assert model != null;
+ assert rootNodeUuid != null;
+ this.name = sourceName;
+ this.cachePolicy = cachePolicy; // may be null
+ this.entityManager = entityManager;
+ this.model = model;
+ this.rootNodeUuid = rootNodeUuid;
+ this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#setListener(org.jboss.dna.graph.connectors.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ if (listener != null) {
+ listeners.addIfAbsent(listener);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return cachePolicy;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#ping(long, java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return entityManager.isOpen();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.requests.Request)
+ */
+ public void execute( ExecutionContext context,
+ Request request ) throws RepositorySourceException {
+ long size = largeValueMinimumSizeInBytes;
+ RequestProcessor proc = model.createRequestProcessor(name, context, entityManager, rootNodeUuid, size);
+ try {
+ proc.process(request);
+ } finally {
+ proc.close();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositoryConnection#close()
+ */
+ public void close() {
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ */
+public final class JpaConnectorI18n {
+
+ public static I18n connectorName;
+ public static I18n nodeDoesNotExist;
+ public static I18n propertyIsRequired;
+ public static I18n errorFindingDataSourceInJndi;
+ public static I18n repositorySourceMustHaveName;
+ public static I18n unknownModelName;
+ public static I18n errorSettingContextClassLoader;
+ public static I18n existingStoreSpecifiesUnknownModel;
+ public static I18n unableToReadLargeValue;
+
+ public static I18n basicModelDescription;
+
+ static {
+ try {
+ I18n.initialize(JpaConnectorI18n.class);
+ } catch (final Exception err) {
+ System.err.println(err);
+ }
+ }
+
+ public static Set<Locale> getLocalizationProblemLocales() {
+ return I18n.getLocalizationProblemLocales(JpaConnectorI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems() {
+ return I18n.getLocalizationProblems(JpaConnectorI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems( Locale locale ) {
+ return I18n.getLocalizationProblems(JpaConnectorI18n.class, locale);
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/JpaSource.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,884 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.ThreadSafe;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.connector.store.jpa.models.basic.BasicModel;
+import org.jboss.dna.connector.store.jpa.util.StoreOptionEntity;
+import org.jboss.dna.connector.store.jpa.util.StoreOptions;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositoryContext;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.connectors.RepositorySourceCapabilities;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaSource implements RepositorySource, ObjectFactory {
+
+ /**
+ * This source is capable of using different database schemas
+ *
+ * @author Randall Hauch
+ */
+ public static class Models {
+ public static final Model BASIC = new BasicModel();
+ private static final Model[] ALL_ARRAY = new Model[] {BASIC};
+ private static final List<Model> MODIFIABLE_MODELS = new ArrayList<Model>(Arrays.asList(ALL_ARRAY));
+ public static final Collection<Model> ALL = Collections.unmodifiableCollection(MODIFIABLE_MODELS);
+ public static final Model DEFAULT = BASIC;
+
+ public static boolean addModel( Model model ) {
+ CheckArg.isNotNull(model, "modelName");
+ for (Model existing : MODIFIABLE_MODELS) {
+ if (existing.getName().equals(model.getName())) return false;
+ }
+ return MODIFIABLE_MODELS.add(model);
+ }
+
+ public static Model getModel( String name ) {
+ CheckArg.isNotEmpty(name, "name");
+ name = name.trim();
+ for (Model existing : ALL) {
+ if (existing.getName().equals(name)) return existing;
+ }
+ return null;
+ }
+ }
+
+ protected static final String SOURCE_NAME = "sourceName";
+ protected static final String ROOT_NODE_UUID = "rootNodeUuid";
+ protected static final String DATA_SOURCE_JNDI_NAME = "dataSourceJndiName";
+ protected static final String DIALECT = "dialect";
+ protected static final String USERNAME = "username";
+ protected static final String PASSWORD = "password";
+ protected static final String URL = "url";
+ protected static final String DRIVER_CLASS_NAME = "driverClassName";
+ protected static final String DRIVER_CLASSLOADER_NAME = "driverClassloaderName";
+ protected static final String MAXIMUM_CONNECTIONS_IN_POOL = "maximumConnectionsInPool";
+ protected static final String MINIMUM_CONNECTIONS_IN_POOL = "minimumConnectionsInPool";
+ protected static final String MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS = "maximumConnectionIdleTimeInSeconds";
+ protected static final String MAXIMUM_SIZE_OF_STATEMENT_CACHE = "maximumSizeOfStatementCache";
+ protected static final String NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED = "numberOfConnectionsToBeAcquiredAsNeeded";
+ protected static final String IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS = "idleTimeInSecondsBeforeTestingConnections";
+ protected static final String CACHE_TIME_TO_LIVE_IN_MILLISECONDS = "cacheTimeToLiveInMilliseconds";
+ protected static final String RETRY_LIMIT = "retryLimit";
+ protected static final String MODEL_NAME = "modelName";
+ protected static final String LARGE_VALUE_SIZE_IN_BYTES = "largeValueSizeInBytes";
+
+ /**
+ * This source supports events.
+ */
+ protected static final boolean SUPPORTS_EVENTS = true;
+ /**
+ * This source supports same-name-siblings.
+ */
+ protected static final boolean SUPPORTS_SAME_NAME_SIBLINGS = true;
+ /**
+ * This source supports udpates by default, but each instance may be configured to {@link #setSupportsUpdates(boolean) be
+ * read-only or updateable}.
+ */
+ public static final boolean DEFAULT_SUPPORTS_UPDATES = true;
+
+ /**
+ * The default UUID that is used for root nodes in a store.
+ */
+ public static final String DEFAULT_ROOT_NODE_UUID = "1497b6fe-8c7e-4bbb-aaa2-24f3d4942668";
+
+ private static final int DEFAULT_RETRY_LIMIT = 0;
+ private static final int DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS = 60 * 5; // 5 minutes
+ private static final int DEFAULT_MAXIMUM_FETCH_DEPTH = 3;
+ private static final int DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL = 5;
+ private static final int DEFAULT_MINIMUM_CONNECTIONS_IN_POOL = 0;
+ private static final int DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS = 60 * 10; // 10 minutes
+ private static final int DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE = 100;
+ private static final int DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED = 1;
+ private static final int DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS = 60 * 3; // 3 minutes
+ private static final int DEFAULT_LARGE_VALUE_SIZE_IN_BYTES = 2 ^ 10; // 1 kilobyte
+
+ /**
+ * The first serialized version of this source.
+ */
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+ private String dataSourceJndiName;
+ private String dialect;
+ private String username;
+ private String password;
+ private String url;
+ private String driverClassName;
+ private String driverClassloaderName;
+ private String rootNodeUuid = DEFAULT_ROOT_NODE_UUID;
+ private int maximumConnectionsInPool = DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL;
+ private int minimumConnectionsInPool = DEFAULT_MINIMUM_CONNECTIONS_IN_POOL;
+ private int maximumConnectionIdleTimeInSeconds = DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS;
+ private int maximumSizeOfStatementCache = DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE;
+ private int numberOfConnectionsToAcquireAsNeeded = DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED;
+ private int idleTimeInSecondsBeforeTestingConnections = DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS;
+ private int retryLimit = DEFAULT_RETRY_LIMIT;
+ private int cacheTimeToLiveInMilliseconds = DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS * 1000;
+ private long largeValueSizeInBytes = DEFAULT_LARGE_VALUE_SIZE_IN_BYTES;
+ private final Capabilities capabilities = new Capabilities();
+ private transient Model model;
+ private String modelName;
+ private transient DataSource dataSource;
+ private transient EntityManagerFactory entityManagerFactory;
+ private transient CachePolicy cachePolicy;
+ private transient RepositoryContext repositoryContext;
+ private transient UUID rootUuid = UUID.fromString(rootNodeUuid);
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public void setName( String name ) {
+ if (name != null) {
+ name = name.trim();
+ if (name.length() == 0) name = null;
+ }
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getCapabilities()
+ */
+ public RepositorySourceCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ /**
+ * Get whether this source supports updates.
+ *
+ * @return true if this source supports updates, or false if this source only supports reading content.
+ */
+ public boolean getSupportsUpdates() {
+ return capabilities.supportsUpdates();
+ }
+
+ /**
+ * Set whether this source supports updates.
+ *
+ * @param supportsUpdates true if this source supports updating content, or false if this source only supports reading
+ * content.
+ */
+ public synchronized void setSupportsUpdates( boolean supportsUpdates ) {
+ capabilities.setSupportsUpdates(supportsUpdates);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getRetryLimit()
+ */
+ public int getRetryLimit() {
+ return retryLimit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#setRetryLimit(int)
+ */
+ public synchronized void setRetryLimit( int limit ) {
+ if (limit < 0) limit = 0;
+ this.retryLimit = limit;
+ }
+
+ /**
+ * Get the time in milliseconds that content returned from this source may used while in the cache.
+ *
+ * @return the time to live, in milliseconds, or 0 if the time to live is not specified by this source
+ */
+ public int getCacheTimeToLiveInMilliseconds() {
+ return cacheTimeToLiveInMilliseconds;
+ }
+
+ /**
+ * Set the time in milliseconds that content returned from this source may used while in the cache.
+ *
+ * @param cacheTimeToLive the time to live, in milliseconds; 0 if the time to live is not specified by this source; or a
+ * negative number for the default value
+ */
+ public synchronized void setCacheTimeToLiveInMilliseconds( int cacheTimeToLive ) {
+ if (cacheTimeToLive < 0) cacheTimeToLive = DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS;
+ this.cacheTimeToLiveInMilliseconds = cacheTimeToLive;
+ this.cachePolicy = cacheTimeToLiveInMilliseconds > 0 ? new JpaCachePolicy(cacheTimeToLiveInMilliseconds) : null;
+ }
+
+ /**
+ * @return rootNodeUuid
+ */
+ public String getRootNodeUuid() {
+ return rootNodeUuid;
+ }
+
+ /**
+ * @param rootNodeUuid Sets rootNodeUuid to the specified value.
+ * @throws IllegalArgumentException if the string value cannot be converted to UUID
+ */
+ public void setRootNodeUuid( String rootNodeUuid ) {
+ if (rootNodeUuid != null && rootNodeUuid.trim().length() == 0) rootNodeUuid = DEFAULT_ROOT_NODE_UUID;
+ this.rootUuid = UUID.fromString(rootNodeUuid);
+ this.rootNodeUuid = rootNodeUuid;
+ }
+
+ /**
+ * @return dialect
+ */
+ public String getDialect() {
+ return dialect;
+ }
+
+ /**
+ * @param dialect Sets dialect to the specified value.
+ */
+ public synchronized void setDialect( String dialect ) {
+ if (dialect != null && dialect.trim().length() == 0) dialect = null;
+ this.dialect = dialect;
+ }
+
+ /**
+ * @return dataSourceJndiName
+ */
+ public String getDataSourceJndiName() {
+ return dataSourceJndiName;
+ }
+
+ /**
+ * @param dataSourceJndiName Sets dataSourceJndiName to the specified value.
+ */
+ public void setDataSourceJndiName( String dataSourceJndiName ) {
+ if (dataSourceJndiName != null && dataSourceJndiName.trim().length() == 0) dataSourceJndiName = null;
+ this.dataSourceJndiName = dataSourceJndiName;
+ }
+
+ /**
+ * @return driverClassName
+ */
+ public String getDriverClassName() {
+ return driverClassName;
+ }
+
+ /**
+ * @param driverClassName Sets driverClassName to the specified value.
+ */
+ public synchronized void setDriverClassName( String driverClassName ) {
+ if (driverClassName != null && driverClassName.trim().length() == 0) driverClassName = null;
+ this.driverClassName = driverClassName;
+ }
+
+ /**
+ * @return driverClassloaderName
+ */
+ public String getDriverClassloaderName() {
+ return driverClassloaderName;
+ }
+
+ /**
+ * @param driverClassloaderName Sets driverClassloaderName to the specified value.
+ */
+ public void setDriverClassloaderName( String driverClassloaderName ) {
+ if (driverClassloaderName != null && driverClassloaderName.trim().length() == 0) driverClassloaderName = null;
+ this.driverClassloaderName = driverClassloaderName;
+ }
+
+ /**
+ * @return username
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * @param username Sets username to the specified value.
+ */
+ public synchronized void setUsername( String username ) {
+ this.username = username;
+ }
+
+ /**
+ * @return password
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * @param password Sets password to the specified value.
+ */
+ public synchronized void setPassword( String password ) {
+ this.password = password;
+ }
+
+ /**
+ * @return url
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * @param url Sets url to the specified value.
+ */
+ public synchronized void setUrl( String url ) {
+ if (url != null && url.trim().length() == 0) url = null;
+ this.url = url;
+ }
+
+ /**
+ * @return maximumConnectionsInPool
+ */
+ public int getMaximumConnectionsInPool() {
+ return maximumConnectionsInPool;
+ }
+
+ /**
+ * @param maximumConnectionsInPool Sets maximumConnectionsInPool to the specified value.
+ */
+ public synchronized void setMaximumConnectionsInPool( int maximumConnectionsInPool ) {
+ if (maximumConnectionsInPool < 0) maximumConnectionsInPool = DEFAULT_MAXIMUM_CONNECTIONS_IN_POOL;
+ this.maximumConnectionsInPool = maximumConnectionsInPool;
+ }
+
+ /**
+ * @return minimumConnectionsInPool
+ */
+ public int getMinimumConnectionsInPool() {
+ return minimumConnectionsInPool;
+ }
+
+ /**
+ * @param minimumConnectionsInPool Sets minimumConnectionsInPool to the specified value.
+ */
+ public synchronized void setMinimumConnectionsInPool( int minimumConnectionsInPool ) {
+ if (minimumConnectionsInPool < 0) minimumConnectionsInPool = DEFAULT_MINIMUM_CONNECTIONS_IN_POOL;
+ this.minimumConnectionsInPool = minimumConnectionsInPool;
+ }
+
+ /**
+ * @return maximumConnectionIdleTimeInSeconds
+ */
+ public int getMaximumConnectionIdleTimeInSeconds() {
+ return maximumConnectionIdleTimeInSeconds;
+ }
+
+ /**
+ * @param maximumConnectionIdleTimeInSeconds Sets maximumConnectionIdleTimeInSeconds to the specified value.
+ */
+ public synchronized void setMaximumConnectionIdleTimeInSeconds( int maximumConnectionIdleTimeInSeconds ) {
+ if (maximumConnectionIdleTimeInSeconds < 0) maximumConnectionIdleTimeInSeconds = DEFAULT_MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS;
+ this.maximumConnectionIdleTimeInSeconds = maximumConnectionIdleTimeInSeconds;
+ }
+
+ /**
+ * @return maximumSizeOfStatementCache
+ */
+ public int getMaximumSizeOfStatementCache() {
+ return maximumSizeOfStatementCache;
+ }
+
+ /**
+ * @param maximumSizeOfStatementCache Sets maximumSizeOfStatementCache to the specified value.
+ */
+ public synchronized void setMaximumSizeOfStatementCache( int maximumSizeOfStatementCache ) {
+ if (maximumSizeOfStatementCache < 0) maximumSizeOfStatementCache = DEFAULT_MAXIMUM_NUMBER_OF_STATEMENTS_TO_CACHE;
+ this.maximumSizeOfStatementCache = maximumSizeOfStatementCache;
+ }
+
+ /**
+ * @return numberOfConnectionsToAcquireAsNeeded
+ */
+ public int getNumberOfConnectionsToAcquireAsNeeded() {
+ return numberOfConnectionsToAcquireAsNeeded;
+ }
+
+ /**
+ * @param numberOfConnectionsToAcquireAsNeeded Sets numberOfConnectionsToAcquireAsNeeded to the specified value.
+ */
+ public synchronized void setNumberOfConnectionsToAcquireAsNeeded( int numberOfConnectionsToAcquireAsNeeded ) {
+ if (numberOfConnectionsToAcquireAsNeeded < 0) numberOfConnectionsToAcquireAsNeeded = DEFAULT_NUMBER_OF_CONNECTIONS_TO_ACQUIRE_AS_NEEDED;
+ this.numberOfConnectionsToAcquireAsNeeded = numberOfConnectionsToAcquireAsNeeded;
+ }
+
+ /**
+ * @return idleTimeInSecondsBeforeTestingConnections
+ */
+ public int getIdleTimeInSecondsBeforeTestingConnections() {
+ return idleTimeInSecondsBeforeTestingConnections;
+ }
+
+ /**
+ * @param idleTimeInSecondsBeforeTestingConnections Sets idleTimeInSecondsBeforeTestingConnections to the specified value.
+ */
+ public synchronized void setIdleTimeInSecondsBeforeTestingConnections( int idleTimeInSecondsBeforeTestingConnections ) {
+ if (idleTimeInSecondsBeforeTestingConnections < 0) idleTimeInSecondsBeforeTestingConnections = DEFAULT_IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS;
+ this.idleTimeInSecondsBeforeTestingConnections = idleTimeInSecondsBeforeTestingConnections;
+ }
+
+ /**
+ * Get the {@link DataSource} object that this source is to use.
+ *
+ * @return the data source; may be null if no data source has been set or found in JNDI
+ * @see #setDataSource(DataSource)
+ * @see #setDataSourceJndiName(String)
+ */
+ /*package*/DataSource getDataSource() {
+ return dataSource;
+ }
+
+ /**
+ * Set the {@link DataSource} instance that this source should use.
+ *
+ * @param dataSource the data source; may be null
+ * @see #getDataSource()
+ * @see #setDataSourceJndiName(String)
+ */
+ /*package*/synchronized void setDataSource( DataSource dataSource ) {
+ this.dataSource = dataSource;
+ }
+
+ /**
+ * Get the model that will be used. This may be null if not yet connected, but after connections will reflect the type of
+ * model that is being used in the store.
+ *
+ * @return the name of the model
+ */
+ public String getModel() {
+ return modelName;
+ }
+
+ /**
+ * Set the model that should be used for this store. If the store already has a model, specifying a different value has no
+ * effect, since the store's model will not be changed. After connection, this value will reflect the actual store value.
+ *
+ * @param modelName the name of the model that should be used for new stores, or null if the default should be used
+ */
+ public synchronized void setModel( String modelName ) {
+ if (modelName != null) {
+ modelName = modelName.trim();
+ if (modelName.length() == 0) modelName = null;
+ }
+ if (modelName == null) {
+ model = null;
+ return;
+ }
+ Model model = Models.getModel(modelName);
+ if (model == null) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (Model existing : Models.ALL) {
+ if (!first) {
+ first = false;
+ sb.append(", ");
+ }
+ sb.append('"').append(existing.getName()).append('"');
+ }
+ String modelNames = sb.toString();
+ throw new IllegalArgumentException(JpaConnectorI18n.unknownModelName.text(model, modelNames));
+ }
+ this.model = model;
+ this.modelName = modelName;
+ }
+
+ /**
+ * @return largeValueSizeInBytes
+ */
+ public long getLargeValueSizeInBytes() {
+ return largeValueSizeInBytes;
+ }
+
+ /**
+ * @param largeValueSizeInBytes Sets largeValueSizeInBytes to the specified value.
+ */
+ public void setLargeValueSizeInBytes( long largeValueSizeInBytes ) {
+ if (largeValueSizeInBytes < 0) largeValueSizeInBytes = DEFAULT_LARGE_VALUE_SIZE_IN_BYTES;
+ this.largeValueSizeInBytes = largeValueSizeInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#initialize(org.jboss.dna.graph.connectors.RepositoryContext)
+ */
+ public void initialize( RepositoryContext context ) throws RepositorySourceException {
+ this.repositoryContext = context;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.Referenceable#getReference()
+ */
+ public Reference getReference() {
+ String className = getClass().getName();
+ String factoryClassName = this.getClass().getName();
+ Reference ref = new Reference(className, factoryClassName, null);
+
+ if (getName() != null) {
+ ref.add(new StringRefAddr(SOURCE_NAME, getName()));
+ }
+ if (getRootNodeUuid() != null) {
+ ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid()));
+ }
+ if (getDataSourceJndiName() != null) {
+ ref.add(new StringRefAddr(DATA_SOURCE_JNDI_NAME, getDataSourceJndiName()));
+ }
+ if (getDialect() != null) {
+ ref.add(new StringRefAddr(DIALECT, getDialect()));
+ }
+ if (getUsername() != null) {
+ ref.add(new StringRefAddr(USERNAME, getUsername()));
+ }
+ if (getPassword() != null) {
+ ref.add(new StringRefAddr(PASSWORD, getPassword()));
+ }
+ if (getUrl() != null) {
+ ref.add(new StringRefAddr(URL, getUrl()));
+ }
+ if (getDriverClassName() != null) {
+ ref.add(new StringRefAddr(DRIVER_CLASS_NAME, getDriverClassName()));
+ }
+ if (getDriverClassloaderName() != null) {
+ ref.add(new StringRefAddr(DRIVER_CLASSLOADER_NAME, getDriverClassloaderName()));
+ }
+ ref.add(new StringRefAddr(MAXIMUM_CONNECTIONS_IN_POOL, Integer.toString(getMaximumConnectionsInPool())));
+ ref.add(new StringRefAddr(MINIMUM_CONNECTIONS_IN_POOL, Integer.toString(getMinimumConnectionsInPool())));
+ ref.add(new StringRefAddr(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS,
+ Integer.toString(getMaximumConnectionIdleTimeInSeconds())));
+ ref.add(new StringRefAddr(MAXIMUM_SIZE_OF_STATEMENT_CACHE, Integer.toString(getMaximumSizeOfStatementCache())));
+ ref.add(new StringRefAddr(NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED,
+ Integer.toString(getNumberOfConnectionsToAcquireAsNeeded())));
+ ref.add(new StringRefAddr(IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS,
+ Integer.toString(getIdleTimeInSecondsBeforeTestingConnections())));
+ ref.add(new StringRefAddr(CACHE_TIME_TO_LIVE_IN_MILLISECONDS, Integer.toString(getCacheTimeToLiveInMilliseconds())));
+ ref.add(new StringRefAddr(LARGE_VALUE_SIZE_IN_BYTES, Long.toString(getLargeValueSizeInBytes())));
+ if (getModel() != null) {
+ ref.add(new StringRefAddr(MODEL_NAME, getModel()));
+ }
+ ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
+ return ref;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getObjectInstance( Object obj,
+ javax.naming.Name name,
+ Context nameCtx,
+ Hashtable<?, ?> environment ) throws Exception {
+ if (obj instanceof Reference) {
+ Map<String, String> values = new HashMap<String, String>();
+ Reference ref = (Reference)obj;
+ Enumeration<?> en = ref.getAll();
+ while (en.hasMoreElements()) {
+ RefAddr subref = (RefAddr)en.nextElement();
+ if (subref instanceof StringRefAddr) {
+ String key = subref.getType();
+ Object value = subref.getContent();
+ if (value != null) values.put(key, value.toString());
+ }
+ }
+ String sourceName = values.get(SOURCE_NAME);
+ String rootNodeUuid = values.get(ROOT_NODE_UUID);
+ String dataSourceJndiName = values.get(DATA_SOURCE_JNDI_NAME);
+ String dialect = values.get(DIALECT);
+ String username = values.get(USERNAME);
+ String password = values.get(PASSWORD);
+ String url = values.get(URL);
+ String driverClassName = values.get(DRIVER_CLASS_NAME);
+ String driverClassloaderName = values.get(DRIVER_CLASSLOADER_NAME);
+ String maxConnectionsInPool = values.get(MAXIMUM_CONNECTIONS_IN_POOL);
+ String minConnectionsInPool = values.get(MINIMUM_CONNECTIONS_IN_POOL);
+ String maxConnectionIdleTimeInSec = values.get(MAXIMUM_CONNECTION_IDLE_TIME_IN_SECONDS);
+ String maxSizeOfStatementCache = values.get(MAXIMUM_SIZE_OF_STATEMENT_CACHE);
+ String acquisitionIncrement = values.get(NUMBER_OF_CONNECTIONS_TO_BE_ACQUIRED_AS_NEEDED);
+ String idleTimeInSeconds = values.get(IDLE_TIME_IN_SECONDS_BEFORE_TESTING_CONNECTIONS);
+ String cacheTtlInMillis = values.get(CACHE_TIME_TO_LIVE_IN_MILLISECONDS);
+ String modelName = values.get(MODEL_NAME);
+ String retryLimit = values.get(RETRY_LIMIT);
+ String largeModelSize = values.get(LARGE_VALUE_SIZE_IN_BYTES);
+
+ // Create the source instance ...
+ JpaSource source = new JpaSource();
+ if (sourceName != null) source.setName(sourceName);
+ if (rootNodeUuid != null) source.setRootNodeUuid(rootNodeUuid);
+ if (dataSourceJndiName != null) source.setDataSourceJndiName(dataSourceJndiName);
+ if (dialect != null) source.setDialect(dialect);
+ if (username != null) source.setUsername(username);
+ if (password != null) source.setPassword(password);
+ if (url != null) source.setUrl(url);
+ if (driverClassName != null) source.setDriverClassName(driverClassName);
+ if (driverClassloaderName != null) source.setDriverClassloaderName(driverClassloaderName);
+ if (maxConnectionsInPool != null) source.setMaximumConnectionsInPool(Integer.parseInt(maxConnectionsInPool));
+ if (minConnectionsInPool != null) source.setMinimumConnectionsInPool(Integer.parseInt(minConnectionsInPool));
+ if (maxConnectionIdleTimeInSec != null) source.setMaximumConnectionIdleTimeInSeconds(Integer.parseInt(maxConnectionIdleTimeInSec));
+ if (maxSizeOfStatementCache != null) source.setMaximumSizeOfStatementCache(Integer.parseInt(maxSizeOfStatementCache));
+ if (acquisitionIncrement != null) source.setNumberOfConnectionsToAcquireAsNeeded(Integer.parseInt(acquisitionIncrement));
+ if (idleTimeInSeconds != null) source.setIdleTimeInSecondsBeforeTestingConnections(Integer.parseInt(idleTimeInSeconds));
+ if (cacheTtlInMillis != null) source.setCacheTimeToLiveInMilliseconds(Integer.parseInt(cacheTtlInMillis));
+ if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
+ if (modelName != null) source.setModel(modelName);
+ if (largeModelSize != null) source.setLargeValueSizeInBytes(Long.parseLong(largeModelSize));
+ return source;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.RepositorySource#getConnection()
+ */
+ public synchronized RepositoryConnection getConnection() throws RepositorySourceException {
+ if (this.name == null || this.name.trim().length() == 0) {
+ throw new RepositorySourceException(JpaConnectorI18n.repositorySourceMustHaveName.text());
+ }
+ assert rootNodeUuid != null;
+ assert rootUuid != null;
+ EntityManager entityManager = null;
+ if (entityManagerFactory == null || !entityManagerFactory.isOpen()) {
+ // Create the JPA EntityManagerFactory by programmatically configuring Hibernate Entity Manager ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+
+ // Configure the entity classes ...
+ configurator.addAnnotatedClass(StoreOptionEntity.class);
+ if (model != null) model.configure(configurator);
+
+ // Configure additional properties, which may be overridden by subclasses ...
+ configure(configurator);
+
+ // Now set the mandatory information, overwriting anything that the subclasses may have tried ...
+ if (this.dataSource == null && this.dataSourceJndiName != null) {
+ // Try to load the DataSource from JNDI ...
+ try {
+ Context context = new InitialContext();
+ dataSource = (DataSource)context.lookup(this.dataSourceJndiName);
+ } catch (Throwable t) {
+ Logger.getLogger(getClass()).error(t, JpaConnectorI18n.errorFindingDataSourceInJndi, name, dataSourceJndiName);
+ }
+ }
+
+ if (this.dataSource != null) {
+ // Set the data source ...
+ configurator.setDataSource(this.dataSource);
+ } else {
+ // Set the context class loader, so that the driver could be found ...
+ if (this.repositoryContext != null && this.driverClassloaderName != null) {
+ try {
+ ExecutionContext context = this.repositoryContext.getExecutionContextFactory().create();
+ ClassLoader loader = context.getClassLoader(this.driverClassloaderName);
+ if (loader != null) {
+ Thread.currentThread().setContextClassLoader(loader);
+ }
+ } catch (Throwable t) {
+ I18n msg = JpaConnectorI18n.errorSettingContextClassLoader;
+ Logger.getLogger(getClass()).error(t, msg, name, driverClassloaderName);
+ }
+ }
+ // Set the connection properties ...
+ setProperty(configurator, "hibernate.dialect", this.dialect);
+ setProperty(configurator, "hibernate.connection.driver_class", this.driverClassName);
+ setProperty(configurator, "hibernate.connection.username", this.username);
+ setProperty(configurator, "hibernate.connection.password", this.password);
+ setProperty(configurator, "hibernate.connection.url", this.url);
+ setProperty(configurator, "hibernate.connection.max_fetch_depth", DEFAULT_MAXIMUM_FETCH_DEPTH);
+ setProperty(configurator, "hibernate.connection.pool_size", 0); // don't use the built-in pool
+ }
+
+ entityManagerFactory = configurator.buildEntityManagerFactory();
+
+ // Establish a connection and obtain the store options...
+ entityManager = entityManagerFactory.createEntityManager();
+
+ // Find and update/set the root node's UUID ...
+ StoreOptions options = new StoreOptions(entityManager);
+ UUID actualUuid = options.getRootNodeUuid();
+ if (actualUuid != null) this.setRootNodeUuid(actualUuid.toString());
+ else options.setRootNodeUuid(this.rootUuid);
+
+ // Find or set the type of model that will be used.
+ String actualModelName = options.getModelName();
+ if (actualModelName == null) {
+ // This is a new store, so set to the specified model ...
+ if (model == null) setModel(Models.DEFAULT.getName());
+ assert model != null;
+ options.setModelName(model);
+ } else {
+ try {
+ setModel(actualModelName);
+ } catch (Throwable e) {
+ // The actual model name doesn't match what's available in the software ...
+ entityManagerFactory.close();
+ String msg = JpaConnectorI18n.existingStoreSpecifiesUnknownModel.text(name, actualModelName);
+ throw new RepositorySourceException(msg);
+ }
+ }
+ entityManagerFactory.close();
+
+ // Now, create another entity manager with the classes from the correct model
+ model.configure(configurator);
+ entityManagerFactory = configurator.buildEntityManagerFactory();
+ entityManager = entityManagerFactory.createEntityManager();
+ }
+ if (entityManager == null) {
+ entityManager = entityManagerFactory.createEntityManager();
+ }
+ return new JpaConnection(getName(), cachePolicy, entityManager, model, rootUuid, largeValueSizeInBytes);
+ }
+
+ /**
+ * Set up the JPA configuration using Hibernate, except for the entity classes (which will already be configured when this
+ * method is called) and the data source or connection information (which will be set after this method returns). Subclasses
+ * may override this method to customize the configuration.
+ * <p>
+ * This method sets up the C3P0 connection pooling, the cache provider, and some DDL options.
+ * </p>
+ *
+ * @param configuration the Hibernate configuration; never null
+ */
+ protected void configure( Ejb3Configuration configuration ) {
+ // Set the connection pooling properties (to use C3P0) ...
+ setProperty(configuration, "hibernate.connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider");
+ setProperty(configuration, "hibernate.c3p0.max_size", this.maximumConnectionsInPool);
+ setProperty(configuration, "hibernate.c3p0.min_size", this.minimumConnectionsInPool);
+ setProperty(configuration, "hibernate.c3p0.timeout", this.idleTimeInSecondsBeforeTestingConnections);
+ setProperty(configuration, "hibernate.c3p0.max_statements", this.maximumSizeOfStatementCache);
+ setProperty(configuration, "hibernate.c3p0.idle_test_period", this.idleTimeInSecondsBeforeTestingConnections);
+ setProperty(configuration, "hibernate.c3p0.acquire_increment", this.numberOfConnectionsToAcquireAsNeeded);
+ setProperty(configuration, "hibernate.c3p0.validate", "false");
+
+ // Disable the second-level cache ...
+ setProperty(configuration, "hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
+
+ // Set up the schema and DDL options ...
+ // setProperty(configuration, "hibernate.show_sql", "true"); // writes all SQL statements to console
+ setProperty(configuration, "hibernate.format_sql", "true");
+ setProperty(configuration, "hibernate.use_sql_comments", "true");
+ setProperty(configuration, "hibernate.hbm2ddl.auto", "create");
+ }
+
+ /**
+ * Close any resources held by this source. This will ensure that all connections are closed.
+ */
+ public synchronized void close() {
+ if (entityManagerFactory != null) {
+ try {
+ entityManagerFactory.close();
+ } finally {
+ entityManagerFactory = null;
+ }
+ }
+ }
+
+ protected void setProperty( Ejb3Configuration configurator,
+ String propertyName,
+ String propertyValue ) {
+ assert configurator != null;
+ assert propertyName != null;
+ assert propertyName.trim().length() != 0;
+ if (propertyValue != null) {
+ configurator.setProperty(propertyName, propertyValue.trim());
+ }
+ }
+
+ protected void setProperty( Ejb3Configuration configurator,
+ String propertyName,
+ int propertyValue ) {
+ assert configurator != null;
+ assert propertyName != null;
+ assert propertyName.trim().length() != 0;
+ configurator.setProperty(propertyName, Integer.toString(propertyValue));
+ }
+
+ @ThreadSafe
+ protected class Capabilities extends RepositorySourceCapabilities {
+ private final AtomicBoolean supportsUpdates = new AtomicBoolean(DEFAULT_SUPPORTS_UPDATES);
+
+ /*package*/Capabilities() {
+ super(SUPPORTS_SAME_NAME_SIBLINGS, DEFAULT_SUPPORTS_UPDATES, SUPPORTS_EVENTS);
+ }
+
+ /*package*/void setSupportsUpdates( boolean supportsUpdates ) {
+ this.supportsUpdates.set(supportsUpdates);
+ }
+
+ @Override
+ public boolean supportsUpdates() {
+ return this.supportsUpdates.get();
+ }
+ }
+
+ @Immutable
+ /*package*/class JpaCachePolicy implements CachePolicy {
+ private static final long serialVersionUID = 1L;
+ private final int ttl;
+
+ /*package*/JpaCachePolicy( int ttl ) {
+ this.ttl = ttl;
+ }
+
+ public long getTimeToLive() {
+ return ttl;
+ }
+
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/Model.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import java.util.Locale;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * A descriptor of a schema used by this connector.
+ *
+ * @see JpaSource.Models
+ * @see JpaSource.Models#addModel(Model)
+ * @see JpaSource#setModel(String)
+ * @see JpaSource#getModel()
+ * @author Randall Hauch
+ */
+public abstract class Model {
+ private final String name;
+ private final I18n description;
+
+ protected Model( String name,
+ I18n description ) {
+ CheckArg.isNotEmpty(name, "name");
+ CheckArg.isNotNull(description, "description");
+ this.name = name;
+ this.description = description;
+ }
+
+ public final String getName() {
+ return this.name;
+ }
+
+ /**
+ * Get the description of this model in the default locale.
+ *
+ * @return the description of this model; never null
+ */
+ public String getDescription() {
+ return description.text();
+ }
+
+ /**
+ * Get the description of this model in the supplied locale.
+ *
+ * @param locale the locale in which the description is to be returned
+ * @return the description of this model; never null
+ */
+ public String getDescription( Locale locale ) {
+ return description.text(locale);
+ }
+
+ public abstract RequestProcessor createRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes );
+
+ /**
+ * Configure the entity class that will be used by JPA to store information in the database.
+ *
+ * @param configurator the Hibernate {@link Ejb3Configuration} component; never null
+ */
+ public abstract void configure( Ejb3Configuration configurator );
+
+ @Override
+ public final int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public final boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Model) {
+ Model that = (Model)obj;
+ if (this.getName().equals(that.getName())) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public final String toString() {
+ return name;
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModel.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.Model;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * Database model that stores node properties as opaque records and children as transparent records. Large property values are
+ * stored separately.
+ *
+ * @author Randall Hauch
+ */
+public class BasicModel extends Model {
+
+ public BasicModel() {
+ super("Basic", JpaConnectorI18n.basicModelDescription);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.Model#createRequestProcessor(java.lang.String, org.jboss.dna.graph.ExecutionContext,
+ * javax.persistence.EntityManager, java.util.UUID, long)
+ */
+ @Override
+ public RequestProcessor createRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes ) {
+ return new BasicRequestProcessor(sourceName, context, entityManager, rootNodeUuid, largeValueMinimumSizeInBytes);
+ }
+
+ /**
+ * Configure the entity class that will be used by JPA to store information in the database.
+ *
+ * @param configurator the Hibernate {@link Ejb3Configuration} component; never null
+ */
+ @Override
+ public void configure( Ejb3Configuration configurator ) {
+ // Add the annotated classes ...
+ configurator.addAnnotatedClass(NamespaceEntity.class);
+ configurator.addAnnotatedClass(NodeId.class);
+ configurator.addAnnotatedClass(PropertiesEntity.class);
+ configurator.addAnnotatedClass(LargeValueEntity.class);
+ configurator.addAnnotatedClass(ChildEntity.class);
+ configurator.addAnnotatedClass(ChildId.class);
+
+ // Set the cache information for each persistent class ...
+ // configurator.setProperty("hibernate.ejb.classcache." + KidpackNode.class.getName(), "read-write");
+ // configurator.setProperty("hibernate.ejb.collectioncache" + KidpackNode.class.getName() + ".distributors",
+ // "read-write, RegionName");
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,473 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityTransaction;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.util.Serializer;
+import org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.NameFactory;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.PathFactory;
+import org.jboss.dna.graph.properties.PathNotFoundException;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.properties.ValueFactories;
+import org.jboss.dna.graph.properties.ValueFactory;
+import org.jboss.dna.graph.requests.CopyBranchRequest;
+import org.jboss.dna.graph.requests.CreateNodeRequest;
+import org.jboss.dna.graph.requests.DeleteBranchRequest;
+import org.jboss.dna.graph.requests.MoveBranchRequest;
+import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
+import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.requests.UpdatePropertiesRequest;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+
+/**
+ * @author Randall Hauch
+ */
+public class BasicRequestProcessor extends RequestProcessor implements LargeValues {
+
+ private final EntityManager entities;
+ private final ValueFactory<String> stringFactory;
+ private final PathFactory pathFactory;
+ private final NameFactory nameFactory;
+ private final Namespaces namespaces;
+ private final UUID rootNodeUuid;
+ private final Serializer serializer;
+ private final long largeValueMinimumSizeInBytes;
+
+ /**
+ * @param sourceName
+ * @param context
+ * @param entityManager
+ * @param rootNodeUuid
+ * @param largeValueMinimumSizeInBytes
+ */
+ public BasicRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes ) {
+ super(sourceName, context);
+ assert entityManager != null;
+ assert rootNodeUuid != null;
+ this.entities = entityManager;
+ this.stringFactory = context.getValueFactories().getStringFactory();
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ this.nameFactory = context.getValueFactories().getNameFactory();
+ this.namespaces = new Namespaces(entityManager);
+ this.rootNodeUuid = rootNodeUuid;
+ this.serializer = new Serializer(context, this);
+ this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
+ // Start the transaction ...
+ this.entities.getTransaction().begin();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ // Location actualLocation = null;
+ // try {
+ // // Create nodes have to be defined via a path ...
+ // Location desiredLocation = request.at();
+ // Path desiredPath = desiredLocation.getPath();
+ // assert desiredPath != null;
+ //
+ // // // Get the parent
+ // // String parentUuidString = getUuidOf(parentLocation);
+ // // Location parentActualLocation = getActualLocation(parentLocation, parentUuidString);
+ // // Path parentPath = parentActualLocation.getPath();
+ // //
+ // // // Now see where the child is to be created ...
+ // // request.
+ //
+ // } catch (Throwable e) { // Includes PathNotFoundException
+ // request.setError(e);
+ // return;
+ // }
+ // if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllChildrenRequest)
+ */
+ @SuppressWarnings( "unchecked" )
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ Location actualLocation = null;
+ try {
+ Location location = request.of();
+ String parentUuidString = getUuidOf(location);
+ actualLocation = getActualLocation(location, parentUuidString);
+ Path path = actualLocation.getPath();
+
+ // Find the children of the supplied node ...
+ Query query = entities.createNamedQuery("ChildEntity.findAllUnderParent");
+ query.setParameter("uuid", parentUuidString);
+ List<ChildEntity> children = query.getResultList();
+ for (ChildEntity child : children) {
+ String namespaceUri = child.getChildNamespace().getUri();
+ String localName = child.getChildName();
+ Name childName = nameFactory.create(namespaceUri, localName);
+ Integer sns = child.getSameNameSiblingIndex();
+ if (sns == null) sns = new Integer(1);
+ Path childPath = pathFactory.create(path, childName, sns);
+ String childUuidString = child.getId().getChildUuidString();
+ Location childLocation = new Location(childPath, UUID.fromString(childUuidString));
+ request.addChild(childLocation);
+ }
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ Location actualLocation = null;
+ try {
+ Location location = request.at();
+ String uuidString = getUuidOf(location);
+ actualLocation = getActualLocation(location, uuidString);
+
+ // Find the properties entity for this node ...
+ Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
+ query.setParameter("uuid", uuidString);
+ PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
+
+ // Deserialize the properties ...
+ int propertyCount = entity.getPropertyCount();
+ Collection<Property> properties = new ArrayList<Property>(propertyCount);
+ byte[] data = entity.getData();
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ serializer.deserializeProperties(ois, properties);
+ for (Property property : properties) {
+ request.addProperty(property);
+ }
+ } catch (NoResultException e) {
+ // there are no properties (probably not expected, but still okay) ...
+ } catch (Throwable e) { // Includes PathNotFoundException
+ request.setError(e);
+ return;
+ }
+ if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#process(org.jboss.dna.graph.requests.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.requests.processor.RequestProcessor#close()
+ */
+ @Override
+ public void close() {
+ EntityTransaction txn = entities.getTransaction();
+ if (txn != null) txn.commit();
+ super.close();
+ }
+
+ protected Location getActualLocation( Location original,
+ String uuidString ) {
+ // If the original has a path and a UUID, it is complete already ...
+ Path path = original.getPath();
+ if (path != null) {
+ if (original.getIdProperty(DnaLexicon.UUID) != null) return original;
+ return original.with(UUID.fromString(uuidString));
+ }
+ // There is no path, so find it by UUID ...
+ path = getPathForUuid(uuidString);
+ return new Location(path, UUID.fromString(uuidString));
+ }
+
+ protected String getUuidOf( Location location ) throws PathNotFoundException {
+ String uuidString = null;
+ if (location.hasIdProperties()) {
+ // Look for the UUID ...
+ Property uuidProperty = location.getIdProperty(DnaLexicon.UUID);
+ if (uuidProperty != null && !uuidProperty.isEmpty()) {
+ uuidString = stringFactory.create(uuidProperty.iterator().next());
+ }
+ }
+ if (uuidString == null) {
+ // Look up the node by using the path to walk down the children, starting at the root ...
+ Path path = location.getPath();
+ if (path == null) {
+ // Location does not have path or DnaLexicon.UUID id property
+ }
+ assert path != null;
+ if (path.isRoot()) {
+ uuidString = rootNodeUuid.toString();
+ } else {
+ String parentUuid = this.rootNodeUuid.toString();
+ ChildEntity child = null;
+ for (Path.Segment segment : path) {
+ child = findByPathSegment(parentUuid, segment);
+ if (child == null) {
+ // Determine the lowest path that exists ...
+ Path lowest = path;
+ while (lowest.getLastSegment() != segment) {
+ lowest = lowest.getParent();
+ }
+ lowest = lowest.getParent();
+ throw new PathNotFoundException(location, lowest);
+ }
+ }
+ assert child != null;
+ uuidString = child.getId().getChildUuidString();
+ }
+ }
+ assert uuidString != null;
+ return uuidString;
+ }
+
+ /**
+ * Find the node with the supplied path segment that is a child of the supplied parent.
+ *
+ * @param parentUuid the UUID of the parent node, in string form
+ * @param pathSegment the path segment of the child
+ * @return the existing namespace, or null if one does not exist
+ * @throws IllegalArgumentException if the manager or URI are null
+ */
+ protected ChildEntity findByPathSegment( String parentUuid,
+ Path.Segment pathSegment ) {
+ assert namespaces != null;
+ assert parentUuid != null;
+ assert pathSegment != null;
+ Name name = pathSegment.getName();
+ String localName = name.getLocalName();
+ String nsUri = name.getNamespaceUri();
+ Integer nsId = namespaces.getId(nsUri, false);
+ if (nsId == null) {
+ // The namespace can't be found, then certainly the node won't be found ...
+ return null;
+ }
+ Query query = entities.createNamedQuery("ChildEntity.findByPathSegment");
+ query.setParameter("parentUuidString", parentUuid);
+ query.setParameter("ns", nsId);
+ query.setParameter("childName", localName);
+ if (pathSegment.hasIndex()) {
+ query.setParameter("sns", localName);
+ } else {
+ query.setParameter("sns", null);
+ }
+ try {
+ return (ChildEntity)query.getSingleResult();
+ } catch (NoResultException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Build up the path for the node with the supplied UUID.
+ *
+ * @param uuidString the UUID of the node
+ * @return the path to the node
+ */
+ protected Path getPathForUuid( String uuidString ) {
+ ChildEntity entity = null;
+ String childUuid = uuidString;
+ LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
+ do {
+ // Find the parent of the UUID ...
+ Query query = entities.createNamedQuery("ChildEntity.findParentByUuid");
+ query.setParameter("childUuidString", childUuid);
+ try {
+ entity = (ChildEntity)query.getSingleResult();
+ String localName = entity.getChildName();
+ String uri = entity.getChildNamespace().getUri();
+ Integer sns = entity.getSameNameSiblingIndex();
+ Name name = nameFactory.create(uri, localName);
+ if (sns != null) {
+ segments.addFirst(pathFactory.createSegment(name, sns));
+ } else {
+ segments.addFirst(pathFactory.createSegment(name));
+ }
+ } catch (NoResultException e) {
+ entity = null;
+ }
+ } while (entity != null);
+ return pathFactory.createAbsolutePath(segments);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return largeValueMinimumSizeInBytes;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ String hashStr = StringUtil.getHexString(hash);
+ LargeValueEntity entity = entities.find(LargeValueEntity.class, hashStr);
+ if (entity == null) {
+ throw new IOException(JpaConnectorI18n.unableToReadLargeValue.text(getSourceName(), hashStr));
+ }
+ byte[] data = entity.getData();
+ return valueFactories.getValueFactory(entity.getType()).create(data);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#write(byte[], long,
+ * org.jboss.dna.graph.properties.PropertyType, java.lang.Object)
+ */
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException {
+ if (value == null) return;
+ String hashStr = StringUtil.getHexString(hash);
+ LargeValueEntity entity = entities.find(LargeValueEntity.class, hashStr);
+ if (entity == null) {
+ entity = new LargeValueEntity();
+ entity.setCompressed(true);
+ entity.setHash(hashStr);
+ entity.setLength(length);
+ entity.setType(type);
+ ValueFactories factories = getExecutionContext().getValueFactories();
+ switch (type) {
+ case BINARY:
+ Binary binary = factories.getBinaryFactory().create(value);
+ try {
+ binary.acquire();
+ entity.setData(binary.getBytes());
+ } finally {
+ binary.release();
+ }
+ break;
+ default:
+ String str = factories.getStringFactory().create(value);
+ entity.setData(str.getBytes());
+ break;
+ }
+ entities.persist(entity);
+ } else {
+ // There is already an existing value, so we'll reuse it and increment the usage count ...
+ entity.incrementUsageCount();
+ }
+ }
+
+ protected static class Namespaces {
+
+ private final EntityManager entityManager;
+ private final Map<String, Integer> cache = new HashMap<String, Integer>();
+
+ public Namespaces( EntityManager manager ) {
+ this.entityManager = manager;
+ }
+
+ public Integer getId( String namespaceUri,
+ boolean createIfRequired ) {
+ Integer id = cache.get(namespaceUri);
+ if (id == null) {
+ NamespaceEntity entity = NamespaceEntity.findByUri(entityManager, namespaceUri, createIfRequired);
+ if (entity == null) return null;
+ id = entity.getId();
+ cache.put(namespaceUri, id);
+ }
+ assert id != null;
+ return id;
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildEntity.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,220 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import org.hibernate.annotations.Index;
+import org.hibernate.annotations.Table;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+
+/**
+ * An entity representing the parent-child relationship between two nodes. In addition to the references to the parent and child
+ * nodes, this entity also maintains the indexInParent of the indexInParent within the parent node's list of all children, the
+ * child's name ( {@link #getChildName() local part} and {@link #getChildNamespace() namespace}), and the same-name-sibiling
+ * indexInParent (if there is one).
+ *
+ * @author Randall Hauch
+ */
+@Entity
+(a)javax.persistence.Table( name = "DNA_BASIC_CHILDREN" )
+@Table( appliesTo = "DNA_BASIC_CHILDREN", indexes = @Index( name = "CHILDINDEX_INX", columnNames = {"PARENT_UUID", "CHILD_INDEX"} ) )
+@NamedQueries( {
+ @NamedQuery( name = "ChildEntity.findByPathSegment", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName AND child.sameNameSiblingIndex = :sns" ),
+ @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select child from ChildEntity as child where child.id.parentUuidString = :parentUuid" )} )
+public class ChildEntity {
+
+ @Id
+ private ChildId id;
+
+ @Column( name = "CHILD_INDEX", nullable = false, unique = false )
+ private Integer indexInParent;
+
+ @ManyToOne
+ @JoinColumn( name = "CHILD_NAME_NS_ID", nullable = false )
+ private NamespaceEntity childNamespace;
+
+ @Column( name = "CHILD_NAME_LOCAL", nullable = true, unique = false, length = 512 )
+ private String childName;
+
+ @Column( name = "SNS_INDEX", nullable = true, unique = false )
+ private Integer sameNameSiblingIndex;
+
+ public ChildEntity() {
+ }
+
+ public ChildEntity( ChildId id,
+ int indexInParent,
+ NamespaceEntity ns,
+ String name ) {
+ this.id = id;
+ this.indexInParent = indexInParent;
+ this.childNamespace = ns;
+ this.childName = name;
+ }
+
+ public ChildEntity( ChildId id,
+ int indexInParent,
+ NamespaceEntity ns,
+ String name,
+ int sameNameSiblingIndex ) {
+ this.id = id;
+ this.indexInParent = indexInParent;
+ this.childNamespace = ns;
+ this.childName = name;
+ this.sameNameSiblingIndex = sameNameSiblingIndex;
+ }
+
+ /**
+ * @return parent
+ */
+ public ChildId getId() {
+ return id;
+ }
+
+ /**
+ * @param childId Sets parent to the specified value.
+ */
+ public void setId( ChildId childId ) {
+ this.id = childId;
+ }
+
+ /**
+ * @return indexInParent
+ */
+ public Integer getIndexInParent() {
+ return indexInParent;
+ }
+
+ /**
+ * @param index Sets indexInParent to the specified value.
+ */
+ public void setIndexInParent( Integer index ) {
+ this.indexInParent = index;
+ }
+
+ /**
+ * @return childName
+ */
+ public String getChildName() {
+ return childName;
+ }
+
+ /**
+ * @param childName Sets childName to the specified value.
+ */
+ public void setChildName( String childName ) {
+ this.childName = childName;
+ }
+
+ /**
+ * @return childNamespace
+ */
+ public NamespaceEntity getChildNamespace() {
+ return childNamespace;
+ }
+
+ /**
+ * @param childNamespace Sets childNamespace to the specified value.
+ */
+ public void setChildNamespace( NamespaceEntity childNamespace ) {
+ this.childNamespace = childNamespace;
+ }
+
+ /**
+ * @return sameNameSiblingIndex
+ */
+ public Integer getSameNameSiblingIndex() {
+ return sameNameSiblingIndex;
+ }
+
+ /**
+ * @param sameNameSiblingIndex Sets sameNameSiblingIndex to the specified value.
+ */
+ public void setSameNameSiblingIndex( Integer sameNameSiblingIndex ) {
+ this.sameNameSiblingIndex = sameNameSiblingIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(id);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChildEntity) {
+ ChildEntity that = (ChildEntity)obj;
+ if (this.id == null) {
+ if (that.id != null) return false;
+ } else {
+ if (!this.id.equals(that.id)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (childNamespace != null) {
+ sb.append('{').append(childNamespace).append("}:");
+ }
+ sb.append(childName);
+ if (sameNameSiblingIndex != null && sameNameSiblingIndex.intValue() > 1) {
+ sb.append('[').append(sameNameSiblingIndex).append(']');
+ }
+ if (id != null) {
+ sb.append(" (id=").append(id.getChildUuidString()).append(")");
+ String parentId = id.getParentUuidString();
+ if (parentId != null) {
+ sb.append(" is child of ").append(parentId);
+ } else {
+ sb.append(" is root");
+ }
+ }
+ return sb.toString();
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/ChildId.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,175 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import java.io.Serializable;
+import java.util.UUID;
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+
+/**
+ * A unique identifer for a parent-child relationship.
+ *
+ * @author Randall Hauch
+ */
+@Embeddable
+public class ChildId implements Serializable {
+
+ /**
+ * Version {@value}
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Column( name = "PARENT_UUID", nullable = false )
+ private String parentUuidString;
+
+ @Column( name = "CHILD_UUID", nullable = false )
+ private String childUuidString;
+
+ private transient UUID parentUuid;
+ private transient UUID childUuid;
+
+ public ChildId() {
+ }
+
+ public ChildId( UUID parentUuid,
+ UUID childUuid ) {
+ setParentUuid(parentUuid);
+ setChildUuid(childUuid);
+ }
+
+ public ChildId( NodeId parentId,
+ NodeId childId ) {
+ if (parentId != null) setParentUuid(parentId.getUuid());
+ if (childId != null) setChildUuid(childId.getUuid());
+ }
+
+ public ChildId( String parentUuid,
+ String childUuid ) {
+ setParentUuidString(parentUuid);
+ setChildUuidString(childUuid);
+ }
+
+ public UUID getParentUuid() {
+ if (parentUuidString == null) return null;
+ if (parentUuid == null) {
+ parentUuid = UUID.fromString(parentUuidString);
+ }
+ return parentUuid;
+ }
+
+ public void setParentUuid( UUID uuid ) {
+ this.parentUuid = uuid;
+ this.parentUuidString = uuid != null ? uuid.toString() : null;
+ }
+
+ public UUID getChildUuid() {
+ if (childUuidString == null) return null;
+ if (childUuid == null) {
+ childUuid = UUID.fromString(childUuidString);
+ }
+ return childUuid;
+ }
+
+ public void setChildUuid( UUID uuid ) {
+ this.childUuid = uuid;
+ this.childUuidString = uuid != null ? uuid.toString() : null;
+ }
+
+ /**
+ * @return parentUuidString
+ */
+ public String getParentUuidString() {
+ return parentUuidString;
+ }
+
+ /**
+ * @param parentUuidString Sets parentUuidString to the specified value.
+ */
+ public void setParentUuidString( String parentUuidString ) {
+ this.parentUuid = null;
+ this.parentUuidString = parentUuidString;
+ }
+
+ /**
+ * @return childUuidString
+ */
+ public String getChildUuidString() {
+ return childUuidString;
+ }
+
+ /**
+ * @param childUuidString Sets childUuidString to the specified value.
+ */
+ public void setChildUuidString( String childUuidString ) {
+ this.childUuid = null;
+ this.childUuidString = childUuidString;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(parentUuidString, childUuidString);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof ChildId) {
+ ChildId that = (ChildId)obj;
+ if (this.parentUuidString == null) {
+ if (that.parentUuidString != null) return false;
+ } else {
+ if (!this.parentUuidString.equals(that.parentUuidString)) return false;
+ }
+ if (this.childUuidString == null) {
+ if (that.childUuidString != null) return false;
+ } else {
+ if (!this.childUuidString.equals(that.childUuidString)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Child " + childUuidString + " of " + parentUuidString;
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,204 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import org.jboss.dna.graph.properties.PropertyType;
+
+/**
+ * A single property value that is too large to be stored on the individual node.
+ *
+ * @author Randall Hauch
+ */
+@Entity( name = "DNA_BASIC_LARGE_VALUES" )
+public class LargeValueEntity {
+
+ /**
+ * The 160-bit SHA-1 hash of this value, in hex form (40-bytes). The SHA-1 algorithm is fast and has not yet proven to have
+ * any duplicates. Even if SHA-2 and SHA-3 are better for cryptographically secure purposes, it is doubtful whether a
+ * repository needs more than SHA-1.
+ */
+ @Id
+ @Column( name = "SHA1", nullable = false, unique = true, length = 40 )
+ private String hash;
+
+ /**
+ * The property type for this value. Typically, this is {@link PropertyType#STRING} or {@link PropertyType#BINARY}, although
+ * technically it could be any type.
+ */
+ @Enumerated( value = EnumType.STRING )
+ @Column( name = "TYPE", nullable = false )
+ private PropertyType type;
+
+ /**
+ * The number of bytes in this value.
+ */
+ @Column( name = "LENGTH", nullable = false )
+ private long length;
+
+ /**
+ * The number of times this value is used. If this value drops below 1, the value could be removed from the store.
+ */
+ @Column( name = "USAGE_COUNT", nullable = false )
+ private int usageCount = 1;
+
+ /**
+ * Flag specifying whether the binary data is stored in a compressed format.
+ */
+ @Column( name = "COMPRESSED", nullable = true )
+ private Boolean compressed;
+
+ /**
+ * Lazily-fetched value
+ */
+ @Lob
+ @Column( name = "DATA", nullable = false )
+ private byte[] data;
+
+ /**
+ * @return hash
+ */
+ public String getHash() {
+ return hash;
+ }
+
+ /**
+ * @param hash Sets hash to the specified value.
+ */
+ public void setHash( String hash ) {
+ this.hash = hash;
+ }
+
+ /**
+ * @return length
+ */
+ public long getLength() {
+ return length;
+ }
+
+ /**
+ * @param length Sets length to the specified value.
+ */
+ public void setLength( long length ) {
+ this.length = length;
+ }
+
+ /**
+ * @return type
+ */
+ public PropertyType getType() {
+ return type;
+ }
+
+ /**
+ * @param type Sets type to the specified value.
+ */
+ public void setType( PropertyType type ) {
+ this.type = type;
+ }
+
+ /**
+ * @return data
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * @param data Sets data to the specified value.
+ */
+ public void setData( byte[] data ) {
+ this.data = data;
+ }
+
+ /**
+ * @return usageCount
+ */
+ public int getUsageCount() {
+ return usageCount;
+ }
+
+ /**
+ * @param usageCount Sets usageCount to the specified value.
+ */
+ public void setUsageCount( int usageCount ) {
+ this.usageCount = usageCount;
+ }
+
+ public void incrementUsageCount() {
+ this.usageCount++;
+ }
+
+ /**
+ * @return compressed
+ */
+ public boolean isCompressed() {
+ return compressed != null && compressed.booleanValue();
+ }
+
+ /**
+ * @param compressed Sets compressed to the specified value.
+ */
+ public void setCompressed( boolean compressed ) {
+ this.compressed = Boolean.valueOf(compressed);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getHash().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof LargeValueEntity) {
+ LargeValueEntity that = (LargeValueEntity)obj;
+ if (this.getHash().equals(that.getHash())) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Large " + this.type + " value (hash=" + this.hash + ",compressed=" + isCompressed() + ")";
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/PropertiesEntity.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,167 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import javax.persistence.Column;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+import org.jboss.dna.connector.store.jpa.util.Serializer;
+
+/**
+ * Represents the packed properties of a single node. Node that the object has the node's identifier and the packed properties,
+ * but nothing else. The PropertiesEntity doesn't even have the name. This is because this class is used to read, modify, and save
+ * the properties of a node. Finding a node by its name or working with the children, however, requires working with the
+ * {@link ChildEntity node children}.
+ *
+ * @author Randall Hauch
+ */
+@Entity
+@Table( name = "DNA_BASIC_NODEPROPS" )
+@NamedQueries( {@NamedQuery( name = "PropertiesEntity.findByUuid", query = "select prop from PropertiesEntity as prop where prop.id.uuidString = :uuid" )} )
+public class PropertiesEntity {
+ @EmbeddedId
+ private NodeId id;
+
+ @Lob
+ @Column( name = "DATA", nullable = false, unique = false )
+ private byte[] data;
+
+ @Column( name = "NUM_PROPS", nullable = false )
+ private int propertyCount;
+
+ /**
+ * Flag specifying whether the binary data is stored in a compressed format.
+ */
+ @Column( name = "COMPRESSED", nullable = true )
+ private Boolean compressed;
+
+ public PropertiesEntity() {
+ }
+
+ public PropertiesEntity( NodeId id ) {
+ setId(id);
+ }
+
+ /**
+ * Get the node's identifier.
+ *
+ * @return the node's identifier
+ */
+ public NodeId getId() {
+ return id;
+ }
+
+ /**
+ * Set the node's identifier.
+ *
+ * @param id the new identifier for the node
+ */
+ public void setId( NodeId id ) {
+ this.id = id;
+ }
+
+ /**
+ * Get the data that represents the {@link Serializer packed} properties.
+ *
+ * @return the raw data representing the properties
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Set the data that represents the {@link Serializer packed} properties.
+ *
+ * @param data the raw data representing the properties
+ */
+ public void setData( byte[] data ) {
+ this.data = data;
+ }
+
+ /**
+ * @return propertyCount
+ */
+ public int getPropertyCount() {
+ return propertyCount;
+ }
+
+ /**
+ * @param propertyCount Sets propertyCount to the specified value.
+ */
+ public void setPropertyCount( int propertyCount ) {
+ this.propertyCount = propertyCount;
+ }
+
+ /**
+ * @return compressed
+ */
+ public boolean isCompressed() {
+ return compressed != null && compressed.booleanValue();
+ }
+
+ /**
+ * @param compressed Sets compressed to the specified value.
+ */
+ public void setCompressed( boolean compressed ) {
+ this.compressed = Boolean.valueOf(compressed);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PropertiesEntity) {
+ PropertiesEntity that = (PropertiesEntity)obj;
+ if (this.getId().equals(that.getId())) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Properties for " + this.id;
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NamespaceEntity.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,170 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.common;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.NoResultException;
+import javax.persistence.Query;
+import org.jboss.dna.common.util.CheckArg;
+
+/**
+ * A NamespaceEntity represents a namespace that has been used in the store. NamespaceEntity records are immutable and shared by
+ * one or more enities.
+ *
+ * @author Randall Hauch
+ */
+@Entity( name = "DNA_NAMESPACES" )
+@NamedQueries( {@NamedQuery( name = "NamespaceEntity.findAll", query = "SELECT ns FROM DNA_NAMESPACES AS ns" ),
+ @NamedQuery( name = "NamespaceEntity.findByUri", query = "SELECT ns FROM DNA_NAMESPACES AS ns WHERE ns.uri = ?1" )} )
+public class NamespaceEntity {
+
+ @Id
+ @GeneratedValue( strategy = GenerationType.AUTO )
+ private Integer id;
+
+ @Column( name = "URI", nullable = false, unique = false, length = 512, updatable = false )
+ private String uri;
+
+ /**
+ *
+ */
+ public NamespaceEntity() {
+ }
+
+ /**
+ * @param uri the namespace URI
+ */
+ public NamespaceEntity( String uri ) {
+ setUri(uri);
+ }
+
+ /**
+ * @return id
+ */
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @param id Sets id to the specified value.
+ */
+ public void setId( Integer id ) {
+ this.id = id;
+ }
+
+ /**
+ * @return uri
+ */
+ public String getUri() {
+ return uri;
+ }
+
+ /**
+ * @param uri Sets uri to the specified value.
+ */
+ public void setUri( String uri ) {
+ this.uri = uri;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NamespaceEntity) {
+ NamespaceEntity that = (NamespaceEntity)obj;
+ if (!this.id.equals(that.id)) return false;
+ if (!this.uri.equals(that.uri)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return uri;
+ }
+
+ /**
+ * Find an existing namespace by its URI, or create and return one if it does not already exist.
+ *
+ * @param manager the entity manager
+ * @param uri the URI
+ * @return the existing namespace, or null if one does not exist
+ * @throws IllegalArgumentException if the manager or URI are null
+ */
+ public static NamespaceEntity findByUri( EntityManager manager,
+ String uri ) {
+ return findByUri(manager, uri, true);
+ }
+
+ /**
+ * Find an existing namespace by its URI.
+ *
+ * @param manager the entity manager
+ * @param uri the URI
+ * @param createIfRequired if the namespace should be persisted if it does not yet exist
+ * @return the existing namespace, or null if one does not exist
+ * @throws IllegalArgumentException if the manager or URI are null
+ */
+ public static NamespaceEntity findByUri( EntityManager manager,
+ String uri,
+ boolean createIfRequired ) {
+ CheckArg.isNotNull(manager, "manager");
+ CheckArg.isNotNull(uri, "uri");
+ Query query = manager.createNamedQuery("NamespaceEntity.findByUri");
+ query.setParameter(1, uri);
+ try {
+ return (NamespaceEntity)query.getSingleResult();
+ } catch (NoResultException e) {
+ if (!createIfRequired) return null;
+ NamespaceEntity namespace = new NamespaceEntity(uri);
+ manager.persist(namespace);
+ return namespace;
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/common/NodeId.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,123 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.common;
+
+import java.io.Serializable;
+import java.util.UUID;
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+
+/**
+ * An identifier for a node, comprised of a single {@link UUID}, and {@link Embeddable embeddable} in a persistent entity. The
+ * identifier takes the form of two <code>long</code> columns: one for the UUID's {@link UUID#getMostSignificantBits() most
+ * significant bits} and one for its {@link UUID#getLeastSignificantBits() least significant bits}.
+ *
+ * @author Randall Hauch
+ */
+@Embeddable
+public class NodeId implements Serializable {
+
+ /**
+ * Version {@value}
+ */
+ private static final long serialVersionUID = 1L;
+
+ @Column( name = "UUID", nullable = true )
+ private String uuidString;
+
+ private transient UUID uuid;
+
+ public NodeId() {
+ }
+
+ public NodeId( UUID uuid ) {
+ setUuid(uuid);
+ }
+
+ public UUID getUuid() {
+ if (uuid == null) {
+ // No need to synchronize, since it is idempotent ...
+ uuid = UUID.fromString(uuidString);
+ }
+ return uuid;
+ }
+
+ public void setUuid( UUID uuid ) {
+ assert uuid != null;
+ this.uuid = uuid;
+ this.uuidString = uuid.toString();
+ }
+
+ /**
+ * @return uuidString
+ */
+ public String getUuidString() {
+ return uuidString;
+ }
+
+ /**
+ * @param uuidString Sets uuidString to the specified value.
+ */
+ public void setUuidString( String uuidString ) {
+ this.uuidString = uuidString;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getUuid().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof NodeId) {
+ NodeId that = (NodeId)obj;
+ if (this.uuidString == null) {
+ if (that.uuidString != null) return false;
+ } else {
+ if (!this.uuidString.equals(that.uuidString)) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getUuid().toString();
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Namespaces.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.persistence.EntityManager;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+
+/**
+ * @author Randall Hauch
+ */
+public class Namespaces {
+
+ private final EntityManager entityManager;
+ private final Map<String, Integer> cache = new HashMap<String, Integer>();
+
+ public Namespaces( EntityManager manager ) {
+ this.entityManager = manager;
+ }
+
+ public int getId( String namespaceUri ) {
+ Integer id = cache.get(namespaceUri);
+ if (id == null) {
+ NamespaceEntity entity = NamespaceEntity.findByUri(entityManager, namespaceUri, true);
+ id = entity.getId();
+ cache.put(namespaceUri, id);
+ }
+ assert id != null;
+ return id;
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/Serializer.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,415 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.math.BigDecimal;
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collection;
+import java.util.UUID;
+import org.jboss.dna.common.SystemFailureException;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.connector.store.jpa.models.basic.LargeValueEntity;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.DateTime;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.PropertyFactory;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.properties.Reference;
+import org.jboss.dna.graph.properties.ValueFactories;
+
+/**
+ * @author Randall Hauch
+ */
+public class Serializer {
+
+ private final PropertyFactory propertyFactory;
+ private final ValueFactories valueFactories;
+ private final LargeValues largeValues;
+
+ public Serializer( ExecutionContext context,
+ LargeValues largeValues ) {
+ this.propertyFactory = context.getPropertyFactory();
+ this.valueFactories = context.getValueFactories();
+ this.largeValues = largeValues;
+ }
+
+ /**
+ * Interface that represents the location where "large" objects are stored.
+ *
+ * @author Randall Hauch
+ */
+ public interface LargeValues {
+ /**
+ * Get the minimum size for large values, specified as {@link String#length() number of characters} for a {@link String}
+ * or the {@link Binary#getSize() number of bytes for a binary value}
+ *
+ * @return the size at which a property value is considered to be <i>large</i>
+ */
+ long getMinimumSize();
+
+ void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException;
+
+ Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException;
+ }
+
+ /**
+ * Serialize the properties' values to the object stream.
+ * <p>
+ * If any of the property values are considered {@link LargeValues#getMinimumSize() large}, the value's hash and length of the
+ * property value will be written to the object stream, but the property value will be sent to the supplied
+ * {@link LargeValueEntity} object.
+ * </p>
+ * <p>
+ * This method does not automatically write each property value to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)}, but instead serializes the primitive values that make up the property value
+ * object with a code that describes the {@link PropertyType property's type}. This is more efficient, since most of the
+ * property values are really non-primitive objects, and writing to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)} would include larger class metadata.
+ * </p>
+ *
+ * @param stream the stream where the properties' values are to be serialized; may not be null
+ * @param number the number of properties exposed by the supplied <code>properties</code> iterator; must be 0 or positive
+ * @param properties the iterator over the properties that are to be serialized; may not be null
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @see #deserializeProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property)
+ */
+ public void serializeProperties( ObjectOutputStream stream,
+ int number,
+ Iterable<Property> properties ) throws IOException {
+ assert number >= 0;
+ assert properties != null;
+ stream.writeInt(number);
+ for (Property property : properties) {
+ if (property == null) continue;
+ serializeProperty(stream, property);
+ }
+ }
+
+ /**
+ * Serialize the property's values to the object stream.
+ * <p>
+ * If any of the property values are considered {@link LargeValues#getMinimumSize() large}, the value's hash and length of the
+ * property value will be written to the object stream, but the property value will be sent to the supplied
+ * {@link LargeValueEntity} object.
+ * </p>
+ * <p>
+ * This method does not automatically write each property value to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)}, but instead serializes the primitive values that make up the property value
+ * object with a code that describes the {@link PropertyType property's type}. This is more efficient, since most of the
+ * property values are really non-primitive objects, and writing to the stream using
+ * {@link ObjectOutputStream#writeObject(Object)} would include larger class metadata.
+ * </p>
+ *
+ * @param stream the stream where the property's values are to be serialized; may not be null
+ * @param property the property to be serialized; may not be null
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable)
+ * @see #deserializeProperty(ObjectInputStream)
+ */
+ public void serializeProperty( ObjectOutputStream stream,
+ Property property ) throws IOException {
+ assert stream != null;
+ assert property != null;
+ // Write the name ...
+ stream.writeObject(property.getName().getString());
+ // Write the number of values ...
+ stream.writeInt(property.size());
+ for (Object value : property) {
+ if (value instanceof String) {
+ String stringValue = (String)value;
+ if (largeValues != null && stringValue.length() > largeValues.getMinimumSize()) {
+ // Store the value in the large values area, but record the hash and length here.
+ byte[] hash = computeHash(stringValue);
+ stream.writeChar('L');
+ stream.writeInt(hash.length);
+ stream.write(hash);
+ stream.writeLong(stringValue.length());
+ // Now write to the large objects ...
+ largeValues.write(computeHash(stringValue), stringValue.length(), PropertyType.STRING, stringValue);
+ } else {
+ stream.writeChar('S');
+ stream.writeObject(stringValue);
+ }
+ } else if (value instanceof Boolean) {
+ stream.writeChar('b');
+ stream.writeBoolean(((Boolean)value).booleanValue());
+ } else if (value instanceof Long) {
+ stream.writeChar('l');
+ stream.writeLong(((Long)value).longValue());
+ } else if (value instanceof Double) {
+ stream.writeChar('d');
+ stream.writeDouble(((Double)value).doubleValue());
+ } else if (value instanceof Integer) {
+ stream.writeChar('i');
+ stream.writeInt(((Integer)value).intValue());
+ } else if (value instanceof Short) {
+ stream.writeChar('s');
+ stream.writeShort(((Short)value).shortValue());
+ } else if (value instanceof Float) {
+ stream.writeChar('f');
+ stream.writeFloat(((Float)value).floatValue());
+ } else if (value instanceof UUID) {
+ stream.writeChar('U');
+ UUID uuid = (UUID)value;
+ stream.writeLong(uuid.getMostSignificantBits());
+ stream.writeLong(uuid.getLeastSignificantBits());
+ } else if (value instanceof URI) {
+ stream.writeChar('I');
+ stream.writeObject(((URI)value).toString());
+ } else if (value instanceof Name) {
+ stream.writeChar('N');
+ stream.writeObject(((Name)value).getString());
+ } else if (value instanceof Path) {
+ stream.writeChar('P');
+ stream.writeObject(((Path)value).getString());
+ } else if (value instanceof DateTime) {
+ stream.writeChar('T');
+ stream.writeObject(((DateTime)value).getString());
+ } else if (value instanceof BigDecimal) {
+ stream.writeChar('D');
+ stream.writeObject(value);
+ } else if (value instanceof Character) {
+ stream.writeChar('c');
+ char c = ((Character)value).charValue();
+ stream.writeChar(c);
+ } else if (value instanceof Reference) {
+ stream.writeChar('R');
+ stream.writeObject(((Reference)value).getString());
+ } else if (value instanceof Binary) {
+ Binary binary = (Binary)value;
+ byte[] hash = null;
+ long length = 0;
+ try {
+ binary.acquire();
+ length = binary.getSize();
+ if (largeValues != null && length > largeValues.getMinimumSize()) {
+ // Store the value in the large values area, but record the hash and length here.
+ hash = binary.getHash();
+ stream.writeChar('L');
+ stream.writeInt(hash.length);
+ stream.write(hash);
+ stream.writeLong(length);
+ // Write to large objects after releasing the binary
+ } else {
+ // The value is small enough to store here ...
+ stream.writeChar('B');
+ stream.writeLong(length);
+ InputStream data = binary.getStream();
+ try {
+ byte[] buffer = new byte[1024];
+ int numRead = 0;
+ while ((numRead = data.read(buffer)) > -1) {
+ stream.write(buffer, 0, numRead);
+ }
+ } finally {
+ data.close();
+ }
+ }
+ } finally {
+ binary.release();
+ }
+ // If this is a large value and the binary has been released, write it to the large objects ...
+ if (largeValues != null && hash != null) {
+ largeValues.write(hash, length, PropertyType.BINARY, value);
+ }
+ } else {
+ // Other kinds of values ...
+ stream.writeChar('O');
+ stream.writeObject(value);
+ }
+ }
+ stream.flush();
+ }
+
+ /**
+ * Deserialize the serialized properties on the supplied object stream.
+ *
+ * @param stream the stream that contains the serialized properties; may not be null
+ * @param properties the collection into which each deserialized property is to be placed; may not be null
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not be found
+ * @see #deserializeProperty(ObjectInputStream)
+ * @see #serializeProperties(ObjectOutputStream, int, Iterable)
+ */
+ public void deserializeProperties( ObjectInputStream stream,
+ Collection<Property> properties ) throws IOException, ClassNotFoundException {
+ assert propertyFactory != null;
+ assert valueFactories != null;
+ assert stream != null;
+ assert properties != null;
+ assert largeValues != null;
+ // Read the number of properties ...
+ int count = stream.readInt();
+ for (int i = 0; i != count; ++i) {
+ Property property = deserializeProperty(stream);
+ assert property != null;
+ properties.add(property);
+ }
+ }
+
+ /**
+ * Deserialize the serialized property on the supplied object stream.
+ *
+ * @param stream the stream that contains the serialized properties; may not be null
+ * @return the deserialized property; never null
+ * @throws IOException if there is an error writing to the <code>stream</code> or <code>largeValues</code>
+ * @throws ClassNotFoundException if the class for the value's object could not be found
+ * @see #deserializeProperties(ObjectInputStream, Collection)
+ * @see #serializeProperty(ObjectOutputStream, Property)
+ */
+ public Property deserializeProperty( ObjectInputStream stream ) throws IOException, ClassNotFoundException {
+ assert propertyFactory != null;
+ assert valueFactories != null;
+ assert stream != null;
+ assert largeValues != null;
+ // Read the name ...
+ String nameStr = (String)stream.readObject();
+ Name name = valueFactories.getNameFactory().create(nameStr);
+ assert name != null;
+ // Read the number of values ...
+ int size = stream.readInt();
+ Object[] values = new Object[size];
+ for (int i = 0; i != size; ++i) {
+ Object value = null;
+ // Read the type of value ...
+ char type = stream.readChar();
+ switch (type) {
+ case 'S':
+ // String
+ value = valueFactories.getStringFactory().create((String)stream.readObject());
+ break;
+ case 'b':
+ // boolean
+ value = valueFactories.getBooleanFactory().create(stream.readBoolean());
+ break;
+ case 'i':
+ // integer
+ value = valueFactories.getLongFactory().create(stream.readInt());
+ break;
+ case 'l':
+ // long
+ value = valueFactories.getLongFactory().create(stream.readLong());
+ break;
+ case 's':
+ // short
+ value = valueFactories.getLongFactory().create(stream.readShort());
+ break;
+ case 'f':
+ // float
+ value = valueFactories.getDoubleFactory().create(stream.readFloat());
+ break;
+ case 'd':
+ // double
+ value = valueFactories.getDoubleFactory().create(stream.readDouble());
+ break;
+ case 'c':
+ // double
+ value = valueFactories.getStringFactory().create("" + stream.readChar());
+ break;
+ case 'U':
+ // UUID
+ long msb = stream.readLong();
+ long lsb = stream.readLong();
+ UUID uuid = new UUID(msb, lsb);
+ value = valueFactories.getUuidFactory().create(uuid);
+ break;
+ case 'I':
+ // URI
+ String uriStr = (String)stream.readObject();
+ value = valueFactories.getUriFactory().create(uriStr);
+ break;
+ case 'N':
+ // Name
+ String nameValueStr = (String)stream.readObject();
+ value = valueFactories.getNameFactory().create(nameValueStr);
+ break;
+ case 'P':
+ // Path
+ String pathStr = (String)stream.readObject();
+ value = valueFactories.getPathFactory().create(pathStr);
+ break;
+ case 'T':
+ // DateTime
+ String dateTimeStr = (String)stream.readObject();
+ value = valueFactories.getDateFactory().create(dateTimeStr);
+ break;
+ case 'D':
+ // BigDecimal
+ Object bigDecimal = stream.readObject();
+ value = valueFactories.getDecimalFactory().create(bigDecimal);
+ break;
+ case 'R':
+ // Reference
+ value = valueFactories.getReferenceFactory().create((String)stream.readObject());
+ break;
+ case 'B':
+ // Binary
+ // Read the length of the content ...
+ long binaryLength = stream.readLong();
+ value = valueFactories.getBinaryFactory().create(stream, binaryLength);
+ break;
+ case 'L':
+ // Large object ...
+ // Read the hash ...
+ int hashLength = stream.readInt();
+ byte[] hash = new byte[hashLength];
+ stream.read(hash);
+ // Read the length of the content ...
+ long length = stream.readLong();
+ value = largeValues.read(valueFactories, hash, length);
+ break;
+ default:
+ // All other objects ...
+ Object object = stream.readObject();
+ value = valueFactories.getObjectFactory().create(object);
+ break;
+ }
+ assert value != null;
+ values[i] = value;
+ }
+ // Add the property to the collection ...
+ return propertyFactory.create(name, values);
+ }
+
+ public byte[] computeHash( String value ) {
+ try {
+ return SecureHash.getHash(SecureHash.Algorithm.SHA_1, value.getBytes());
+ } catch (NoSuchAlgorithmException e) {
+ throw new SystemFailureException(e);
+ }
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptionEntity.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,143 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.connector.store.jpa.JpaSource;
+import org.jboss.dna.connector.store.jpa.Model;
+
+/**
+ * An option for the store. This is typically used to save store-specific values.
+ * <p>
+ * This JPA entity is always added to the {@link Ejb3Configuration} in the {@link JpaSource#getConnection() JpaSource}, and
+ * therefore should not be {@link Model#configure(Ejb3Configuration) added to the configuration} by a {@link Model}.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+@Entity( name = "DNA_OPTIONS" )
+@NamedQueries( {@NamedQuery( name = "StoreOptionEntity.findAll", query = "SELECT option FROM DNA_OPTIONS AS option" )} )
+public class StoreOptionEntity {
+
+ @Id
+ @Column( name = "NAME", nullable = false, unique = true, length = 512 )
+ private String name;
+
+ @Column( name = "VALUE", nullable = false, unique = false, length = 512 )
+ private String value;
+
+ /**
+ *
+ */
+ protected StoreOptionEntity() {
+ }
+
+ /**
+ * @param name the name of the option; may not be null or empty
+ * @param value the value of the option; may be null
+ */
+ public StoreOptionEntity( String name,
+ String value ) {
+ CheckArg.isNotEmpty(name, "name");
+ setName(name);
+ setValue(value);
+ }
+
+ /**
+ * @param name the name of the option; may not be null or empty
+ */
+ public StoreOptionEntity( String name ) {
+ CheckArg.isNotEmpty(name, "name");
+ setName(name);
+ }
+
+ /**
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public void setName( String name ) {
+ this.name = name;
+ }
+
+ /**
+ * @return value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * @param value Sets value to the specified value.
+ */
+ public void setValue( String value ) {
+ this.value = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof StoreOptionEntity) {
+ StoreOptionEntity that = (StoreOptionEntity)obj;
+ if (!this.getName().equals(that.getName())) return false;
+ if (!this.getValue().equals(that.getValue())) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Option " + getName() + " = \"" + getValue() + "\"";
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/util/StoreOptions.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,103 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.connector.store.jpa.Model;
+
+/**
+ * @author Randall Hauch
+ */
+public class StoreOptions {
+
+ public static final String ROOT_NODE_UUID = "org.jboss.dna.store.rootNodeUuid";
+ public static final String VERSION = "org.jboss.dna.store.version";
+ public static final String MODEL = "org.jboss.dna.store.model";
+
+ private final EntityManager entityManager;
+
+ public StoreOptions( EntityManager manager ) {
+ CheckArg.isNotNull(manager, "manager");
+ this.entityManager = manager;
+ }
+
+ public UUID getRootNodeUuid() {
+ String value = getOption(ROOT_NODE_UUID);
+ return value != null ? UUID.fromString(value) : null;
+ }
+
+ public void setRootNodeUuid( UUID uuid ) {
+ CheckArg.isNotNull(uuid, "uuid");
+ setOption(ROOT_NODE_UUID, uuid.toString());
+ }
+
+ public String getVersion() {
+ return getOption(VERSION);
+ }
+
+ public void setVersion( String version ) {
+ setOption(VERSION, version);
+ }
+
+ public String getModelName() {
+ return getOption(MODEL);
+ }
+
+ public void setModelName( Model model ) {
+ String modelName = model != null ? model.getName() : null;
+ setOption(MODEL, modelName);
+ }
+
+ public String getOption( String name ) {
+ StoreOptionEntity entity = entityManager.find(StoreOptionEntity.class, name);
+ return entity != null ? entity.getValue() : null;
+ }
+
+ public void setOption( String name,
+ String value ) {
+ CheckArg.isNotEmpty(name, "name");
+ if (value != null) value = value.trim();
+ StoreOptionEntity entity = entityManager.find(StoreOptionEntity.class, name);
+ if (entity == null) {
+ if (value != null) {
+ // There is no existing entity, but there is a valid value ...
+ entity = new StoreOptionEntity(name, value);
+ entityManager.persist(entity);
+ }
+ } else {
+ if (value != null) {
+ // Set value on the entity ...
+ entity.setValue(value);
+ } else {
+ // The existing entity is to be removed ...
+ entityManager.remove(entity);
+ }
+ }
+ }
+
+ public void removeOption( String name ) {
+ StoreOptionEntity entity = new StoreOptionEntity(name);
+ entityManager.remove(entity);
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/main/resources/org/jboss/dna/connector/store/jpa/JpaConnectorI18n.properties 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,33 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2008, Red Hat Middleware LLC, and individual contributors
+# as indicated by the @author tags. See the copyright.txt file in the
+# distribution for a full listing of individual contributors.
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of
+# the License, or (at your option) any later version.
+#
+# This software is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+#
+
+connectorName = JPA Graph Store Connector
+nodeDoesNotExist = Could not find an existing node at {0}
+propertyIsRequired = The {0} property is required but has no value
+errorFindingDataSourceInJndi = Repository source {0} unable to find DataSource in JNDI at {1}
+repositorySourceMustHaveName = The JPA Graph Store repository source must have a name
+unknownModelName = The model name "{0}" is unknown; expected one of {1}
+errorSettingContextClassLoader = Error while setting the current context class loader for JAP repository source {0} to classloader name {1}
+existingStoreSpecifiesUnknownModel = The JPA repository source {0} uses a model that is not known: {1}
+unableToReadLargeValue = Unable to read from {0} the large property with hash = {1}
+
+basicModelDescription = Database model that stores node properties as opaque records and children as transparent records. Large property values are stored separately.
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import static org.mockito.Mockito.mock;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.connector.store.jpa.models.basic.BasicModel;
+import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnectionTest {
+
+ private ExecutionContext context;
+ private JpaConnection connection;
+ private EntityManagerFactory factory;
+ private EntityManager manager;
+ private Model model;
+ private CachePolicy cachePolicy;
+ private UUID rootNodeUuid;
+ private long largeValueSize;
+
+ @Before
+ public void beforeEach() throws Exception {
+ context = new BasicExecutionContext();
+ model = new BasicModel();
+ rootNodeUuid = UUID.randomUUID();
+ largeValueSize = 2 ^ 10; // 1 kilobyte
+
+ // Connect to the database ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+ model.configure(configurator);
+ configurator.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username", "sa");
+ configurator.setProperty("hibernate.connection.password", "");
+ configurator.setProperty("hibernate.connection.url", "jdbc:hsqldb:.");
+ configurator.setProperty("hibernate.show_sql", "true");
+ configurator.setProperty("hibernate.format_sql", "true");
+ configurator.setProperty("hibernate.use_sql_comments", "true");
+ configurator.setProperty("hibernate.hbm2ddl.auto", "create");
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+
+ // Create the connection ...
+ cachePolicy = mock(CachePolicy.class);
+ connection = new JpaConnection("source", cachePolicy, manager, model, rootNodeUuid, largeValueSize);
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ try {
+ if (manager != null) manager.close();
+ } finally {
+ manager = null;
+ if (factory != null) {
+ try {
+ factory.close();
+ } finally {
+ factory = null;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void shouldAlwaysReadRootNode() {
+
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorI18nTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import org.jboss.dna.common.AbstractI18nTest;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnectorI18nTest extends AbstractI18nTest {
+
+ public JpaConnectorI18nTest() {
+ super(JpaConnectorI18n.class);
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaSourceTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,111 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaSourceTest {
+
+ private JpaSource source;
+ private JpaConnection connection;
+
+ @Before
+ public void beforeEach() throws Exception {
+ this.source = new JpaSource();
+ // Set the connection properties to be an in-memory HSQL database ...
+ this.source.setName("Test Repository");
+ this.source.setDialect("org.hibernate.dialect.HSQLDialect");
+ this.source.setDriverClassName("org.hsqldb.jdbcDriver");
+ this.source.setUsername("sa");
+ this.source.setPassword("");
+ this.source.setUrl("jdbc:hsqldb:.");
+ this.source.setMaximumConnectionsInPool(3);
+ this.source.setMinimumConnectionsInPool(0);
+ this.source.setNumberOfConnectionsToAcquireAsNeeded(1);
+ this.source.setMaximumSizeOfStatementCache(100);
+ this.source.setMaximumConnectionIdleTimeInSeconds(0);
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ try {
+ if (this.connection != null) {
+ this.connection.close();
+ }
+ } finally {
+ this.source.close();
+ }
+ }
+
+ @Test( expected = RepositorySourceException.class )
+ public void shouldFailToCreateConnectionIfSourceHasNoName() {
+ source.setName(null);
+ source.getConnection();
+ }
+
+ @Test
+ public void shouldHaveNoDefaultModelUponConstruction() {
+ assertThat(source.getModel(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldCreateConnection() throws Exception {
+ connection = (JpaConnection)source.getConnection();
+ assertThat(connection, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldAllowMultipleConnectionsToBeOpenAtTheSameTime() throws Exception {
+ List<RepositoryConnection> connections = new ArrayList<RepositoryConnection>();
+ try {
+ for (int i = 0; i != 10; ++i) {
+ RepositoryConnection conn = source.getConnection();
+ assertThat(conn, is(notNullValue()));
+ connections.add(conn);
+ }
+ } finally {
+ // Close all open connections ...
+ for (RepositoryConnection conn : connections) {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/ModelTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,114 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class ModelTest {
+
+ private String validName;
+ private I18n validDescription;
+ private I18n validDescription2;
+ private Model model1;
+ private Model model2;
+ private Model model3;
+ @Mock
+ private RequestProcessor requestProcessor;
+
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ validName = "Concrete Model";
+ validDescription = JpaConnectorI18n.basicModelDescription;
+ validDescription2 = JpaConnectorI18n.connectorName;
+ model1 = new ConcreteModel(validName, validDescription);
+ model2 = new ConcreteModel(validName, validDescription2);
+ model3 = new ConcreteModel(validName + " ", validDescription);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowNullNameInConstructor() {
+ new ConcreteModel(null, validDescription);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowEmptyNameInConstructor() {
+ new ConcreteModel("", validDescription);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldNotAllowNullDescriptionInConstructor() {
+ new ConcreteModel(validName, null);
+ }
+
+ @Test
+ public void shouldNotTrimName() {
+ assertThat(model3.getName(), is(validName + " "));
+ }
+
+ @Test
+ public void shouldConsiderTwoModelsEqualIfTheyHaveTheSameName() {
+ assertThat(model1, is(model2));
+ }
+
+ @Test
+ public void shouldConsiderTwoModelsNotEqualIfTheyHaveDifferentNames() {
+ assertThat(model1, is(not(model3)));
+ assertThat(model2, is(not(model3)));
+ }
+
+ protected class ConcreteModel extends Model {
+ protected ConcreteModel( String name,
+ I18n description ) {
+ super(name, description);
+ }
+
+ @Override
+ public void configure( Ejb3Configuration configurator ) {
+ }
+
+ @SuppressWarnings( "synthetic-access" )
+ @Override
+ public RequestProcessor createRequestProcessor( String sourceName,
+ ExecutionContext context,
+ EntityManager entityManager,
+ UUID rootNodeUuid,
+ long largeValueMinimumSizeInBytes ) {
+ return requestProcessor;
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/models/basic/BasicModelTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,354 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.models.basic;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.connector.store.jpa.JpaConnectorI18n;
+import org.jboss.dna.connector.store.jpa.models.common.NamespaceEntity;
+import org.jboss.dna.connector.store.jpa.models.common.NodeId;
+import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * This test not only verifies the (minimal) functionality of the {@link BasicModel} class, but it also verifies that the entity
+ * classes used by the {@link BasicModel#configure(Ejb3Configuration) configuration} are consistent and error-free. In other
+ * words, if there are any problems with any of the entity annotations, they will be found when the {@link EntityManager} is
+ * {@link #startEntityManager() started}.
+ *
+ * @author Randall Hauch
+ */
+public class BasicModelTest {
+
+ private EntityManagerFactory factory;
+ private EntityManager manager;
+ private BasicModel model;
+ private ExecutionContext context;
+
+ @BeforeClass
+ public static void beforeAll() throws Exception {
+ }
+
+ @Before
+ public void beforeEach() throws Exception {
+ model = new BasicModel();
+ context = new BasicExecutionContext();
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ try {
+ if (manager != null) manager.close();
+ } finally {
+ manager = null;
+ if (factory != null) {
+ try {
+ factory.close();
+ } finally {
+ factory = null;
+ }
+ }
+ }
+ }
+
+ protected EntityManager startEntityManager() {
+ if (manager == null) {
+ // Connect to the database ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+ model.configure(configurator);
+ configurator.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username", "sa");
+ configurator.setProperty("hibernate.connection.password", "");
+ configurator.setProperty("hibernate.connection.url", "jdbc:hsqldb:.");
+ configurator.setProperty("hibernate.show_sql", "true");
+ configurator.setProperty("hibernate.format_sql", "true");
+ configurator.setProperty("hibernate.use_sql_comments", "true");
+ configurator.setProperty("hibernate.hbm2ddl.auto", "create");
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+ }
+ return manager;
+ }
+
+ @Test
+ public void shouldHaveName() {
+ assertThat(model.getName(), is("Basic"));
+ }
+
+ @Test
+ public void shouldHaveDescription() {
+ assertThat(model.getDescription(), is(JpaConnectorI18n.basicModelDescription.text()));
+ assertThat(model.getDescription(Locale.US), is(JpaConnectorI18n.basicModelDescription.text()));
+ }
+
+ @Test
+ public void shouldCreateRequestProcessor() {
+ EntityManager manager = mock(EntityManager.class);
+ EntityTransaction txn = mock(EntityTransaction.class);
+ stub(manager.getTransaction()).toReturn(txn);
+ RequestProcessor proc = model.createRequestProcessor("test source", context, manager, UUID.randomUUID(), 100);
+ assertThat(proc, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldPersistPropertyEntityWithCompressedFlagAndNoChildren() {
+ startEntityManager();
+ NodeId nodeId = new NodeId(UUID.randomUUID());
+ PropertiesEntity prop = new PropertiesEntity();
+ prop.setCompressed(true);
+ prop.setData("Hello, World".getBytes());
+ prop.setId(nodeId);
+ manager.getTransaction().begin();
+ try {
+ // Save a properties entity (with compressed data) ...
+ manager.persist(prop);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ PropertiesEntity prop2 = manager.find(PropertiesEntity.class, nodeId);
+ assertThat(prop2.isCompressed(), is(prop.isCompressed()));
+ assertThat(prop2.getId(), is(prop.getId()));
+ assertThat(prop2.getData(), is(prop.getData()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistPropertyEntityWithUncompressedFlagAndNoChildren() {
+ startEntityManager();
+ NodeId nodeId = new NodeId(UUID.randomUUID());
+ PropertiesEntity prop = new PropertiesEntity();
+ prop.setData("Hello, World".getBytes());
+ prop.setId(nodeId);
+ manager.getTransaction().begin();
+ try {
+ // Save a properties entity (with compressed data) ...
+ manager.persist(prop);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ PropertiesEntity prop2 = manager.find(PropertiesEntity.class, nodeId);
+ assertThat(prop2.isCompressed(), is(prop.isCompressed()));
+ assertThat(prop2.getId(), is(prop.getId()));
+ assertThat(prop2.getData(), is(prop.getData()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistLargeValueEntityWithCompressedFlag() throws UnsupportedEncodingException, NoSuchAlgorithmException {
+ startEntityManager();
+ byte[] content = "Jack and Jill went up the hill to grab a pail of water.".getBytes();
+ String hash = StringUtil.getHexString(SecureHash.getHash(SecureHash.Algorithm.SHA_1, content));
+ LargeValueEntity entity = new LargeValueEntity();
+ entity.setCompressed(true);
+ entity.setHash(hash);
+ entity.setLength(content.length);
+ entity.setData(content);
+ entity.setType(PropertyType.STRING);
+ manager.getTransaction().begin();
+ try {
+ // Save the entity ...
+ manager.persist(entity);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ LargeValueEntity entity2 = manager.find(LargeValueEntity.class, hash);
+ assertThat(entity2.isCompressed(), is(entity.isCompressed()));
+ assertThat(entity2.getHash(), is(entity.getHash()));
+ assertThat(entity2.getData(), is(entity.getData()));
+ assertThat(entity2.getLength(), is(entity.getLength()));
+ assertThat(entity2.getType(), is(entity.getType()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistLargeValueEntityWithUncompressedFlag() throws UnsupportedEncodingException, NoSuchAlgorithmException {
+ startEntityManager();
+ byte[] content = "Jack and Jill went up the hill to grab a pail of water.".getBytes();
+ String hash = StringUtil.getHexString(SecureHash.getHash(SecureHash.Algorithm.SHA_1, content));
+ LargeValueEntity entity = new LargeValueEntity();
+ // entity.setCompressed(false);
+ entity.setHash(hash);
+ entity.setLength(content.length);
+ entity.setData(content);
+ entity.setType(PropertyType.STRING);
+ manager.getTransaction().begin();
+ try {
+ // Save the entity ...
+ manager.persist(entity);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ LargeValueEntity entity2 = manager.find(LargeValueEntity.class, hash);
+ assertThat(entity2.isCompressed(), is(entity.isCompressed()));
+ assertThat(entity2.getHash(), is(entity.getHash()));
+ assertThat(entity2.getData(), is(entity.getData()));
+ assertThat(entity2.getLength(), is(entity.getLength()));
+ assertThat(entity2.getType(), is(entity.getType()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistNamespaceEntity() {
+ startEntityManager();
+ String uri = "http://www.example.com";
+ NamespaceEntity namespace = new NamespaceEntity(uri);
+ manager.getTransaction().begin();
+ try {
+ // Save a namespace entity ...
+ manager.persist(namespace);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns2 = manager.find(NamespaceEntity.class, namespace.getId());
+ assertThat(ns2.getUri(), is(namespace.getUri()));
+ assertThat(ns2.getId(), is(namespace.getId()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ // Look up by namespace ...
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns2 = NamespaceEntity.findByUri(manager, uri);
+ assertThat(ns2.getUri(), is(namespace.getUri()));
+ assertThat(ns2.getId(), is(namespace.getId()));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+
+ @Test
+ public void shouldPersistChildEntity() {
+ startEntityManager();
+ UUID parentId = UUID.randomUUID();
+
+ // Create UUIDs for several children ...
+ ChildId childId1 = new ChildId(parentId, UUID.randomUUID());
+ ChildId childId2 = new ChildId(parentId, UUID.randomUUID());
+ ChildId childId3 = new ChildId(parentId, UUID.randomUUID());
+ assertThat(childId1, is(not(childId2)));
+ assertThat(childId1, is(not(childId3)));
+ assertThat(childId2, is(not(childId3)));
+
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns = NamespaceEntity.findByUri(manager, "http://www.example.com");
+
+ // Create the child entities ...
+ ChildEntity child1 = new ChildEntity(childId1, 1, ns, "child1");
+ ChildEntity child2 = new ChildEntity(childId2, 2, ns, "child2");
+ ChildEntity child3 = new ChildEntity(childId3, 3, ns, "child3", 1);
+
+ // Save a properties entities ...
+ manager.persist(child1);
+ manager.persist(child2);
+ manager.persist(child3);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ // manager.getTransaction().rollback();
+ throw t;
+ }
+ // Look up the object ...
+ manager.getTransaction().begin();
+ try {
+ NamespaceEntity ns = NamespaceEntity.findByUri(manager, "http://www.example.com");
+
+ ChildEntity child1a = manager.find(ChildEntity.class, childId1);
+ ChildEntity child2a = manager.find(ChildEntity.class, childId2);
+ ChildEntity child3a = manager.find(ChildEntity.class, childId3);
+
+ assertThat(child1a.getId(), is(childId1));
+ assertThat(child1a.getIndexInParent(), is(1));
+ assertThat(child1a.getChildName(), is("child1"));
+ assertThat(child1a.getChildNamespace(), is(ns));
+ assertThat(child1a.getSameNameSiblingIndex(), is(nullValue()));
+
+ assertThat(child2a.getId(), is(childId2));
+ assertThat(child2a.getIndexInParent(), is(2));
+ assertThat(child2a.getChildName(), is("child2"));
+ assertThat(child2a.getChildNamespace(), is(ns));
+ assertThat(child2a.getSameNameSiblingIndex(), is(nullValue()));
+
+ assertThat(child3a.getId(), is(childId3));
+ assertThat(child3a.getIndexInParent(), is(3));
+ assertThat(child3a.getChildName(), is("child3"));
+ assertThat(child3a.getChildNamespace(), is(ns));
+ assertThat(child3a.getSameNameSiblingIndex(), is(1));
+ } finally {
+ manager.getTransaction().rollback();
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/SerializerTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,378 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.SecureHash;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.BasicExecutionContext;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.properties.Binary;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.PropertyFactory;
+import org.jboss.dna.graph.properties.PropertyType;
+import org.jboss.dna.graph.properties.ValueFactories;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class SerializerTest {
+
+ private Serializer serializer;
+ private ExecutionContext context;
+ private LargeValuesHolder largeValues;
+ private PropertyFactory propertyFactory;
+ private ValueFactories valueFactories;
+
+ @Before
+ public void beforeEach() {
+ context = new BasicExecutionContext();
+ propertyFactory = context.getPropertyFactory();
+ valueFactories = context.getValueFactories();
+ largeValues = new LargeValuesHolder();
+ serializer = new Serializer(context, largeValues);
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeLongProperty() throws Exception {
+ Property prop = createProperty("p1", new Long(1));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeIntegerProperty() throws Exception {
+ Property prop = createProperty("p1", new Integer(1));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeShortProperty() throws Exception {
+ Property prop = createProperty("p1", new Short((short)1));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeFloatProperty() throws Exception {
+ Property prop = createProperty("p1", new Float(1.0f));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeDoubleProperty() throws Exception {
+ Property prop = createProperty("p1", new Double(1.0d));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeBooleanProperty() throws Exception {
+ Property prop = createProperty("p1", new Boolean(true));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeNameProperty() throws Exception {
+ Property prop = createProperty("p1", valueFactories.getNameFactory().create("something"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializePathProperty() throws Exception {
+ Property prop = createProperty("p1", valueFactories.getPathFactory().create("/a/b/c/something"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeDateTimeProperty() throws Exception {
+ Property prop = createProperty("p1", valueFactories.getDateFactory().createUtc());
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+
+ prop = createProperty("p1", valueFactories.getDateFactory().create());
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeUuidProperty() throws Exception {
+ Property prop = createProperty("p1", UUID.randomUUID());
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeUriProperty() throws Exception {
+ Property prop = createProperty("p1", new URI("http://example.com"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeReferenceProperty() throws Exception {
+ UUID uuid = UUID.randomUUID();
+ Property prop = createProperty("p1", valueFactories.getReferenceFactory().create(uuid.toString()));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeBigDecimalProperty() throws Exception {
+ Property prop = createProperty("p1", valueFactories.getDecimalFactory().create("1.0123455243284347375478525485466895512"));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeSmallBinaryProperty() throws Exception {
+ String value = "v1";
+ Property prop = createProperty("p1", valueFactories.getBinaryFactory().create(value));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeLargeBinaryProperty() throws Exception {
+ String value = "really really long string that will be converted to a binary value and tested like that";
+ Property prop = createProperty("p1", valueFactories.getBinaryFactory().create(value));
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(1));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeSmallStringProperty() throws Exception {
+ Property prop = createProperty("p1", "v1");
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(0));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeLargeStringProperty() throws Exception {
+ String value = "v234567890123456789012345678901234567890";
+ Property prop = createProperty("p1", value);
+ assertSerializableAndDeserializable(serializer, prop);
+ assertThat(largeValues.getCount(), is(1));
+ assertThat(largeValues.get(value).value, is((Object)value));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeSmallAndLargeStringProperty() throws Exception {
+ Property prop1 = createProperty("p1", "v1");
+ String value = "v234567890123456789012345678901234567890";
+ Property prop2 = createProperty("p1", value);
+ Property prop3 = createProperty("p1", "v2");
+ Property prop4 = createProperty("p1", new String(value)); // make sure it's a different String object
+
+ assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4);
+ assertThat(largeValues.getCount(), is(1));
+ }
+
+ @Test
+ public void shouldSerializeAndDeserializeMixtureOfSmallAndLargeProperties() throws Exception {
+ Property prop1 = createProperty("p1", "v1");
+ String value = "v234567890123456789012345678901234567890";
+ Property prop2 = createProperty("p1", value);
+ Property prop3 = createProperty("p1", "v2");
+ Property prop4 = createProperty("p1", new String(value)); // make sure it's a different String object
+ Property prop5 = createProperty("p1", valueFactories.getBinaryFactory().create("something"));
+ String binaryValue = "really really long string that will be converted to a binary value and tested like that";
+ Property prop6 = createProperty("p1", valueFactories.getBinaryFactory().create(binaryValue));
+
+ assertSerializableAndDeserializable(serializer, prop1, prop2, prop3, prop4, prop5, prop6);
+ assertThat(largeValues.getCount(), is(2));
+ }
+
+ protected Property createProperty( String name,
+ Object... values ) {
+ return propertyFactory.create(valueFactories.getNameFactory().create(name), values);
+ }
+
+ protected void assertSerializableAndDeserializable( Serializer serializer,
+ Property... properties ) throws IOException, ClassNotFoundException {
+ for (Property property : properties) {
+ // Serialize the properties one at a time ...
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ try {
+ serializer.serializeProperty(oos, property);
+ } finally {
+ oos.close();
+ }
+ byte[] bytes = baos.toByteArray();
+
+ // Deserialize ...
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ Property copy = null;
+ try {
+ copy = serializer.deserializeProperty(ois);
+ } finally {
+ ois.close();
+ }
+ // Check the property ...
+ assertThat(copy, is(property));
+ }
+
+ // Now serialize and deserialize the list of properties ...
+ List<Property> propertyList = Arrays.asList(properties);
+ List<Property> outputProperties = new ArrayList<Property>(propertyList.size());
+
+ // Serialize the properties one at a time ...
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ try {
+ serializer.serializeProperties(oos, propertyList.size(), propertyList);
+ } finally {
+ oos.close();
+ }
+ byte[] bytes = baos.toByteArray();
+
+ // Deserialize ...
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ try {
+ serializer.deserializeProperties(ois, outputProperties);
+ } finally {
+ ois.close();
+ }
+
+ // Check the properties match ...
+ assertThat(outputProperties.size(), is(propertyList.size()));
+ assertThat(outputProperties, hasItems(propertyList.toArray(new Property[propertyList.size()])));
+ }
+
+ protected class LargeValuesHolder implements Serializer.LargeValues {
+ private int minimumSize = 20;
+ private final Map<String, LargeValue> largeValuesByHexHash = new HashMap<String, LargeValue>();
+
+ public LargeValuesHolder() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#getMinimumSize()
+ */
+ public long getMinimumSize() {
+ return minimumSize;
+ }
+
+ /**
+ * @param minimumSize Sets minimumSize to the specified value.
+ */
+ public void setMinimumSize( int minimumSize ) {
+ CheckArg.isPositive(minimumSize, "minimumSize");
+ this.minimumSize = minimumSize;
+ }
+
+ public int getCount() {
+ return this.largeValuesByHexHash.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#read(org.jboss.dna.graph.properties.ValueFactories,
+ * byte[], long)
+ */
+ public Object read( ValueFactories valueFactories,
+ byte[] hash,
+ long length ) throws IOException {
+ LargeValue largeValue = get(hash);
+ return largeValue != null ? largeValue.value : null;
+ }
+
+ public LargeValue get( String obj ) throws IOException, NoSuchAlgorithmException {
+ byte[] hash = SecureHash.getHash(SecureHash.Algorithm.SHA_1, obj.getBytes());
+ return get(hash);
+ }
+
+ public LargeValue get( Binary obj ) throws IOException {
+ return get(obj.getHash());
+ }
+
+ public LargeValue get( byte[] hash ) throws IOException {
+ String hexHash = StringUtil.getHexString(hash);
+ return largeValuesByHexHash.get(hexHash);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.connector.store.jpa.util.Serializer.LargeValues#write(byte[], long,
+ * org.jboss.dna.graph.properties.PropertyType, java.lang.Object)
+ */
+ public void write( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) throws IOException {
+ String hexHash = StringUtil.getHexString(hash);
+ largeValuesByHexHash.put(hexHash, new LargeValue(hash, length, type, value));
+ }
+
+ protected class LargeValue {
+ protected final byte[] hash;
+ protected final long length;
+ protected final PropertyType type;
+ protected final Object value;
+
+ protected LargeValue( byte[] hash,
+ long length,
+ PropertyType type,
+ Object value ) {
+ assert hash != null;
+ assert length > 0;
+ assert type != null;
+ assert value != null;
+ this.hash = hash;
+ this.length = length;
+ this.type = type;
+ this.value = value;
+ }
+ }
+ }
+
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/util/StoreOptionsTest.java 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,212 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.connector.store.jpa.util;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.List;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Query;
+import org.hibernate.ejb.Ejb3Configuration;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class StoreOptionsTest {
+
+ protected static EntityManagerFactory factory;
+ protected static EntityManager manager;
+ protected StoreOptions options;
+
+ @BeforeClass
+ public static void beforeAll() throws Exception {
+ // Connect to the database ...
+ Ejb3Configuration configurator = new Ejb3Configuration();
+ configurator.addAnnotatedClass(StoreOptionEntity.class);
+ configurator.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
+ configurator.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
+ configurator.setProperty("hibernate.connection.username", "sa");
+ configurator.setProperty("hibernate.connection.password", "");
+ configurator.setProperty("hibernate.connection.url", "jdbc:hsqldb:.");
+ configurator.setProperty("hibernate.show_sql", "false");
+ configurator.setProperty("hibernate.format_sql", "true");
+ configurator.setProperty("hibernate.use_sql_comments", "true");
+ configurator.setProperty("hibernate.hbm2ddl.auto", "create");
+ factory = configurator.buildEntityManagerFactory();
+ manager = factory.createEntityManager();
+ }
+
+ @Before
+ public void beforeEach() throws Exception {
+ removeAllOptionEntities();
+ options = new StoreOptions(manager);
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ removeAllOptionEntities();
+ }
+
+ @AfterClass
+ public static void afterAll() throws Exception {
+ try {
+ manager.close();
+ } finally {
+ factory.close();
+ }
+ }
+
+ protected void removeAllOptionEntities() {
+ try {
+ manager.getTransaction().begin();
+ List<StoreOptionEntity> optionEntities = getAllOptionEntities();
+ for (StoreOptionEntity entity : optionEntities) {
+ manager.remove(entity);
+ }
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ @SuppressWarnings( "unchecked" )
+ protected List<StoreOptionEntity> getAllOptionEntities() {
+ Query query = manager.createNamedQuery("StoreOptionEntity.findAll");
+ return query.getResultList();
+ }
+
+ protected void assertNoOptions() {
+ try {
+ manager.getTransaction().begin();
+ assertThat(getAllOptionEntities().isEmpty(), is(true));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ protected void assertOption( String name,
+ String expectedValue ) {
+ try {
+ manager.getTransaction().begin();
+ String actualValue = options.getOption(name);
+ assertThat(expectedValue, is(actualValue));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ //
+ // protected StoreOptionEntity getOption( String name ) {
+ // return options.readStoreOption(name);
+ // }
+ //
+ protected void setOptionInTxn( String name,
+ String value ) {
+ try {
+ manager.getTransaction().begin();
+ options.setOption(name, value);
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ assertOption(name, value);
+ }
+
+ @Test
+ public void shouldReturnNullForNonExistantOption() {
+ assertNoOptions();
+ assertThat(options.getOption("non-existant name"), is(nullValue()));
+ }
+
+ @Test
+ public void shouldReturnValueForExistingOption() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ @Test
+ public void shouldSetValueOnExistingOption() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ options.setOption("name1", "value2");
+ assertThat(options.getOption("name1"), is("value2"));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+
+ assertOption("name1", "value2");
+ }
+
+ @Test
+ public void shouldRemoveOptionWhenSetToNullValue() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ options.setOption("name1", null);
+ assertThat(options.getOption("name1"), is(nullValue()));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+
+ @Test
+ public void shouldNotRemoveOptionWhenSetToEmptyValue() {
+ setOptionInTxn("name1", "value1");
+ try {
+ manager.getTransaction().begin();
+ assertThat(options.getOption("name1"), is("value1"));
+ options.setOption("name1", "");
+ assertThat(options.getOption("name1"), is(""));
+ manager.getTransaction().commit();
+ } catch (RuntimeException t) {
+ manager.getTransaction().rollback();
+ throw t;
+ }
+ }
+}
Added: trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties (rev 0)
+++ trunk/extensions/dna-connector-store-jpa/src/test/resources/log4j.properties 2008-11-19 19:34:05 UTC (rev 641)
@@ -0,0 +1,19 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Set up the default logging to be INFO level, then override specific units
+log4j.logger.org.jboss.dna=INFO
+# Hibernate
+log4j.logger.org.hibernate=ERROR
+# C3P0
+log4j.logger.com.mchange=ERROR
+
+# JBoss Cache logging
+log4j.logger.org.jboss.cache=WARN, stdout
+
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2008-11-18 13:00:30 UTC (rev 640)
+++ trunk/pom.xml 2008-11-19 19:34:05 UTC (rev 641)
@@ -129,6 +129,7 @@
<module>extensions/dna-connector-inmemory</module>
<module>extensions/dna-connector-jbosscache</module>
<module>extensions/dna-connector-svn</module>
+ <module>extensions/dna-connector-store-jpa</module>
<module>extensions/dna-mimetype-detector-aperture</module>
<module>dna-integration-tests</module>
<module>docs/examples/gettingstarted</module>
17 years, 1 month