You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by re...@apache.org on 2015/03/23 17:38:19 UTC
[35/51] [partial] incubator-taverna-engine git commit:
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceServiceImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceServiceImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceServiceImpl.java
new file mode 100644
index 0000000..30620bd
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceServiceImpl.java
@@ -0,0 +1,730 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import static java.lang.Float.MAX_VALUE;
+import static org.apache.taverna.reference.T2ReferenceType.ErrorDocument;
+import static org.apache.taverna.reference.T2ReferenceType.IdentifiedList;
+import static org.apache.taverna.reference.T2ReferenceType.ReferenceSet;
+import static org.apache.taverna.reference.impl.T2ReferenceImpl.getAsImpl;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.taverna.reference.ContextualizedT2Reference;
+import org.apache.taverna.reference.ErrorDocument;
+import org.apache.taverna.reference.ErrorDocumentServiceException;
+import org.apache.taverna.reference.ExternalReferenceSPI;
+import org.apache.taverna.reference.Identified;
+import org.apache.taverna.reference.IdentifiedList;
+import org.apache.taverna.reference.ListServiceException;
+import org.apache.taverna.reference.ReferenceContext;
+import org.apache.taverna.reference.ReferenceService;
+import org.apache.taverna.reference.ReferenceServiceException;
+import org.apache.taverna.reference.ReferenceSet;
+import org.apache.taverna.reference.ReferenceSetServiceException;
+import org.apache.taverna.reference.StreamToValueConverterSPI;
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.ValueCarryingExternalReference;
+import org.apache.taverna.reference.ValueToReferenceConversionException;
+import org.apache.taverna.reference.ValueToReferenceConverterSPI;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Implementation of ReferenceService, inject with ReferenceSetService,
+ * ErrorDocumentService and ListService to enable.
+ *
+ * @author Tom Oinn
+ * @author Alan R Williams
+ * @author Stuart Owen
+ * @author Stian Soiland-Reyes
+ */
+public class ReferenceServiceImpl extends AbstractReferenceServiceImpl
+ implements ReferenceService {
+ private final Logger log = Logger.getLogger(ReferenceServiceImpl.class);
+
+ /**
+ * The top level registration method is used to register either as yet
+ * unregistered ErrorDocuments and ReferenceSets (if these are passed in and
+ * already have an identifier this call does nothing) and arbitrarily nested
+ * Lists of the same. In addition any ExternalReferenceSPI instances found
+ * will be wrapped in a single item ReferenceSet and registered, and any
+ * Throwables will be wrapped in an ErrorDocument and registered. Lists will
+ * be converted to IdentifiedList<T2Reference> and registered if all
+ * children can be (or were already) appropriately named.
+ * <p>
+ * This method is only valid on parameters of the following type :
+ * <ol>
+ * <li>{@link ReferenceSet} - registered if not already registered,
+ * otherwise returns existing T2Reference</li>
+ * <li>{@link ErrorDocument} - same behaviour as ReferenceSet</li>
+ * <li>{@link ExternalReferenceSPI} - wrapped in ReferenceSet, registered
+ * and ID returned</li>
+ * <li>Throwable - wrapped in ErrorDocument with no message, registered and
+ * ID returned</li>
+ * <li>List - all children are first registered, if this succeeds the list
+ * is itself registered as an IdentifiedList of T2Reference and its
+ * reference returned.</li>
+ * </ol>
+ * The exception to this is if the useConvertorSPI parameter is set to true
+ * - in this case any objects which do not match the above allowed list will
+ * be run through any available ValueToReferenceConvertorSPI instances in
+ * turn until one succeeds or all fail, which may result in the creation of
+ * ExternalReferenceSPI instances. As these can be registered such objects
+ * will not cause an exception to be thrown.
+ *
+ * @param o
+ * the object to register with the reference system, must comply
+ * with and will be interpreted as shown in the type list above.
+ * @param targetDepth
+ * the depth of the top level object supplied. This is needed
+ * when registering empty collections and error documents,
+ * whether as top level types or as members of a collection
+ * within the top level type. If registering a collection this is
+ * the collection depth, so a List of ReferenceSchemeSPI would be
+ * depth 1. Failing to specify this correctly will result in
+ * serious problems downstream so be careful! We can't catch all
+ * potential problems in this method (although some errors will
+ * be trapped).
+ * @param useConverterSPI
+ * whether to attempt to use the ValueToReferenceConvertorSPI
+ * registry (if defined and available) to map arbitrary objects
+ * to ExternalReferenceSPI instances on the fly. The registry of
+ * converters is generally injected into the implementation of
+ * this service.
+ * @param context
+ * ReferenceContext to use if required by component services,
+ * this is most likely to be used by the object to reference
+ * converters if engaged.
+ * <p>
+ * If the context is null a new empty reference context is
+ * inserted.
+ * @return a T2Reference to the registered object
+ * @throws ReferenceServiceException
+ * if the object type (or, for collections, the recursive type
+ * of its contents) is not in the allowed list or if a problem
+ * occurs during registration. Also thrown if attempting to use
+ * the converter SPI without an attached registry.
+ */
+ @Override
+ public T2Reference register(Object o, int targetDepth,
+ boolean useConverterSPI, ReferenceContext context)
+ throws ReferenceServiceException {
+ checkServices();
+ if (context == null)
+ context = new EmptyReferenceContext();
+ if (useConverterSPI)
+ checkConverterRegistry();
+ return getNameForObject(o, targetDepth, useConverterSPI, context);
+ }
+
+ @SuppressWarnings("unchecked")
+ private T2Reference getNameForObject(Object o, int currentDepth,
+ boolean useConverterSPI, ReferenceContext context)
+ throws ReferenceServiceException {
+ if (currentDepth < 0)
+ throw new ReferenceServiceException("Cannot register at depth "
+ + currentDepth + ": " + o);
+ /*
+ * First check whether this is an Identified, and if so whether it
+ * already has an ID. If this is the case then return it, we assume that
+ * anything which has an identifier already allocated must have been
+ * registered (this is implicit in the contract for the various
+ * sub-services
+ */
+ if (o instanceof Identified) {
+ Identified i = (Identified) o;
+ if (i.getId() != null)
+ return i.getId();
+ }
+ /*
+ * Then check whether the item *is* a T2Reference, in which case we can
+ * just return it (useful for when registering lists of existing
+ * references)
+ */
+ if (o instanceof T2Reference)
+ return (T2Reference) o;
+
+ if (o.getClass().isArray()) {
+ Class<?> elementType = o.getClass().getComponentType();
+ if (elementType.getCanonicalName().equals("char")) {
+ char[] cArray = (char[]) o;
+ List<Character> cList = new ArrayList<>();
+ for (char c : cArray)
+ cList.add(new Character(c));
+ o = cList;
+ } else if (elementType.getCanonicalName().equals("short")) {
+ short[] cArray = (short[]) o;
+ List<Short> cList = new ArrayList<>();
+ for (short c : cArray)
+ cList.add(new Short(c));
+ o = cList;
+ } else if (elementType.getCanonicalName().equals("int")) {
+ int[] cArray = (int[]) o;
+ List<Integer> cList = new ArrayList<>();
+ for (int c : cArray)
+ cList.add(new Integer(c));
+ o = cList;
+ } else if (elementType.getCanonicalName().equals("long")) {
+ long[] cArray = (long[]) o;
+ List<Long> cList = new ArrayList<>();
+ for (long c : cArray)
+ cList.add(new Long(c));
+ o = cList;
+ } else if (elementType.getCanonicalName().equals("float")) {
+ float[] cArray = (float[]) o;
+ List<Float> cList = new ArrayList<>();
+ for (float c : cArray)
+ cList.add(new Float(c));
+ o = cList;
+ } else if (elementType.getCanonicalName().equals("double")) {
+ double[] cArray = (double[]) o;
+ List<Double> cList = new ArrayList<>();
+ for (double c : cArray)
+ cList.add(new Double(c));
+ o = cList;
+ } else if (elementType.getCanonicalName().equals("boolean")) {
+ boolean[] cArray = (boolean[]) o;
+ List<Boolean> cList = new ArrayList<>();
+ for (boolean c : cArray)
+ cList.add(new Boolean(c));
+ o = cList;
+ } else if (!elementType.getCanonicalName().equals("byte")) {
+ // Covert arrays of objects
+ Object[] cArray = (Object[]) o;
+ List<Object> cList = new ArrayList<>();
+ for (Object c : cArray)
+ cList.add(c);
+ o = cList;
+ }
+ }
+
+ // If a Collection but not a List
+ if ((o instanceof Collection) && !(o instanceof List)) {
+ List<Object> cList = new ArrayList<>();
+ cList.addAll((Collection<Object>) o);
+ o = cList;
+ }
+ // Next check lists.
+ if (o instanceof List) {
+ if (currentDepth < 1)
+ throw new ReferenceServiceException(
+ "Cannot register list at depth " + currentDepth);
+ List<?> l = (List<?>) o;
+ /*
+ * If the list is empty then register a new empty list of the
+ * appropriate depth and return it
+ */
+ if (l.isEmpty())
+ try {
+ return listService.registerEmptyList(currentDepth, context)
+ .getId();
+ } catch (ListServiceException lse) {
+ throw new ReferenceServiceException(lse);
+ }
+ /*
+ * Otherwise construct a new list of T2Reference and register it,
+ * calling the getNameForObject method on all children of the list
+ * to construct the list of references
+ */
+ else {
+ List<T2Reference> references = new ArrayList<>();
+ for (Object item : l)
+ /*
+ * Recursively call this method with a depth one lower than
+ * the current depth
+ */
+ references.add(getNameForObject(item, currentDepth - 1,
+ useConverterSPI, context));
+ try {
+ return listService.registerList(references, context)
+ .getId();
+ } catch (ListServiceException lse) {
+ throw new ReferenceServiceException(lse);
+ }
+ }
+ } else {
+ /*
+ * Neither a list nor an already identified object, first thing is
+ * to engage the converters if enabled. Only engage if we don't
+ * already have a Throwable or an ExternalReferenceSPI instance
+ */
+ if (useConverterSPI && (o instanceof Throwable == false)
+ && (o instanceof ExternalReferenceSPI == false)) {
+ if (currentDepth != 0)
+ throw new ReferenceServiceException(
+ "Cannot register object " + o + " at depth "
+ + currentDepth);
+
+ for (ValueToReferenceConverterSPI converter : converters)
+ if (converter.canConvert(o, context))
+ try {
+ o = converter.convert(o, context);
+ break;
+ } catch (ValueToReferenceConversionException vtrce) {
+ /*
+ * Fail, but that doesn't matter at the moment as
+ * there may be more converters to try.
+ *
+ * TODO - log this!
+ */
+ }
+ }
+ /*
+ * If the object is neither a Throwable nor an ExternalReferenceSPI
+ * instance at this point we should fail the registration process,
+ * this means either that the conversion process wasn't enabled or
+ * that it failed to map the object type correctly.
+ */
+ if (!(o instanceof Throwable)
+ && !(o instanceof ExternalReferenceSPI))
+ throw new ReferenceServiceException(
+ "Failed to register object "
+ + o
+ + ", found a type '"
+ + o.getClass().getCanonicalName()
+ + "' which cannot currently be registered with the reference manager");
+
+ // Have either a Throwable or an ExternalReferenceSPI
+ if (o instanceof Throwable)
+ // Wrap in an ErrorDocument and return the ID
+ try {
+ ErrorDocument doc = errorDocumentService.registerError(
+ (Throwable) o, currentDepth, context);
+ return doc.getId();
+ } catch (ErrorDocumentServiceException edse) {
+ throw new ReferenceServiceException(edse);
+ }
+ if (o instanceof ExternalReferenceSPI) {
+ if (currentDepth != 0)
+ throw new ReferenceServiceException(
+ "Cannot register external references at depth "
+ + currentDepth);
+ try {
+ Set<ExternalReferenceSPI> references = new HashSet<ExternalReferenceSPI>();
+ references.add((ExternalReferenceSPI) o);
+ ReferenceSet rs = referenceSetService.registerReferenceSet(
+ references, context);
+ return rs.getId();
+ } catch (ReferenceSetServiceException rsse) {
+ throw new ReferenceServiceException(rsse);
+ }
+ }
+ }
+ throw new ReferenceServiceException(
+ "Should never see this, reference registration"
+ + " logic has fallen off the end of the"
+ + " world, check the code!");
+ }
+
+ /**
+ * Perform recursive identifier resolution, building a collection structure
+ * of Identified objects, any collection elements being IdentifiedLists of
+ * Identified subclasses. If the id has depth 0 this will just return the
+ * Identified to which that id refers.
+ *
+ * @param id
+ * the T2Reference to resolve
+ * @param ensureTypes
+ * a set of ExternalReferenceSPI classes, this is used to augment
+ * any resolved ReferenceSet instances to ensure that each one
+ * has at least one of the specified types. If augmentation is
+ * not required this can be set to null.
+ * @param context
+ * the ReferenceContext to use to resolve this and any
+ * recursively resolved identifiers
+ * <p>
+ * If the context is null a new EmptyReferenceContext is inserted
+ * in its place.
+ * @return fully resolved Identified subclass - this is either a (recursive)
+ * IdentifiedList of Identified, a ReferenceSet or an ErrorDocument
+ * @throws ReferenceServiceException
+ * if any problems occur during resolution
+ */
+ @Override
+ public Identified resolveIdentifier(T2Reference id,
+ Set<Class<ExternalReferenceSPI>> ensureTypes,
+ ReferenceContext context) throws ReferenceServiceException {
+ checkServices();
+ if (context == null)
+ context = new EmptyReferenceContext();
+ switch (id.getReferenceType()) {
+ case ReferenceSet:
+ try {
+ ReferenceSet rs;
+ if (ensureTypes == null)
+ rs = referenceSetService.getReferenceSet(id);
+ else
+ rs = referenceSetService.getReferenceSetWithAugmentation(
+ id, ensureTypes, context);
+ if (rs == null)
+ throw new ReferenceServiceException(
+ "Could not find ReferenceSet " + id);
+ return rs;
+ } catch (ReferenceSetServiceException rsse) {
+ throw new ReferenceServiceException(rsse);
+ }
+
+ case ErrorDocument:
+ try {
+ ErrorDocument ed = errorDocumentService.getError(id);
+ if (ed == null)
+ throw new ReferenceServiceException(
+ "Could not find ErrorDocument " + id);
+ return ed;
+ } catch (ErrorDocumentServiceException edse) {
+ throw new ReferenceServiceException(edse);
+ }
+
+ case IdentifiedList:
+ try {
+ IdentifiedList<T2Reference> idList = listService.getList(id);
+ if (idList == null)
+ throw new ReferenceServiceException(
+ "Could not find IdentifiedList " + id);
+ /*
+ * Construct a new list, and populate with the result of
+ * resolving each ID in turn
+ */
+ IdentifiedArrayList<Identified> newList = new IdentifiedArrayList<>();
+ for (T2Reference item : idList)
+ newList.add(resolveIdentifier(item, ensureTypes, context));
+ newList.setTypedId(getAsImpl(id));
+ return newList;
+ } catch (ListServiceException lse) {
+ throw new ReferenceServiceException(lse);
+ }
+
+ default:
+ throw new ReferenceServiceException("Unsupported ID type : "
+ + id.getReferenceType());
+ }
+ }
+
+ @Override
+ public Object renderIdentifier(T2Reference id, Class<?> leafClass,
+ ReferenceContext context) throws ReferenceServiceException {
+ // Check we have the services installed
+ checkServices();
+
+ // Insert an empty context if context was null
+ if (context == null)
+ context = new EmptyReferenceContext();
+ // Reject if the source reference contains errors
+ if (id.containsErrors())
+ throw new ReferenceServiceException(
+ "Can't render an identifier which contains errors to a POJO");
+
+ /*
+ * Attempt to find an appropriate StreamToValueConverterSPI instance to
+ * build the specified class
+ */
+ StreamToValueConverterSPI<?> converter = null;
+ if (valueBuilders != null)
+ for (StreamToValueConverterSPI<?> stvc : valueBuilders) {
+ Class<?> builtClass = stvc.getPojoClass();
+ if (leafClass.isAssignableFrom(builtClass)) {
+ converter = stvc;
+ break;
+ }
+ }
+ if (converter == null)
+ log.warn("No stream->value converters found for type '"
+ + leafClass.getCanonicalName() + "'");
+
+ // Render the identifier
+ return renderIdentifierInner(id, leafClass, context, converter);
+ }
+
+ private Object renderIdentifierInner(T2Reference id, Class<?> leafClass,
+ ReferenceContext context, StreamToValueConverterSPI<?> converter)
+ throws ReferenceServiceException {
+ checkServices();
+
+ switch (id.getReferenceType()) {
+ case IdentifiedList:
+ try {
+ IdentifiedList<T2Reference> idList = listService.getList(id);
+ if (idList == null)
+ throw new ReferenceServiceException(
+ "Could not find IdentifiedList " + id);
+ List<Object> result = new ArrayList<>();
+ for (T2Reference child : idList)
+ result.add(renderIdentifierInner(child, leafClass, context,
+ converter));
+ return result;
+ } catch (ListServiceException lse) {
+ throw new ReferenceServiceException(lse);
+ }
+
+ case ReferenceSet:
+ try {
+ ReferenceSet rs = referenceSetService.getReferenceSet(id);
+ if (rs == null)
+ throw new ReferenceServiceException(
+ "Could not find ReferenceSet " + id);
+ // Check that there are references in the set
+ if (rs.getExternalReferences().isEmpty())
+ throw new ReferenceServiceException(
+ "Can't render an empty reference set to a POJO");
+ /*
+ * If we can't directly map to an appropriate value keep track
+ * of the cheapest reference from which to try to build the pojo
+ * from a stream
+ */
+ ExternalReferenceSPI cheapestReference = null;
+ float cheapestReferenceCost = MAX_VALUE;
+ for (ExternalReferenceSPI ers : rs.getExternalReferences()) {
+ if (ers instanceof ValueCarryingExternalReference<?>) {
+ ValueCarryingExternalReference<?> vcer = (ValueCarryingExternalReference<?>) ers;
+ if (leafClass.isAssignableFrom(vcer.getValueType()))
+ return vcer.getValue();
+ }
+ // Got here so this wasn't an appropriate value type
+ if (cheapestReference == null
+ || ers.getResolutionCost() < cheapestReferenceCost) {
+ cheapestReference = ers;
+ cheapestReferenceCost = ers.getResolutionCost();
+ }
+ }
+ if (converter != null && cheapestReference != null)
+ try (InputStream stream = cheapestReference
+ .openStream(context)) {
+ return converter.renderFrom(stream,
+ cheapestReference.getDataNature(),
+ cheapestReference.getCharset());
+ }
+ } catch (Exception e) {
+ throw new ReferenceServiceException(e);
+ }
+ throw new ReferenceServiceException(
+ "No converter found, and reference set didn't contain"
+ + " an appropriate value carrying reference, cannot render to POJO");
+
+ default:
+ throw new ReferenceServiceException("Unsupported ID type : "
+ + id.getReferenceType());
+ }
+ }
+
+ /**
+ * Initiates a traversal of the specified t2reference, traversing to
+ * whatever level of depth is required such that all identifiers returned
+ * within the iterator have the specified depth. The context (i.e. the index
+ * path from the originally specified reference to each reference within the
+ * iteration) is included through use of the ContextualizedT2Reference
+ * wrapper class
+ *
+ * @param source
+ * the T2Reference from which to traverse. In general this is the
+ * root of a collection structure.
+ * @param desiredDepth
+ * the desired depth of all returned T2References, must be less
+ * than or equal to that of the source reference.
+ * @throws ReferenceServiceException
+ * if unable to create the iterator for some reason. Note that
+ * implementations are free to lazily perform the iteration so
+ * this method may succeed but the iterator produced can fail
+ * when used. If the iterator fails it will do so by throwing
+ * one of the underlying sub-service exceptions.
+ */
+ @Override
+ public Iterator<ContextualizedT2Reference> traverseFrom(T2Reference source,
+ int desiredDepth) throws ReferenceServiceException {
+ checkServices();
+ if (desiredDepth < 0)
+ throw new ReferenceServiceException(
+ "Cannot traverse to a negative depth");
+ List<ContextualizedT2Reference> workingSet = new ArrayList<>();
+ workingSet.add(new ContextualizedT2ReferenceImpl(source, new int[0]));
+ int currentDepth = source.getDepth();
+ while (currentDepth > desiredDepth) {
+ List<ContextualizedT2Reference> newSet = new ArrayList<>();
+ for (ContextualizedT2Reference ci : workingSet) {
+ T2ReferenceImpl ref = (T2ReferenceImpl) ci.getReference();
+ switch (ref.getReferenceType()) {
+ case IdentifiedList:
+ try {
+ int position = 0;
+ for (T2Reference child : getListService().getList(ref))
+ newSet.add(new ContextualizedT2ReferenceImpl(child,
+ addIndex(ci.getIndex(), position++)));
+ } catch (ListServiceException lse) {
+ throw new ReferenceServiceException(lse);
+ }
+ break;
+ case ReferenceSet:
+ throw new ReferenceServiceException(
+ "Should never be trying to drill inside a data document identifier");
+ case ErrorDocument:
+ newSet.add(new ContextualizedT2ReferenceImpl(ref
+ .getDeeperErrorReference(), addIndex(ci.getIndex(),
+ 0)));
+ break;
+ default:
+ throw new ReferenceServiceException(
+ "Fallen off end of case statement, unknown reference type!");
+ }
+ }
+ currentDepth--;
+ workingSet = newSet;
+ }
+ return workingSet.iterator();
+ }
+
+ /**
+ * Append to an int[]
+ *
+ * @param current
+ * current int[]
+ * @param head
+ * new int item to append to the current array
+ * @return new array of int with the head added
+ */
+ private static int[] addIndex(int[] current, int head) {
+ int[] result = new int[current.length + 1];
+ System.arraycopy(current, 0, result, 0, current.length);
+ result[current.length] = head;
+ return result;
+ }
+
+ /**
+ * Parse the reference contained in the string and return a
+ * {@link T2Reference} with the correct properties
+ */
+ @Override
+ public T2Reference referenceFromString(String reference) {
+ T2ReferenceImpl newRef = new T2ReferenceImpl();
+ Map<String, String> parseRef = parseRef(reference);
+ newRef.setNamespacePart(parseRef.get("namespace"));
+ newRef.setLocalPart(parseRef.get("localPart"));
+ String type = parseRef.get("type");
+ if (type.equals("ref")) {
+ newRef.setReferenceType(ReferenceSet);
+ } else if (type.equals("list")) {
+ newRef.setReferenceType(IdentifiedList);
+ newRef.setContainsErrors(Boolean.parseBoolean(parseRef.get("error")));
+ newRef.setDepth(Integer.parseInt(parseRef.get("depth")));
+ } else if (type.equals("error")) {
+ newRef.setContainsErrors(true);
+ newRef.setReferenceType(ErrorDocument);
+ newRef.setDepth(Integer.parseInt(parseRef.get("depth")));
+ } else {
+ return null;
+ // should throw an error
+ }
+
+ return newRef;
+ }
+
+ /**
+ * Parse the reference and return a map with localPart, namespace, depth,
+ * contains errors and the type
+ *
+ * @param ref
+ * @return
+ */
+ private Map<String, String> parseRef(String ref) {
+ String[] split = ref.split("\\?");
+ /*
+ * get the bit before and after the final '/' ie. the local part and the
+ * depth, there might not be a split1[1] since it might not be a list
+ */
+ String[] split2 = split[1].split("/");
+ // get the t2:abc:// and the namespace
+ String[] split3 = split[0].split("//");
+ // get the t2 bit and the reference type bit
+ String[] split4 = split3[0].split(":");
+
+ Map<String, String> refPartsMap = new HashMap<String, String>();
+ refPartsMap.put("type", split4[1]);
+ refPartsMap.put("namespace", split3[1]);
+ refPartsMap.put("localPart", split2[0]);
+
+ if (refPartsMap.get("type").equals("list")) {
+ refPartsMap.put("error", split2[1]);
+ refPartsMap.put("depth", split2[2]);
+ }
+ if (refPartsMap.get("type").equals("error"))
+ refPartsMap.put("depth", split2[1]);
+
+ return refPartsMap;
+
+ }
+
+ @Override
+ public boolean delete(List<T2Reference> references)
+ throws ReferenceServiceException {
+ for (T2Reference reference : references)
+ delete(reference);
+ return true;
+ }
+
+ @Override
+ public boolean delete(T2Reference reference)
+ throws ReferenceServiceException {
+ switch (reference.getReferenceType()) {
+ case IdentifiedList:
+ return listService.delete(reference);
+ case ReferenceSet:
+ return referenceSetService.delete(reference);
+ case ErrorDocument:
+ return errorDocumentService.delete(reference);
+ default:
+ throw new ReferenceServiceException("Unknown reference type!");
+ }
+ }
+
+ @Override
+ public void deleteReferencesForWorkflowRun(String workflowRunId)
+ throws ReferenceServiceException {
+ String errorString = "";
+ try {
+ listService.deleteIdentifiedListsForWorkflowRun(workflowRunId);
+ } catch (ReferenceServiceException resex) {
+ errorString += "Failed to delete lists for workflow run: "
+ + workflowRunId + ".";
+ }
+ try {
+ referenceSetService
+ .deleteReferenceSetsForWorkflowRun(workflowRunId);
+ } catch (ReferenceServiceException resex) {
+ errorString += "Failed to delete reference sets for workflow run: "
+ + workflowRunId + ".";
+ }
+ try {
+ errorDocumentService
+ .deleteErrorDocumentsForWorkflowRun(workflowRunId);
+ } catch (ReferenceServiceException resex) {
+ errorString += "Failed to delete error documents for workflow run: "
+ + workflowRunId + ".";
+ }
+ if (!errorString.equals(""))
+ throw new ReferenceServiceException(errorString);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetAugmentorImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetAugmentorImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetAugmentorImpl.java
new file mode 100644
index 0000000..994d104
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetAugmentorImpl.java
@@ -0,0 +1,461 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+import org.apache.taverna.reference.ExternalReferenceBuilderSPI;
+import org.apache.taverna.reference.ExternalReferenceSPI;
+import org.apache.taverna.reference.ExternalReferenceTranslatorSPI;
+import org.apache.taverna.reference.ReferenceContext;
+import org.apache.taverna.reference.ReferenceSet;
+import org.apache.taverna.reference.ReferenceSetAugmentationException;
+import org.apache.taverna.reference.ReferenceSetAugmentor;
+import org.apache.taverna.reference.ReferenceSetAugmentorCallback;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Implementation of ReferenceSetAugmentor using Dijkstra's shortest path
+ * algorithm over a type graph built from SPI instance registries of reference
+ * builders and reference translators.
+ *
+ * @author Tom Oinn
+ */
+public class ReferenceSetAugmentorImpl implements ReferenceSetAugmentor {
+ private final Logger log = Logger
+ .getLogger(ReferenceSetAugmentorImpl.class);
+
+ /**
+ * A list of ExternalReferenceBuilderSPI instances used to construct
+ * ExternalReferenceSPI instances from byte streams
+ */
+ private List<ExternalReferenceBuilderSPI<?>> builders;
+
+ /**
+ * A list of ExternalReferenceTranslatorSPI instances used to construct
+ * ExternalReferenceSPI instances from existing ExternalReferenceSPI
+ * instances.
+ */
+ private List<ExternalReferenceTranslatorSPI<?, ?>> translators;
+
+ private boolean cacheValid = false;
+
+ private final Set<Class<ExternalReferenceSPI>> knownReferenceTypes = new HashSet<>();
+ @SuppressWarnings("rawtypes")
+ private final Map<Class<ExternalReferenceSPI>, Set<ExternalReferenceTranslatorSPI>> adjacencySets = new HashMap<>();
+ private final Map<Class<ExternalReferenceSPI>, ShortestPathSolver> solvers = new HashMap<>();
+
+ /**
+ * Default constructor to make life easier when using Spring. To be
+ * functional this implementation should be injected with InstanceRegistry
+ * implementations containing lists of known implementations of the
+ * ExternalReferenceBuilderSPI and ExternalReferenceTranslatorSPI
+ * interfaces.
+ */
+ public ReferenceSetAugmentorImpl() {
+ super();
+ }
+
+ public void buildersUpdated(Object service, Map<?, ?> properties) {
+ cacheValid = false;
+ }
+
+ public void translatorsUpdated(Object service, Map<?, ?> properties) {
+ cacheValid = false;
+ }
+
+ /**
+ * Inject a list containing all known implementations of
+ * ExternalReferenceBuilderSPI.
+ *
+ * @throws IllegalStateException
+ * if this has already been set, the instance registries should
+ * only be set on bean construction.
+ */
+ public synchronized void setBuilders(
+ List<ExternalReferenceBuilderSPI<?>> builders) {
+ if (this.builders != null) {
+ log.error("Builder registry already injected, invalid operation");
+ throw new IllegalStateException(
+ "Can't inject the external reference builder registry "
+ + "multiple times.");
+ }
+
+ this.builders = builders;
+ if (log.isDebugEnabled()) {
+ log.debug("* Builders injected :");
+ int counter = 0;
+ for (ExternalReferenceBuilderSPI<?> builder : builders)
+ log.debug("* " + (++counter) + ") "
+ + builder.getClass().getSimpleName() + ", builds "
+ + builder.getReferenceType().getSimpleName());
+ }
+ cacheValid = false;
+ }
+
+ /**
+ * Inject a list containing all known implementations of
+ * ExternalReferenceTranslatorSPI.
+ *
+ * @throws IllegalStateException
+ * if this has already been set, the instance registries should
+ * only be set on bean construction.
+ */
+ public synchronized void setTranslators(
+ List<ExternalReferenceTranslatorSPI<?, ?>> translators) {
+ if (this.translators == null) {
+ this.translators = translators;
+ if (log.isDebugEnabled()) {
+ log.debug("* Translators injected :");
+ int counter = 0;
+ for (ExternalReferenceTranslatorSPI<?, ?> translator : translators)
+ log.debug("* "
+ + (++counter)
+ + ") "
+ + translator.getClass().getSimpleName()
+ + ", translates "
+ + translator.getSourceReferenceType()
+ .getSimpleName()
+ + " to "
+ + translator.getTargetReferenceType()
+ .getSimpleName());
+ }
+ cacheValid = false;
+ } else {
+ log.error("Translator registry already injected, invalid operation");
+ throw new IllegalStateException(
+ "Can't inject the translator registry multiple times.");
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected synchronized final void update() {
+ if (builders == null || translators == null || cacheValid)
+ return;
+ log.debug("# Refreshing shortest path cache");
+ knownReferenceTypes.clear();
+ solvers.clear();
+ adjacencySets.clear();
+ for (ExternalReferenceBuilderSPI erb : builders)
+ knownReferenceTypes.add(erb.getReferenceType());
+ for (ExternalReferenceTranslatorSPI ert : translators) {
+ knownReferenceTypes.add(ert.getSourceReferenceType());
+ knownReferenceTypes.add(ert.getTargetReferenceType());
+ getNeighbours(ert.getTargetReferenceType()).add(ert);
+ }
+ for (Class<ExternalReferenceSPI> type : knownReferenceTypes)
+ try {
+ solvers.put(type, new ShortestPathSolver(type));
+ } catch (Throwable t) {
+ log.error(t);
+ if (t instanceof RuntimeException)
+ throw (RuntimeException) t;
+ }
+ log.debug("# Path cache refresh done");
+ cacheValid = true;
+ }
+
+ @SuppressWarnings("rawtypes")
+ Set<ExternalReferenceTranslatorSPI> getNeighbours(
+ Class<ExternalReferenceSPI> node) {
+ Set<ExternalReferenceTranslatorSPI> adjacentTo = adjacencySets
+ .get(node);
+ if (adjacentTo != null)
+ return adjacentTo;
+
+ HashSet<ExternalReferenceTranslatorSPI> neighbours = new HashSet<>();
+ adjacencySets.put(node, neighbours);
+ return neighbours;
+ }
+
+ @Override
+ public final Set<ExternalReferenceSPI> augmentReferenceSet(
+ ReferenceSet references,
+ Set<Class<ExternalReferenceSPI>> targetReferenceTypes,
+ ReferenceContext context) throws ReferenceSetAugmentationException {
+ synchronized (this) {
+ if (!cacheValid)
+ update();
+ }
+
+ // Synchronize on the reference set itself
+ synchronized (references) {
+ /*
+ * First check whether we actually need to modify the reference set
+ * at all - it's perfectly valid to call the augmentor when nothing
+ * actually needs to be done (ideally you wouldn't do this, but it's
+ * likely to happen)
+ */
+ for (ExternalReferenceSPI er : references.getExternalReferences())
+ if (targetReferenceTypes.contains(er.getClass()))
+ return new HashSet<>();
+
+ // Need to perform augmentation if we reach this point
+ List<TranslationPath> candidatePaths = new ArrayList<>();
+ for (Class<ExternalReferenceSPI> target : targetReferenceTypes) {
+ ShortestPathSolver solver = solvers.get(target);
+ if (solver == null) {
+ solver = new ShortestPathSolver(target);
+ solvers.put(target, solver);
+ }
+ if (solver != null)
+ for (TranslationPath path : solver.getTranslationPaths()) {
+ for (ExternalReferenceSPI er : references
+ .getExternalReferences())
+ if (er.getClass().equals(path.getSourceType()))
+ candidatePaths.add(path);
+ for (TranslationPath dereferenceBasedPath : path
+ .getDereferenceBasedPaths(references))
+ candidatePaths.add(dereferenceBasedPath);
+ }
+ }
+ /*
+ * Now add candidate paths to represent a no-translator 'direct from
+ * byte stream source' path for each target type compatible
+ * reference builder
+ */
+ for (ExternalReferenceBuilderSPI<?> builder : builders)
+ if (targetReferenceTypes.contains(builder.getReferenceType()))
+ /*
+ * The builder can construct one of the target types, add
+ * paths for all possible pairs of 'de-reference existing
+ * reference' and the builder
+ */
+ for (ExternalReferenceSPI er : references
+ .getExternalReferences()) {
+ TranslationPath newPath = new TranslationPath();
+ newPath.setBuilders(builders);
+ newPath.setInitialBuilder(builder);
+ newPath.setSourceReference(er);
+ candidatePaths.add(newPath);
+ }
+
+ /*
+ * Got a list of candidate paths sorted by estimated overall path
+ * cost
+ */
+ Collections.sort(candidatePaths);
+ if (log.isDebugEnabled()) {
+ log.debug("Found "
+ + candidatePaths.size()
+ + " contextual translation path(s) including builder based :");
+ int counter = 0;
+ for (TranslationPath path : candidatePaths)
+ log.debug(" " + (++counter) + ") " + path.toString());
+ }
+
+ if (candidatePaths.isEmpty()) {
+ log.warn("No candidate paths found for augmentation");
+ throw new ReferenceSetAugmentationException(
+ "No candidate translation paths were found");
+ }
+
+ log.debug("Performing augmentation :");
+ int counter = 0;
+ for (TranslationPath path : candidatePaths)
+ try {
+ counter++;
+ Set<ExternalReferenceSPI> newReferences = path
+ .doTranslation(references, context);
+ if (log.isDebugEnabled())
+ log.debug(" Success (" + counter + "), created "
+ + printRefSet(newReferences));
+ return newReferences;
+ } catch (Exception ex) {
+ log.debug(" Failed (" + counter + ")");
+ log.trace(ex);
+ // Use next path...
+ }
+ log.warn(" No paths succeeded, augmentation failed");
+ throw new ReferenceSetAugmentationException(
+ "All paths threw exceptions, can't perform augmentation");
+ }
+ }
+
+ private String printRefSet(Set<ExternalReferenceSPI> set) {
+ StringBuilder sb = new StringBuilder("[");
+ String sep = "";
+ for (ExternalReferenceSPI ref : set) {
+ sb.append(sep).append(ref.toString());
+ sep = ",";
+ }
+ return sb.append("]").toString();
+ }
+
+ @Override
+ public final void augmentReferenceSetAsynch(final ReferenceSet references,
+ final Set<Class<ExternalReferenceSPI>> targetReferenceTypes,
+ final ReferenceContext context,
+ final ReferenceSetAugmentorCallback callback)
+ throws ReferenceSetAugmentationException {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.augmentationCompleted(augmentReferenceSet(
+ references, targetReferenceTypes, context));
+ } catch (ReferenceSetAugmentationException rsae) {
+ callback.augmentationFailed(rsae);
+ }
+ }
+ };
+ executeRunnable(r);
+ }
+
+ /**
+ * Schedule a runnable for execution - current naive implementation uses a
+ * new thread and executes immediately, but this is where any thread pool
+ * logic would go if we wanted to add that.
+ *
+ * @param r
+ */
+ private void executeRunnable(Runnable r) {
+ new Thread(r).start();
+ }
+
+ class ShortestPathSolver {
+ private Map<Class<ExternalReferenceSPI>, Class<ExternalReferenceSPI>> predecessors;
+ private Map<Class<ExternalReferenceSPI>, ExternalReferenceTranslatorSPI<?, ?>> translators;
+ private Map<Class<ExternalReferenceSPI>, Float> shortestDistances;
+ private final Comparator<Class<ExternalReferenceSPI>> shortestDistanceComparator = new Comparator<Class<ExternalReferenceSPI>>() {
+ @Override
+ public int compare(Class<ExternalReferenceSPI> left,
+ Class<ExternalReferenceSPI> right) {
+ float shortestDistanceLeft = shortestDistances.get(left);
+ float shortestDistanceRight = shortestDistances.get(right);
+ if (shortestDistanceLeft > shortestDistanceRight)
+ return +1;
+ if (shortestDistanceLeft < shortestDistanceRight)
+ return -1;
+ return left.getCanonicalName().compareTo(
+ right.getCanonicalName());
+ }
+ };
+ private final PriorityQueue<Class<ExternalReferenceSPI>> unsettledNodes = new PriorityQueue<>(
+ 10, shortestDistanceComparator);
+ private final Set<Class<ExternalReferenceSPI>> settledNodes = new HashSet<>();
+
+ private final List<TranslationPath> translationPaths = new ArrayList<>();
+
+ public List<TranslationPath> getTranslationPaths() {
+ return this.translationPaths;
+ }
+
+ public ShortestPathSolver(Class<ExternalReferenceSPI> targetType) {
+ log.debug("# Constructing shortest paths to '"
+ + targetType.getSimpleName() + "'");
+ predecessors = new HashMap<>();
+ translators = new HashMap<>();
+ shortestDistances = new HashMap<>();
+ setShortestDistance(targetType, 0.0f);
+ unsettledNodes.add(targetType);
+ while (!unsettledNodes.isEmpty()) {
+ Class<ExternalReferenceSPI> u = extractMin();
+ settledNodes.add(u);
+ relaxNeighbours(u);
+ }
+ for (Class<ExternalReferenceSPI> c : settledNodes)
+ if (!c.equals(targetType)) {
+ // Don't calculate a path to itself!
+ TranslationPath p = new TranslationPath();
+ p.setBuilders(builders);
+ Class<ExternalReferenceSPI> node = c;
+ while (predecessors.get(node) != null) {
+ p.pathSteps().add(translators.get(node));
+ // Recurse, should terminate at the target type
+ node = predecessors.get(node);
+ }
+ translationPaths.add(p);
+ }
+ Collections.sort(translationPaths);
+ if (translationPaths.isEmpty())
+ log.debug("# no paths discovered, type not reachable through translation");
+ else if (log.isDebugEnabled()) {
+ log.debug("# found " + translationPaths.size()
+ + " distinct path(s) :");
+ int counter = 0;
+ for (TranslationPath path : translationPaths)
+ log.debug("# " + (++counter) + ") " + path);
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private void relaxNeighbours(Class<ExternalReferenceSPI> u) {
+ log.trace("# relaxing node " + u.getSimpleName());
+ Set<Class<ExternalReferenceSPI>> alreadySeen = new HashSet<>();
+ for (ExternalReferenceTranslatorSPI ert : getNeighbours(u)) {
+ // all the translators that translate *to* u
+ Class<ExternalReferenceSPI> v = ert.getSourceReferenceType();
+ log.trace("# translator found from from '" + v + "' : "
+ + ert.getClass().getSimpleName());
+ if (!alreadySeen.contains(v) && !isSettled(v)) {
+ /*
+ * Avoid duplicate edges, always take the first one where
+ * such duplicates exist
+ */
+ alreadySeen.add(v);
+ if (getShortestDistance(v) > getShortestDistance(u)
+ + ert.getTranslationCost()) {
+ setShortestDistance(
+ v,
+ getShortestDistance(u)
+ + ert.getTranslationCost());
+ setPredecessor(v, u, ert);
+ unsettledNodes.add(v);
+ }
+ }
+ }
+ }
+
+ private boolean isSettled(Class<ExternalReferenceSPI> node) {
+ return settledNodes.contains(node);
+ }
+
+ private void setShortestDistance(Class<ExternalReferenceSPI> node,
+ float distance) {
+ shortestDistances.put(node, distance);
+ }
+
+ private float getShortestDistance(Class<ExternalReferenceSPI> node) {
+ Float d = shortestDistances.get(node);
+ return (d == null) ? Float.MAX_VALUE : d;
+ }
+
+ private Class<ExternalReferenceSPI> extractMin() {
+ return unsettledNodes.poll();
+ }
+
+ private void setPredecessor(Class<ExternalReferenceSPI> child,
+ Class<ExternalReferenceSPI> parent,
+ ExternalReferenceTranslatorSPI<?, ?> translator) {
+ predecessors.put(child, parent);
+ translators.put(child, translator);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetImpl.java
new file mode 100644
index 0000000..785a552
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetImpl.java
@@ -0,0 +1,122 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import java.util.Set;
+
+import org.apache.taverna.reference.ExternalReferenceSPI;
+import org.apache.taverna.reference.ReferenceSet;
+import org.apache.taverna.reference.h3.HibernateMappedEntity;
+
+/**
+ * An implementation of ReferenceSet with the additional methods and metadata
+ * required by Hibernate3 to allow it to be persisted in a relational store. As
+ * with everything else in this package you shouldn't be using this class
+ * directly! Instead of this class you should use the registration methods on
+ * {@link org.apache.taverna.reference.ReferenceSetService}, implementations of
+ * that interface will handle the construction of ReferenceSet implementations
+ * (including this one).
+ *
+ * @author Tom Oinn
+ */
+public class ReferenceSetImpl extends AbstractEntityImpl implements
+ ReferenceSet, HibernateMappedEntity {
+ private Set<ExternalReferenceSPI> externalReferences;
+ private Long approximateSizeInBytes = new Long(-1);
+
+ /**
+ * Construct a new ReferenceSetImpl with the given set of external
+ * references and identifier.
+ *
+ * @param references
+ * the set of ExternalReferenceSPI which this reference set
+ * should contain initially
+ * @param id
+ * the T2Reference to use, must be an instance of
+ * ReferenceSetT2ReferenceImpl so hibernate can make use of it as
+ * a compound primary key component
+ */
+ public ReferenceSetImpl(Set<ExternalReferenceSPI> references,
+ T2ReferenceImpl id) {
+ setTypedId(id);
+ this.externalReferences = references;
+
+ // Should be at least one - otherwise we cannot calculate the data size
+ if (externalReferences != null && externalReferences.size() > 0) {
+ // Just take the first ExternalReferenceSPI returned
+ ExternalReferenceSPI externalReferenceSPI = externalReferences
+ .toArray(new ExternalReferenceSPI[0])[0];
+ approximateSizeInBytes = externalReferenceSPI
+ .getApproximateSizeInBytes();
+ }
+ }
+
+ /**
+ * Default constructor, used by Hibernate when reconstructing this bean from
+ * the database. If you call this directly from your code you must then call
+ * both {@link #setExternalReferences(Set)} and
+ * {@link #setId(T2ReferenceImpl)} before any use of the reference set. If
+ * you're not writing the reference manager implementation you shouldn't be
+ * using this class anyway.
+ */
+ public ReferenceSetImpl() {
+ //
+ }
+
+ /**
+ * For debugging purposes, prints a summary of the contents and identifier
+ * of this reference set.
+ *
+ * @return human readable string representation of this object. This is not
+ * regarded as 'stable' and should not be parsed for any reason!
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getId()).append(" [").append(externalReferences.size())
+ .append("]\n");
+ for (ExternalReferenceSPI ref : externalReferences)
+ sb.append(" ").append(ref).append("\n");
+ return sb.toString();
+
+ }
+
+ @Override
+ public Set<ExternalReferenceSPI> getExternalReferences() {
+ return externalReferences;
+ }
+
+ /**
+ * This method is only ever called from within Hibernate, and is used to
+ * initialize the set of external references.
+ */
+ public void setExternalReferences(Set<ExternalReferenceSPI> newReferences) {
+ this.externalReferences = newReferences;
+ }
+
+ public void setApproximateSizeInBytes(Long sizeInBytes) {
+ this.approximateSizeInBytes = sizeInBytes;
+ }
+
+ @Override
+ public Long getApproximateSizeInBytes() {
+ return approximateSizeInBytes;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetServiceImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetServiceImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetServiceImpl.java
new file mode 100644
index 0000000..0a50883
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/ReferenceSetServiceImpl.java
@@ -0,0 +1,152 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import static org.apache.taverna.reference.impl.T2ReferenceImpl.getAsImpl;
+
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import org.apache.taverna.reference.DaoException;
+import org.apache.taverna.reference.ExternalReferenceSPI;
+import org.apache.taverna.reference.ReferenceContext;
+import org.apache.taverna.reference.ReferenceServiceException;
+import org.apache.taverna.reference.ReferenceSet;
+import org.apache.taverna.reference.ReferenceSetAugmentationException;
+import org.apache.taverna.reference.ReferenceSetService;
+import org.apache.taverna.reference.ReferenceSetServiceException;
+import org.apache.taverna.reference.T2Reference;
+
+/**
+ * Implementation of ReferenceSetService, inject with an appropriate
+ * ReferenceSetDao to enable. Implements translation functionality as long as an
+ * appropriate ReferenceSetAugmentor implementation is injected.
+ *
+ * @author Tom Oinn
+ */
+public class ReferenceSetServiceImpl extends AbstractReferenceSetServiceImpl
+ implements ReferenceSetService {
+ @Override
+ public ReferenceSet getReferenceSet(T2Reference id)
+ throws ReferenceSetServiceException {
+ checkDao();
+ try {
+ return referenceSetDao.get(id);
+ } catch (DaoException de) {
+ throw new ReferenceSetServiceException(de);
+ }
+ }
+
+ private Map<URI,Object> locks = new WeakHashMap<>();
+
+ private Object getLock(T2Reference id) {
+ URI uri = id.toUri();
+ synchronized (locks) {
+ Object lock = locks.get(uri);
+ if (lock == null) {
+ lock = new Object();
+ locks.put(uri, lock);
+ }
+ return lock;
+ }
+ }
+
+ @Override
+ public ReferenceSet getReferenceSetWithAugmentation(T2Reference id,
+ Set<Class<ExternalReferenceSPI>> ensureTypes,
+ ReferenceContext context) throws ReferenceSetServiceException {
+ checkDao();
+ checkAugmentor();
+ if (context == null)
+ context = new EmptyReferenceContext();
+ // Obtain the reference set
+
+ try {
+ /*
+ * Synchronize on the reference set, should ensure that we don't
+ * have multiple concurrent translations assuming that Hibernate
+ * retrieves the same entity each time. Except we have to
+ * synchronize on the reference, and in fact we have to synchronize
+ * on the URI form.
+ */
+ synchronized (getLock(id)) {
+ ReferenceSet rs = getReferenceSet(id);
+ Set<ExternalReferenceSPI> newReferences = referenceSetAugmentor
+ .augmentReferenceSet(rs, ensureTypes, context);
+ if (!newReferences.isEmpty()) {
+ /*
+ * Write back changes to the store if we got here, this can
+ * potentially throw an unsupported operation exception in
+ * which case we have to fail the augmentation.
+ */
+ try {
+ rs.getExternalReferences().addAll(newReferences);
+ } catch (RuntimeException re) {
+ throw new ReferenceSetAugmentationException(
+ "Can't add new references back into existing reference set instance");
+ }
+ referenceSetDao.update(rs);
+ }
+ return rs;
+ }
+ } catch (ReferenceSetAugmentationException rsae) {
+ throw new ReferenceSetServiceException(rsae);
+ }
+ }
+
+ @Override
+ public ReferenceSet registerReferenceSet(
+ Set<ExternalReferenceSPI> references, ReferenceContext context)
+ throws ReferenceSetServiceException {
+ checkDao();
+ checkGenerator();
+
+ ReferenceSetImpl rsi = new ReferenceSetImpl(new HashSet<>(references),
+ getAsImpl(t2ReferenceGenerator
+ .nextReferenceSetReference(context)));
+
+ try {
+ referenceSetDao.store(rsi);
+ return rsi;
+ } catch (DaoException de) {
+ throw new ReferenceSetServiceException(de);
+ }
+ }
+
+ @Override
+ public boolean delete(T2Reference reference)
+ throws ReferenceServiceException {
+ checkDao();
+ ReferenceSet set = referenceSetDao.get(reference);
+ if (set == null)
+ return false;
+ return referenceSetDao.delete(set);
+ }
+
+ @Override
+ public void deleteReferenceSetsForWorkflowRun(String workflowRunId)
+ throws ReferenceServiceException {
+ checkDao();
+ referenceSetDao.deleteReferenceSetsForWFRun(workflowRunId);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleCacheProviderImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleCacheProviderImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleCacheProviderImpl.java
new file mode 100644
index 0000000..5ea2e82
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleCacheProviderImpl.java
@@ -0,0 +1,56 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.taverna.reference.Identified;
+import org.apache.taverna.reference.ReferenceServiceCacheProvider;
+import org.apache.taverna.reference.T2Reference;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Completely naive cache provider that just stores everything in a map. This
+ * <em>will</em> run out of memory as it makes no attempt to evict old items,
+ * it's really just here as a test!
+ *
+ * @author Tom Oinn
+ */
+public class SimpleCacheProviderImpl implements ReferenceServiceCacheProvider {
+ private final Logger log = Logger.getLogger(SimpleCacheProviderImpl.class);
+ private Map<T2Reference, Identified> cache = new HashMap<>();
+
+ @Override
+ public Identified get(T2Reference id) {
+ if (log.isDebugEnabled())
+ log.debug("Get " + id.toString() + " (" + cache.containsKey(id)
+ + ")");
+ return cache.get(id);
+ }
+
+ @Override
+ public void put(Identified i) {
+ if (log.isDebugEnabled())
+ log.debug("Put " + i.getId().toString());
+ cache.put(i.getId(), i);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleT2ReferenceGenerator.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleT2ReferenceGenerator.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleT2ReferenceGenerator.java
new file mode 100644
index 0000000..4be90b5
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/SimpleT2ReferenceGenerator.java
@@ -0,0 +1,62 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import org.apache.taverna.reference.T2ReferenceGenerator;
+
+/**
+ * Simple implementation of T2ReferenceGenerator intended to be injected into
+ * the service layers for integration testing. Exposes a namespace property
+ * which can be configured through spring and allocates local parts based on an
+ * integer counter - this is only guaranteed to be unique within a single
+ * instance of this object so isn't suitable for real production use. For proper
+ * usage use an implementation tied to the backing store you're putting t2
+ * reference objects into.
+ *
+ * @author Tom Oinn
+ */
+public class SimpleT2ReferenceGenerator extends AbstractT2ReferenceGenerator implements T2ReferenceGenerator {
+ private String namespace = "testNS";
+ private String localPrefix = "test";
+ private int counter = 0;
+
+ /**
+ * Set the namespace for identifiers generated by this class as a string
+ *
+ * @param newNamespace
+ * the namespace to use
+ */
+ public void setNamespace(String newNamespace) {
+ this.namespace = newNamespace;
+ }
+
+ /**
+ * Get the namespace for identifiers generated by this class
+ */
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @Override
+ protected synchronized String getNextLocalPart() {
+ return localPrefix + (counter++);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/StackTraceElementBeanImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/StackTraceElementBeanImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/StackTraceElementBeanImpl.java
new file mode 100644
index 0000000..12158f5
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/StackTraceElementBeanImpl.java
@@ -0,0 +1,76 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import org.apache.taverna.reference.StackTraceElementBean;
+import org.apache.taverna.reference.h3.HibernateComponentClass;
+
+/**
+ * Simple bean implementation of StackTraceElement for Hibernate
+ *
+ * @author Tom Oinn
+ */
+public class StackTraceElementBeanImpl implements StackTraceElementBean,
+ HibernateComponentClass {
+ private String className;
+ private String fileName;
+ private String methodName;
+ private int lineNumber;
+
+ public StackTraceElementBeanImpl() {
+ //
+ }
+
+ @Override
+ public String getClassName() {
+ return this.className;
+ }
+
+ public void setClassName(String className) {
+ this.className = className;
+ }
+
+ @Override
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ @Override
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ @Override
+ public String getMethodName() {
+ return this.methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceImpl.java
new file mode 100644
index 0000000..2f0b385
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceImpl.java
@@ -0,0 +1,287 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import static org.apache.taverna.reference.T2ReferenceType.ErrorDocument;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.UUID;
+
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.T2ReferenceType;
+import org.apache.taverna.reference.h3.HibernateComponentClass;
+
+import org.apache.log4j.Logger;
+
+/**
+ * An implementation of T2Reference specific to the ReferenceSetImpl. This is
+ * needed because ReferenceSetImpl uses a component based primary key driven
+ * from the namespace and local parts of T2Reference. This in turn means we can
+ * query hibernate directly with a T2Reference instance in the data access
+ * object. Because this is only used as a component (i.e. a value type) we don't
+ * need to define a hibernate mapping file for it.
+ *
+ * @author Tom Oinn
+ * @author David Withers
+ */
+public class T2ReferenceImpl implements T2Reference, Serializable, HibernateComponentClass {
+ private static Logger logger = Logger.getLogger(T2ReferenceImpl.class);
+
+ private static final long serialVersionUID = 8363330461158750319L;
+ private String localPart;
+ private String namespacePart;
+ private long localMostSigBits, localLeastSigBits;
+ private boolean containsErrors = false;
+ private T2ReferenceType referenceType = T2ReferenceType.ReferenceSet;
+ private int depth = 0;
+ private transient URI cachedURI;
+
+ public T2ReferenceImpl() {
+ // Default constructor for Hibernate et al
+ }
+
+ /**
+ * Construct a deep copy of the given T2Reference
+ *
+ * @param source
+ * T2Reference to copy
+ */
+ private T2ReferenceImpl(T2Reference source) {
+ super();
+ setNamespacePart(source.getNamespacePart());
+ setLocalPart(source.getLocalPart());
+ setContainsErrors(source.containsErrors());
+ setReferenceType(source.getReferenceType());
+ setDepth(source.getDepth());
+ }
+
+ public static T2ReferenceImpl getAsImpl(T2Reference source) {
+ if (source instanceof T2ReferenceImpl)
+ return (T2ReferenceImpl) source;
+ return new T2ReferenceImpl(source);
+ }
+
+ /**
+ * Return whether the identified entity either is or contains errors
+ */
+ @Override
+ public boolean containsErrors() {
+ return this.containsErrors;
+ }
+
+ /**
+ * Property accessor for Hibernate, complies with java bean spec
+ */
+ public boolean getContainsErrors() {
+ return this.containsErrors;
+ }
+
+ /**
+ * Get the depth of the entity referred to by this reference
+ */
+ @Override
+ public int getDepth() {
+ return this.depth;
+ }
+
+ /**
+ * Get the local part of the URI for this reference
+ */
+ @Override
+ public String getLocalPart() {
+ if (localPart == null) {
+ UUID localPartUUID = new UUID(localMostSigBits, localLeastSigBits);
+ return localPartUUID.toString();
+ }
+ return localPart;
+ }
+
+ /**
+ * Get the namespace part of the URI for this reference
+ */
+ @Override
+ public String getNamespacePart() {
+ return namespacePart;
+ }
+
+ /**
+ * Get the type of the entity to which this reference refers
+ */
+ @Override
+ public T2ReferenceType getReferenceType() {
+ return referenceType;
+ }
+
+ /**
+ * This method is only ever called from within Hibernate when
+ * re-constructing the identifier component to set the namespace part of the
+ * identifier.
+ */
+ public synchronized void setNamespacePart(String namespacePart) {
+ this.namespacePart = namespacePart;
+ }
+
+ /**
+ * This method is only ever called from within Hibernate when
+ * re-constructing the identifier component to set the local part of the
+ * identifier.
+ */
+ public synchronized void setLocalPart(String localPart) {
+ try {
+ UUID namespacePartUUID = UUID.fromString(localPart);
+ localMostSigBits = namespacePartUUID.getMostSignificantBits();
+ localLeastSigBits = namespacePartUUID.getLeastSignificantBits();
+ this.localPart = null;
+ } catch (IllegalArgumentException e) {
+ this.localPart = localPart;
+ }
+ }
+
+ /**
+ * This method is only ever called from within Hibernate when
+ * re-constructing the identifier component to set the depth of the
+ * identifier.
+ */
+ public synchronized void setDepth(int depth) {
+ this.depth = depth;
+ }
+
+ /**
+ * This method is only ever called from within Hibernate when
+ * re-constructing the identifier component to set the error property of the
+ * identifier.
+ */
+ public synchronized void setContainsErrors(boolean containsErrors) {
+ this.containsErrors = containsErrors;
+ }
+
+ /**
+ * This method is only ever called from within Hibernate when
+ * re-constructing the identifier component to set the reference type
+ * property of the identifier.
+ */
+ public synchronized void setReferenceType(T2ReferenceType type) {
+ this.referenceType = type;
+ }
+
+ /**
+ * By default when printing an identifier we use {@link #toUri()}.
+ * {@link java.net.URI#toASCIIString() toASCIIString()}
+ */
+ @Override
+ public String toString() {
+ return toUri().toASCIIString();
+ }
+
+ /**
+ * Drill inside an error document reference to get the error one deeper than
+ * this as long as it is at least depth 1.
+ */
+ T2ReferenceImpl getDeeperErrorReference() {
+ if (!getReferenceType().equals(ErrorDocument))
+ throw new AssertionError(
+ "Attempt to get a deeper reference on something that isn't an error ref");
+ if (getDepth() == 0)
+ throw new AssertionError(
+ "Error identifier already has depth 0, cannot decrease");
+
+ T2ReferenceImpl result = new T2ReferenceImpl();
+ result.setContainsErrors(true);
+ result.setDepth(getDepth() - 1);
+ result.setLocalPart(getLocalPart());
+ result.setNamespacePart(getNamespacePart());
+ result.setReferenceType(ErrorDocument);
+ return result;
+ }
+
+ /**
+ * Returns the identifier expressed as a {@link java.net.URI URI},
+ * constructed based on the reference type. For references to ReferenceSet
+ * this is
+ * <code>new URI("t2:ref//" + namespacePart + "?" + localPart)</code>
+ * leading to URIs of the form <code>t2:ref//namespace?local</code>
+ */
+ @Override
+ public synchronized URI toUri() {
+ try {
+ if (cachedURI == null)
+ switch (referenceType) {
+ case ReferenceSet:
+ cachedURI = new URI("t2:ref//" + getNamespacePart() + "?"
+ + getLocalPart());
+ case IdentifiedList:
+ cachedURI = new URI("t2:list//" + getNamespacePart() + "?"
+ + getLocalPart() + "/" + containsErrors + "/"
+ + depth);
+ case ErrorDocument:
+ cachedURI = new URI("t2:error//" + getNamespacePart() + "?"
+ + getLocalPart() + "/" + depth);
+ }
+ } catch (URISyntaxException e) {
+ logger.error("Unable to create URI", e);
+ }
+ return cachedURI;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + depth;
+ result = 31 * result + (int) (localLeastSigBits ^ (localLeastSigBits >>> 32));
+ result = 31 * result + (int) (localMostSigBits ^ (localMostSigBits >>> 32));
+ result = 31 * result + ((localPart == null) ? 0 : localPart.hashCode());
+ result = 31 * result + ((namespacePart == null) ? 0 : namespacePart.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ T2ReferenceImpl other = (T2ReferenceImpl) obj;
+ if (depth != other.depth)
+ return false;
+ if (localLeastSigBits != other.localLeastSigBits)
+ return false;
+ if (localMostSigBits != other.localMostSigBits)
+ return false;
+ if (localPart == null) {
+ if (other.localPart != null)
+ return false;
+ } else if (!localPart.equals(other.localPart))
+ return false;
+ if (namespacePart == null) {
+ if (other.namespacePart != null)
+ return false;
+ } else if (!namespacePart.equals(other.namespacePart))
+ return false;
+ return true;
+ }
+
+ public synchronized String getCompactForm() {
+ return getNamespacePart() + ":" + getLocalPart() + ":" + getDepth();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceListImpl.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceListImpl.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceListImpl.java
new file mode 100644
index 0000000..2b0a449
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/T2ReferenceListImpl.java
@@ -0,0 +1,74 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import java.util.List;
+
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.h3.HibernateMappedEntity;
+
+/**
+ * Simple extension of
+ * <code>{@link IdentifiedArrayList IdentifiedArrayList<T2Reference>}</code>
+ * exposing get and set methods for the list contents so we can map it in
+ * hibernate.
+ *
+ * @author Tom Oinn
+ */
+public class T2ReferenceListImpl extends IdentifiedArrayList<T2Reference>
+ implements HibernateMappedEntity {
+ public T2ReferenceListImpl() {
+ super();
+ }
+
+ /**
+ * This is only called from Hibernate, outside of test code, so is
+ * relatively safe to leave unchecked.
+ */
+ @SuppressWarnings("rawtypes")
+ public List getListContents() {
+ return this.listDelegate;
+ }
+
+ /**
+ * This is only called from Hibernate, outside of test code, so is
+ * relatively safe to leave unchecked.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void setListContents(List newList) {
+ this.listDelegate = newList;
+ }
+
+ /**
+ * Print the contents of this list for vaguely human readable debug
+ * purposes.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getId()).append("\n");
+ int counter = 0;
+ for (T2Reference ref : listDelegate)
+ sb.append(" ").append(++counter).append(") ").append(ref)
+ .append("\n");
+ return sb.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateErrorDocumentDao.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateErrorDocumentDao.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateErrorDocumentDao.java
new file mode 100644
index 0000000..38a6988
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateErrorDocumentDao.java
@@ -0,0 +1,154 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import static org.apache.taverna.reference.T2ReferenceType.ErrorDocument;
+
+import java.util.List;
+
+import org.apache.taverna.reference.DaoException;
+import org.apache.taverna.reference.ErrorDocument;
+import org.apache.taverna.reference.ErrorDocumentDao;
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.annotations.DeleteIdentifiedOperation;
+import org.apache.taverna.reference.annotations.GetIdentifiedOperation;
+import org.apache.taverna.reference.annotations.PutIdentifiedOperation;
+
+import org.hibernate.Query;
+import org.hibernate.SessionFactory;
+
+/**
+ * An implementation of ErrorDocumentDao based on raw hibernate session factory
+ * injection and running within a spring managed context through auto-proxy
+ * generation. To use this in spring inject a property 'sessionFactory' with
+ * either a {@link org.springframework.orm.hibernate3.LocalSessionFactoryBean
+ * LocalSessionFactoryBean} or the equivalent class from the T2Platform module
+ * to add SPI based implementation discovery and mapping. To use outside of
+ * Spring ensure you call the setSessionFactory(..) method before using this
+ * (but really, use it from Spring, so much easier).
+ * <p>
+ * Methods in this Dao require transactional support
+ *
+ * @author Tom Oinn
+ */
+public class TransactionalHibernateErrorDocumentDao implements ErrorDocumentDao {
+ private static final String GET_ERRORS_FOR_RUN = "FROM ErrorDocumentImpl WHERE namespacePart = :workflow_run_id";
+ private SessionFactory sessionFactory;
+
+ public void setSessionFactory(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ /**
+ * Fetch an ErrorDocument list by id
+ *
+ * @param ref
+ * the T2Reference to fetch
+ * @return a retrieved identified list of T2 references
+ * @throws DaoException
+ * if the supplied reference is of the wrong type or if
+ * something goes wrong fetching the data or connecting to the
+ * database
+ */
+ @Override
+ @GetIdentifiedOperation
+ public ErrorDocument get(T2Reference ref) throws DaoException {
+ if (ref == null)
+ throw new DaoException(
+ "Supplied reference is null, can't retrieve.");
+ if (!ref.getReferenceType().equals(ErrorDocument))
+ throw new DaoException(
+ "This dao can only retrieve reference of type T2Reference.ErrorDocument");
+ if (!(ref instanceof T2ReferenceImpl))
+ throw new DaoException(
+ "Reference must be an instance of T2ReferenceImpl");
+
+ try {
+ return (ErrorDocumentImpl) sessionFactory.getCurrentSession().get(
+ ErrorDocumentImpl.class,
+ ((T2ReferenceImpl) ref).getCompactForm());
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+
+ @Override
+ @PutIdentifiedOperation
+ public void store(ErrorDocument theDocument) throws DaoException {
+ if (theDocument.getId() == null)
+ throw new DaoException(
+ "Supplied error document set has a null ID, allocate "
+ + "an ID before calling the store method in the dao.");
+ if (!theDocument.getId().getReferenceType().equals(ErrorDocument))
+ throw new DaoException("Strangely the list ID doesn't have type "
+ + "T2ReferenceType.ErrorDocument, something has probably "
+ + "gone badly wrong somewhere earlier!");
+ if (!(theDocument instanceof ErrorDocumentImpl))
+ throw new DaoException(
+ "Supplied ErrorDocument not an instance of ErrorDocumentImpl");
+
+ try {
+ sessionFactory.getCurrentSession().save(theDocument);
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+
+ @Override
+ @DeleteIdentifiedOperation
+ public boolean delete(ErrorDocument theDocument) throws DaoException {
+ if (theDocument.getId() == null)
+ throw new DaoException(
+ "Supplied error document set has a null ID, allocate "
+ + "an ID before calling the store method in the dao.");
+ if (!theDocument.getId().getReferenceType().equals(ErrorDocument))
+ throw new DaoException("Strangely the list ID doesn't have type "
+ + "T2ReferenceType.ErrorDocument, something has probably "
+ + "gone badly wrong somewhere earlier!");
+ if (!(theDocument instanceof ErrorDocumentImpl))
+ throw new DaoException(
+ "Supplied ErrorDocument not an instance of ErrorDocumentImpl");
+
+ try {
+ sessionFactory.getCurrentSession().delete(theDocument);
+ return true;
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ @DeleteIdentifiedOperation
+ public synchronized void deleteErrorDocumentsForWFRun(String workflowRunId)
+ throws DaoException {
+ try {
+ // Select all ErrorDocuments for this wf run
+ Query selectQuery = sessionFactory.getCurrentSession().createQuery(
+ GET_ERRORS_FOR_RUN);
+ selectQuery.setString("workflow_run_id", workflowRunId);
+ List<ErrorDocument> errorDocuments = selectQuery.list();
+ for (ErrorDocument errorDocument : errorDocuments)
+ delete(errorDocument);
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-engine/blob/5f1ddb71/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateListDao.java
----------------------------------------------------------------------
diff --git a/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateListDao.java b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateListDao.java
new file mode 100644
index 0000000..3cc6561
--- /dev/null
+++ b/taverna-reference-impl/src/main/java/org/apache/taverna/reference/impl/TransactionalHibernateListDao.java
@@ -0,0 +1,153 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.reference.impl;
+
+import static org.apache.taverna.reference.T2ReferenceType.IdentifiedList;
+
+import java.util.List;
+
+import org.apache.taverna.reference.DaoException;
+import org.apache.taverna.reference.IdentifiedList;
+import org.apache.taverna.reference.ListDao;
+import org.apache.taverna.reference.T2Reference;
+import org.apache.taverna.reference.annotations.DeleteIdentifiedOperation;
+import org.apache.taverna.reference.annotations.GetIdentifiedOperation;
+import org.apache.taverna.reference.annotations.PutIdentifiedOperation;
+
+import org.hibernate.Query;
+import org.hibernate.SessionFactory;
+
+/**
+ * An implementation of ListDao based on based on raw hibernate session factory
+ * injection and running within a spring managed context through auto-proxy
+ * generation. To use this in spring inject a property 'sessionFactory' with
+ * either a {@link org.springframework.orm.hibernate3.LocalSessionFactoryBean
+ * LocalSessionFactoryBean} or the equivalent class from the T2Platform module
+ * to add SPI based implementation discovery and mapping. To use outside of
+ * Spring ensure you call the setSessionFactory(..) method before using this
+ * (but really, use it from Spring, so much easier).
+ * <p>
+ * Methods in this Dao require transactional support
+ *
+ * @author Tom Oinn
+ */
+public class TransactionalHibernateListDao implements ListDao {
+ private static final String GET_REFLISTS_FOR_RUN = "FROM T2ReferenceListImpl WHERE namespacePart = :workflow_run_id";
+ private SessionFactory sessionFactory;
+
+ public void setSessionFactory(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
+
+ /**
+ * Fetch a t2reference list by id
+ *
+ * @param ref
+ * the T2Reference to fetch
+ * @return a retrieved identified list of T2 references
+ * @throws DaoException
+ * if the supplied reference is of the wrong type or if
+ * something goes wrong fetching the data or connecting to the
+ * database
+ */
+ @Override
+ @GetIdentifiedOperation
+ public IdentifiedList<T2Reference> get(T2Reference ref) throws DaoException {
+ if (ref == null)
+ throw new DaoException(
+ "Supplied reference is null, can't retrieve.");
+ if (!ref.getReferenceType().equals(IdentifiedList))
+ throw new DaoException(
+ "This dao can only retrieve reference of type T2Reference.IdentifiedList");
+ if (!(ref instanceof T2ReferenceImpl))
+ throw new DaoException(
+ "Reference must be an instance of T2ReferenceImpl");
+
+ try {
+ return (T2ReferenceListImpl) sessionFactory.getCurrentSession()
+ .get(T2ReferenceListImpl.class,
+ ((T2ReferenceImpl) ref).getCompactForm());
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+
+ @Override
+ @PutIdentifiedOperation
+ public void store(IdentifiedList<T2Reference> theList) throws DaoException {
+ if (theList.getId() == null)
+ throw new DaoException("Supplied list set has a null ID, allocate "
+ + "an ID before calling the store method in the dao.");
+ if (!theList.getId().getReferenceType().equals(IdentifiedList))
+ throw new DaoException("Strangely the list ID doesn't have type "
+ + "T2ReferenceType.IdentifiedList, something has probably "
+ + "gone badly wrong somewhere earlier!");
+ if (!(theList instanceof T2ReferenceListImpl))
+ throw new DaoException(
+ "Supplied identifier list not an instance of T2ReferenceList");
+
+ try {
+ sessionFactory.getCurrentSession().save(theList);
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+
+ @Override
+ public boolean delete(IdentifiedList<T2Reference> theList)
+ throws DaoException {
+ if (theList.getId() == null)
+ throw new DaoException("Supplied list set has a null ID, allocate "
+ + "an ID before calling the store method in the dao.");
+ if (!theList.getId().getReferenceType().equals(IdentifiedList))
+ throw new DaoException("Strangely the list ID doesn't have type "
+ + "T2ReferenceType.IdentifiedList, something has probably "
+ + "gone badly wrong somewhere earlier!");
+ if (!(theList instanceof T2ReferenceListImpl))
+ throw new DaoException(
+ "Supplied identifier list not an instance of T2ReferenceList");
+
+ try {
+ sessionFactory.getCurrentSession().delete(theList);
+ return true;
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ @DeleteIdentifiedOperation
+ public synchronized void deleteIdentifiedListsForWFRun(String workflowRunId)
+ throws DaoException {
+ try {
+ // Select all T2Reference lists for this wf run
+ Query selectQuery = sessionFactory.getCurrentSession().createQuery(
+ GET_REFLISTS_FOR_RUN);
+ selectQuery.setString("workflow_run_id", workflowRunId);
+ List<IdentifiedList<T2Reference>> referenceLists = selectQuery
+ .list();
+ for (IdentifiedList<T2Reference> referenceList : referenceLists)
+ delete(referenceList);
+ } catch (Exception ex) {
+ throw new DaoException(ex);
+ }
+ }
+}