org.artificer.repository.jcr.mapper.ArtifactToJCRNodeVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.artificer.repository.jcr.mapper.ArtifactToJCRNodeVisitor.java

Source

/*
 * Copyright 2012 JBoss Inc
 *
 * Licensed 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.artificer.repository.jcr.mapper;

import org.apache.commons.lang.StringUtils;
import org.artificer.repository.jcr.JCRConstants;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.*;
import org.artificer.common.ArtifactType;
import org.artificer.common.ArtificerException;
import org.artificer.common.visitors.HierarchicalArtifactVisitor;
import org.artificer.repository.jcr.ClassificationHelper;

import javax.jcr.*;
import javax.jcr.Property;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.VersionException;
import javax.xml.namespace.QName;
import java.net.URI;
import java.util.*;
import java.util.Map.Entry;

/**
 * An artifact visitor used to update a JCR node.  This class is responsible
 * for modifying a JCR node using information found in the supplied s-ramp
 * artifact.
 *
 * @author eric.wittmann@redhat.com
 */
public class ArtifactToJCRNodeVisitor extends HierarchicalArtifactVisitor {

    private ArtifactType artifactType;
    private Node jcrNode;
    private JCRReferenceFactory referenceFactory;
    private ClassificationHelper classificationHelper;
    private boolean processRelationships = true;

    /**
     * Constructor.
      * @param artifactType the type of the artifact being persisted
     * @param jcrNode the JCR node this visitor will be updating
     * @param referenceFactory a resolver to find JCR nodes by UUID
     * @param classificationHelper helps resolve, verify, and normalize classifications
     */
    public ArtifactToJCRNodeVisitor(ArtifactType artifactType, Node jcrNode, JCRReferenceFactory referenceFactory,
            ClassificationHelper classificationHelper) {
        this.artifactType = artifactType;
        this.jcrNode = jcrNode;
        this.referenceFactory = referenceFactory;
        this.classificationHelper = classificationHelper;
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visitBase(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType)
     */
    @Override
    protected void visitBase(BaseArtifactType artifact) {
        super.visitBase(artifact);
        try {
            updateArtifactMetaData(artifact);
            updateClassifications(artifact);
            updateArtifactProperties(artifact);
            updateGenericRelationships(artifact);
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visitDerived(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.DerivedArtifactType)
     */
    @Override
    protected void visitDerived(DerivedArtifactType artifact) {
        super.visitDerived(artifact);
        try {
            if (artifact.getRelatedDocument() != null) {
                setRelationship("relatedDocument", 1, 1, false, false, artifact.getRelatedDocument(),
                        artifact.getRelatedDocument().getArtifactType().toString());
            }
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visitWsdlDerived(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.WsdlDerivedArtifactType)
     */
    @Override
    protected void visitWsdlDerived(WsdlDerivedArtifactType artifact) {
        super.visitWsdlDerived(artifact);
        try {
            this.jcrNode.setProperty(JCRConstants.SRAMP_NAMESPACE, artifact.getNamespace());

            setRelationships("extension", -1, 1, false, false, artifact.getExtension(),
                    WsdlExtensionEnum.WSDL_EXTENSION.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visitNamedWsdlDerived(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.NamedWsdlDerivedArtifactType)
     */
    @Override
    protected void visitNamedWsdlDerived(NamedWsdlDerivedArtifactType artifact) {
        super.visitNamedWsdlDerived(artifact);
        try {
            this.jcrNode.setProperty(JCRConstants.SRAMP_NC_NAME, artifact.getNCName());
        } catch (Exception e) {
            error = e;
        }
    }

    @Override
    protected void visitServiceImplementation(ServiceImplementationModelType artifact) {
        super.visitServiceImplementation(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (DocumentArtifactTarget documentation : artifact.getDocumentation()) {
                targetTypes.add(documentation.getArtifactType().toString());
            }
            setRelationships("documentation", -1, -1, false, false, artifact.getDocumentation(), targetTypes);
        } catch (Exception e) {
            error = e;
        }
    }

    @Override
    protected void visitSoa(SoaModelType artifact) {
        super.visitSoa(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (DocumentArtifactTarget documentation : artifact.getDocumentation()) {
                targetTypes.add(documentation.getArtifactType().toString());
            }
            setRelationships("documentation", -1, -1, false, false, artifact.getDocumentation(), targetTypes);
        } catch (Exception e) {
            error = e;
        }
    }

    @Override
    protected void visitElement(Element artifact) {
        super.visitElement(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (ElementTarget target : artifact.getRepresents()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("represents", -1, -1, false, false, artifact.getRepresents(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (ElementTarget target : artifact.getUses()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("uses", -1, -1, false, false, artifact.getUses(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (ServiceTarget target : artifact.getPerforms()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("performs", -1, -1, false, false, artifact.getPerforms(), targetTypes);

            if (artifact.getDirectsOrchestration() != null) {
                setRelationship("directsOrchestration", -1, 1, false, false, artifact.getDirectsOrchestration(),
                        artifact.getDirectsOrchestration().getArtifactType().toString());
            }

            if (artifact.getDirectsOrchestrationProcess() != null) {
                setRelationship("directsOrchestrationProcess", -1, 1, false, false,
                        artifact.getDirectsOrchestrationProcess(),
                        artifact.getDirectsOrchestrationProcess().getArtifactType().toString());
            }

            targetTypes = new ArrayList<String>();
            for (EventTarget target : artifact.getGenerates()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("generates", -1, -1, false, false, artifact.getGenerates(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (EventTarget target : artifact.getRespondsTo()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("respondsTo", -1, -1, false, false, artifact.getRespondsTo(), targetTypes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void visitActor(Actor artifact) {
        super.visitActor(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (TaskTarget target : artifact.getDoes()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("does", -1, -1, false, false, artifact.getDoes(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (PolicyTarget target : artifact.getSetsPolicy()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("setsPolicy", -1, -1, false, false, artifact.getSetsPolicy(), targetTypes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Updates the basic s-ramp meta data.
     * @param artifact
     * @throws Exception
     */
    private void updateArtifactMetaData(BaseArtifactType artifact) throws Exception {
        if (artifact.getName() != null)
            setProperty(JCRConstants.SRAMP_NAME, artifact.getName());
        else
            setProperty(JCRConstants.SRAMP_NAME, artifact.getClass().getSimpleName());
        if (artifact.getDescription() != null)
            setProperty(JCRConstants.SRAMP_DESCRIPTION, artifact.getDescription());
        if (artifact.getVersion() != null)
            setProperty("version", artifact.getVersion());
        setProperty(JCRConstants.SRAMP_DERIVED, this.artifactType.isDerived());
    }

    /**
     * Updates the classifications.
     *
     * @param artifact
     * @throws Exception
     */
    private void updateClassifications(BaseArtifactType artifact) throws Exception {
        Collection<URI> classifications = this.classificationHelper.resolveAll(artifact.getClassifiedBy());
        Collection<URI> normalizedClassifications = this.classificationHelper.normalizeAll(classifications);

        // Store the classifications
        String[] values = new String[classifications.size()];
        int idx = 0;
        for (URI classification : classifications) {
            values[idx++] = classification.toString();
        }
        setProperty(JCRConstants.SRAMP_CLASSIFIED_BY, values);

        // Store the normalized classifications
        values = new String[normalizedClassifications.size()];
        idx = 0;
        for (URI classification : normalizedClassifications) {
            values[idx++] = classification.toString();
        }
        setProperty(JCRConstants.SRAMP_NORMALIZED_CLASSIFIED_BY, values);
    }

    /**
     * Updates the custom s-ramp properties.
     * @param artifact
     * @throws Exception
     */
    private void updateArtifactProperties(BaseArtifactType artifact) throws Exception {
        Map<String, String> artifactProps = getArtifactProperties(artifact);
        Set<String> nodeProps = getNodePropertyNames(jcrNode);

        Set<String> propsToRemove = nodeProps;
        propsToRemove.removeAll(artifactProps.keySet());

        String srampPropsPrefix = JCRConstants.SRAMP_PROPERTIES + ":";

        // Remove all properties that have been earmarked for removal.
        for (String propToRemove : propsToRemove) {
            String qname = srampPropsPrefix + propToRemove;
            this.jcrNode.setProperty(qname, (Value) null);
        }

        // Set all new property values
        for (Entry<String, String> prop : artifactProps.entrySet()) {
            String name = prop.getKey();
            String qname = srampPropsPrefix + name;
            String val = prop.getValue();
            if (StringUtils.isEmpty(val)) {
                // Need to support no-value properties, but JCR will remove it if it's null.  Further, if it's an
                // empty string, the property existence query fails.  Therefore, use a placeholder that will eventually
                // be removed by JCRNodeToArtifactVisitor.
                val = JCRConstants.NO_VALUE;
            }
            setProperty(qname, val);
        }
    }

    /**
     * Updates the generic artifact relationships.
     * @param artifact
     * @throws RepositoryException
     */
    private void updateGenericRelationships(BaseArtifactType artifact) throws Exception {
        // Create/Update all the relationships included in the artifact
        Set<String> updatedRelationshipTypes = new HashSet<String>();
        for (Relationship relationship : artifact.getRelationship()) {
            setRelationships(relationship.getRelationshipType(), -1, 0, true, false,
                    relationship.getRelationshipTarget(), Collections.EMPTY_LIST,
                    relationship.getOtherAttributes());
            updatedRelationshipTypes.add(relationship.getRelationshipType());
        }

        // Now remove any relationships that weren't just updated or created (the ones
        // not included on the artifact but that have existing JCR nodes).
        NodeIterator existingNodes = this.jcrNode.getNodes();
        while (existingNodes.hasNext()) {
            Node node = existingNodes.nextNode();
            // Only remove generic relationships
            if (node.isNodeType(JCRConstants.SRAMP_RELATIONSHIP) && node.hasProperty(JCRConstants.SRAMP_GENERIC)
                    && node.getProperty(JCRConstants.SRAMP_GENERIC).getBoolean()) {
                String type = node.getProperty(JCRConstants.SRAMP_RELATIONSHIP_TYPE).getString();
                // If this relationship type was *not* updated above, then remove it because
                // it's not included in the latest artifact meta-data
                if (!updatedRelationshipTypes.contains(type)) {
                    node.remove();
                }
            }
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.ExtendedArtifactType)
     */
    @Override
    public void visit(ExtendedArtifactType artifact) {
        super.visit(artifact);
        try {
            if (artifact.getExtendedType() != null)
                setProperty(JCRConstants.SRAMP_EXTENDED_TYPE, artifact.getExtendedType());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.ExtendedDocument)
     */
    @Override
    public void visit(ExtendedDocument artifact) {
        super.visit(artifact);
        try {
            if (artifact.getExtendedType() != null)
                setProperty(JCRConstants.SRAMP_EXTENDED_TYPE, artifact.getExtendedType());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.XsdDocument)
     */
    @Override
    public void visit(XsdDocument artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_TARGET_NAMESPACE, artifact.getTargetNamespace());
            setRelationships("importedXsds", -1, 1, false, false, artifact.getImportedXsds(),
                    XsdDocumentEnum.XSD_DOCUMENT.toString());
            setRelationships("includedXsds", -1, 1, false, false, artifact.getIncludedXsds(),
                    XsdDocumentEnum.XSD_DOCUMENT.toString());
            setRelationships("redefinedXsds", -1, 1, false, false, artifact.getRedefinedXsds(),
                    XsdDocumentEnum.XSD_DOCUMENT.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.AttributeDeclaration)
     */
    @Override
    public void visit(AttributeDeclaration artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_NAMESPACE, artifact.getNamespace());
            setProperty(JCRConstants.SRAMP_NC_NAME, artifact.getNCName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.ComplexTypeDeclaration)
     */
    @Override
    public void visit(ComplexTypeDeclaration artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_NAMESPACE, artifact.getNamespace());
            setProperty(JCRConstants.SRAMP_NC_NAME, artifact.getNCName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.ElementDeclaration)
     */
    @Override
    public void visit(ElementDeclaration artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_NAMESPACE, artifact.getNamespace());
            setProperty(JCRConstants.SRAMP_NC_NAME, artifact.getNCName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.SimpleTypeDeclaration)
     */
    @Override
    public void visit(SimpleTypeDeclaration artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_NAMESPACE, artifact.getNamespace());
            setProperty(JCRConstants.SRAMP_NC_NAME, artifact.getNCName());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.WsdlDocument)
     */
    @Override
    public void visit(WsdlDocument artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_TARGET_NAMESPACE, artifact.getTargetNamespace());
            setRelationships("importedXsds", -1, 1, false, false, artifact.getImportedXsds(),
                    XsdDocumentEnum.XSD_DOCUMENT.toString());
            setRelationships("includedXsds", -1, 1, false, false, artifact.getIncludedXsds(),
                    XsdDocumentEnum.XSD_DOCUMENT.toString());
            setRelationships("redefinedXsds", -1, 1, false, false, artifact.getRedefinedXsds(),
                    XsdDocumentEnum.XSD_DOCUMENT.toString());
            setRelationships("importedWsdls", -1, 1, false, false, artifact.getImportedWsdls(),
                    WsdlDocumentEnum.WSDL_DOCUMENT.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * Message has references to all its {@link Part}s.
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Message)
     */
    @Override
    public void visit(Message artifact) {
        super.visit(artifact);
        try {
            setRelationships("part", -1, 1, false, true, artifact.getPart(), PartEnum.PART.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Part)
     */
    @Override
    public void visit(Part artifact) {
        super.visit(artifact);
        try {
            if (artifact.getElement() != null) {
                if (this.jcrNode.hasNode(JCRConstants.SRAMP_RELATIONSHIPS + ":type")) {
                    this.jcrNode.getNode(JCRConstants.SRAMP_RELATIONSHIPS + ":type").remove();
                }
                setRelationship("element", 1, 1, false, true, artifact.getElement(),
                        ElementDeclarationEnum.ELEMENT_DECLARATION.toString());
            } else if (artifact.getType() != null) {
                if (this.jcrNode.hasNode(JCRConstants.SRAMP_RELATIONSHIPS + ":element")) {
                    this.jcrNode.getNode(JCRConstants.SRAMP_RELATIONSHIPS + ":element").remove();
                }
                setRelationship("type", 1, 1, false, true, artifact.getType(), XsdTypeEnum.XSD_TYPE.toString());
            }
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.PortType)
     */
    @Override
    public void visit(PortType artifact) {
        super.visit(artifact);
        try {
            setRelationships("operation", -1, 1, false, true, artifact.getOperation(),
                    OperationEnum.OPERATION.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Operation)
     */
    @Override
    public void visit(Operation artifact) {
        super.visit(artifact);
        try {
            setRelationship("input", 1, 1, false, true, artifact.getInput(),
                    OperationInputEnum.OPERATION_INPUT.toString());
            setRelationship("output", 1, 1, false, true, artifact.getOutput(),
                    OperationOutputEnum.OPERATION_OUTPUT.toString());
            setRelationships("fault", -1, 1, false, true, artifact.getFault(), FaultEnum.FAULT.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.OperationInput)
     */
    @Override
    public void visit(OperationInput artifact) {
        super.visit(artifact);
        try {
            setRelationship("message", 1, 1, false, true, artifact.getMessage(), MessageEnum.MESSAGE.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.OperationOutput)
     */
    @Override
    public void visit(OperationOutput artifact) {
        super.visit(artifact);
        try {
            setRelationship("message", 1, 1, false, true, artifact.getMessage(), MessageEnum.MESSAGE.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Fault)
     */
    @Override
    public void visit(Fault artifact) {
        super.visit(artifact);
        try {
            setRelationship("message", 1, 1, false, true, artifact.getMessage(), MessageEnum.MESSAGE.toString());
        } catch (Exception e) {
            error = e;
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Binding)
     */
    @Override
    public void visit(Binding artifact) {
        super.visit(artifact);
        try {
            setRelationships("bindingOperation", -1, 1, false, true, artifact.getBindingOperation(),
                    BindingOperationEnum.BINDING_OPERATION.toString());
            setRelationship("portType", 1, 1, false, true, artifact.getPortType(),
                    PortTypeEnum.PORT_TYPE.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.SoapBinding)
     */
    @Override
    public void visit(SoapBinding artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_STYLE, artifact.getStyle());
            setProperty(JCRConstants.SRAMP_TRANSPORT, artifact.getTransport());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BindingOperation)
     */
    @Override
    public void visit(BindingOperation artifact) {
        super.visit(artifact);
        try {
            setRelationship("input", 1, 1, false, true, artifact.getInput(),
                    BindingOperationInputEnum.BINDING_OPERATION_INPUT.toString());
            setRelationship("output", 1, 1, false, true, artifact.getOutput(),
                    BindingOperationOutputEnum.BINDING_OPERATION_OUTPUT.toString());
            setRelationships("fault", -1, 1, false, true, artifact.getFault(),
                    BindingOperationFaultEnum.BINDING_OPERATION_FAULT.toString());
            setRelationship("operation", 1, 1, false, true, artifact.getOperation(),
                    OperationEnum.OPERATION.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.WsdlService)
     */
    @Override
    public void visit(WsdlService artifact) {
        super.visit(artifact);
        try {
            setRelationships("port", -1, 1, false, true, artifact.getPort(), PortEnum.PORT.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Port)
     */
    @Override
    public void visit(Port artifact) {
        super.visit(artifact);
        try {
            setRelationship("binding", 1, 1, false, true, artifact.getBinding(), BindingEnum.BINDING.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @see org.artificer.common.visitors.HierarchicalArtifactVisitor#visit(org.oasis_open.docs.s_ramp.ns.s_ramp_v1.SoapAddress)
     */
    @Override
    public void visit(SoapAddress artifact) {
        super.visit(artifact);
        try {
            setProperty(JCRConstants.SRAMP_SOAP_LOCATION, artifact.getSoapLocation());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(ServiceEndpoint artifact) {
        super.visit(artifact);
        try {
            if (artifact.getEndpointDefinedBy() != null) {
                setRelationship("endpointDefinedBy", -1, 1, false, false, artifact.getEndpointDefinedBy(),
                        artifact.getEndpointDefinedBy().getArtifactType().toString());
            }
            this.jcrNode.setProperty(JCRConstants.SRAMP_URL, artifact.getUrl());
            // TODO: These have to currently be added on the subclass visitors, as they're not currently
            // on ServiceImplementationModelType itself.
            this.jcrNode.setProperty(JCRConstants.SRAMP_END, artifact.getEnd());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(ServiceInstance artifact) {
        super.visit(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (BaseArtifactTarget target : artifact.getUses()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("uses", -1, -1, false, false, artifact.getUses(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (BaseArtifactTarget target : artifact.getDescribedBy()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("describedBy", -1, -1, false, false, artifact.getDescribedBy(), targetTypes);

            // TODO: These have to currently be added on the subclass visitors, as they're not currently
            // on ServiceImplementationModelType itself.
            this.jcrNode.setProperty(JCRConstants.SRAMP_END, artifact.getEnd());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(ServiceOperation artifact) {
        super.visit(artifact);
        try {
            if (artifact.getOperationDefinedBy() != null) {
                setRelationship("operationDefinedBy", -1, 1, false, false, artifact.getOperationDefinedBy(),
                        artifact.getOperationDefinedBy().getArtifactType().toString());
            }
            // TODO: These have to currently be added on the subclass visitors, as they're not currently
            // on ServiceImplementationModelType itself.
            this.jcrNode.setProperty(JCRConstants.SRAMP_END, artifact.getEnd());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(Policy artifact) {
        super.visit(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (PolicySubjectTarget target : artifact.getAppliesTo()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("appliesTo", -1, -1, false, false, artifact.getAppliesTo(), targetTypes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(ServiceInterface artifact) {
        super.visit(artifact);
        try {
            if (artifact.getInterfaceDefinedBy() != null) {
                setRelationship("interfaceDefinedBy", -1, 1, false, false, artifact.getInterfaceDefinedBy(),
                        artifact.getInterfaceDefinedBy().getArtifactType().toString());
            }

            if (artifact.getHasOperation() != null) {
                setRelationship("hasOperation", -1, 1, false, false, artifact.getHasOperation(),
                        artifact.getHasOperation().getArtifactType().toString());
            }

            List<String> targetTypes = new ArrayList<String>();
            for (InformationTypeTarget target : artifact.getHasOutput()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("hasOutput", -1, -1, false, false, artifact.getHasOutput(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (InformationTypeTarget target : artifact.getHasInput()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("hasInput", -1, -1, false, false, artifact.getHasInput(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (ServiceTarget target : artifact.getIsInterfaceOf()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("isInterfaceOf", -1, -1, false, false, artifact.getIsInterfaceOf(), targetTypes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(ServiceContract artifact) {
        super.visit(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (ActorTarget target : artifact.getInvolvesParty()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("involvesParty", -1, -1, false, false, artifact.getInvolvesParty(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (EffectTarget target : artifact.getSpecifies()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("specifies", -1, -1, false, false, artifact.getSpecifies(), targetTypes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(Organization artifact) {
        super.visit(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (ServiceImplementationModelTarget target : artifact.getProvides()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("provides", -1, -1, false, false, artifact.getProvides(), targetTypes);

            this.jcrNode.setProperty(JCRConstants.SRAMP_END, artifact.getEnd());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void visit(Service artifact) {
        super.visit(artifact);
        try {
            List<String> targetTypes = new ArrayList<String>();
            for (ServiceContractTarget target : artifact.getHasContract()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("hasContract", -1, -1, false, false, artifact.getHasContract(), targetTypes);

            targetTypes = new ArrayList<String>();
            for (ServiceInterfaceTarget target : artifact.getHasInterface()) {
                targetTypes.add(target.getArtifactType().toString());
            }
            setRelationships("hasInterface", -1, -1, false, false, artifact.getHasInterface(), targetTypes);

            if (artifact.getHasInstance() != null) {
                setRelationship("hasInstance", -1, 1, false, false, artifact.getHasInstance(),
                        artifact.getHasInstance().getArtifactType().toString());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets all of the custom properties from the artifact and returns them as a map.
     * @param artifact
     */
    private static Map<String, String> getArtifactProperties(BaseArtifactType artifact) {
        Map<String, String> props = new HashMap<String, String>();
        for (org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Property property : artifact.getProperty())
            props.put(property.getPropertyName(), property.getPropertyValue());
        return props;
    }

    /**
     * Gets all of the custom s-ramp property names currently stored on the given
     * JCR node.
     * @param jcrNode
     * @throws RepositoryException
     */
    private static Set<String> getNodePropertyNames(Node jcrNode) throws RepositoryException {
        String srampPropsPrefix = JCRConstants.SRAMP_PROPERTIES + ":";
        int srampPropsPrefixLen = srampPropsPrefix.length();

        Set<String> rval = new HashSet<String>();
        PropertyIterator properties = jcrNode.getProperties();
        while (properties.hasNext()) {
            Property prop = properties.nextProperty();
            String propName = prop.getName();
            if (propName.startsWith(srampPropsPrefix)) {
                propName = propName.substring(srampPropsPrefixLen);
                rval.add(propName);
            }
        }
        return rval;
    }

    /**
     * Sets a relationship on the given artifact parent node.
     * @param relationshipType
     * @param maxCardinality
     * @param minCardinality
     * @param isGeneric
     * @param isDerived
     * @param target
      * @param targetType
     * @throws Exception
     */
    private void setRelationship(String relationshipType, int maxCardinality, int minCardinality, boolean isGeneric,
            boolean isDerived, Target target, String targetType) throws Exception {
        if (!isProcessRelationships())
            return;
        if (target != null || minCardinality == 0) {
            Node relationshipNode = getOrCreateRelationshipNode(this.jcrNode, relationshipType, maxCardinality,
                    isGeneric, isDerived);
            if (StringUtils.isNotBlank(target.getValue())) {
                Value targetValue = this.referenceFactory.createReference(target.getValue());
                Node targetNode = getOrCreateTargetNode(relationshipNode, targetType, target.getValue(),
                        targetValue);
                setOtherAttributes(targetNode, target.getOtherAttributes());
            }
        } else {
            // If the minimum cardinality is > 0 but no targets have been provided, then
            // remove the relationship node.
            removeRelationship(relationshipType);
        }
    }

    private void setRelationships(String relationshipType, int maxCardinality, int minCardinality,
            boolean isGeneric, boolean isDerived, List<? extends Target> targets, List<String> targetTypes,
            Map<QName, String> relationshipOtherAttributes) throws Exception {
        if (!isProcessRelationships())
            return;
        if ((targets != null && targets.size() > 0) || minCardinality == 0) {
            Node relationshipNode = getOrCreateRelationshipNode(this.jcrNode, relationshipType, maxCardinality,
                    isGeneric, isDerived);
            for (int i = 0; i < targets.size(); i++) {
                Target target = targets.get(i);
                if (StringUtils.isNotBlank(target.getValue())) {
                    Value targetValue = this.referenceFactory.createReference(target.getValue());
                    String targetType = targetTypes.size() > i ? targetTypes.get(i) : null;
                    Node targetNode = getOrCreateTargetNode(relationshipNode, targetType, target.getValue(),
                            targetValue);
                    setOtherAttributes(targetNode, target.getOtherAttributes());
                }
            }

            setOtherAttributes(relationshipNode, relationshipOtherAttributes);
        } else {
            // If the minimum cardinality is > 0 but no targets have been provided, then
            // remove the relationship node.
            removeRelationship(relationshipType);
        }
    }

    private void setRelationships(String relationshipType, int maxCardinality, int minCardinality,
            boolean isGeneric, boolean isDerived, List<? extends Target> targets, List<String> targetTypes)
            throws Exception {
        setRelationships(relationshipType, maxCardinality, minCardinality, isGeneric, isDerived, targets,
                targetTypes, Collections.EMPTY_MAP);
    }

    private void setRelationships(String relationshipType, int maxCardinality, int minCardinality,
            boolean isGeneric, boolean isDerived, List<? extends Target> targets, String targetType)
            throws Exception {
        List<String> targetTypes = new ArrayList<String>();
        for (int i = 0; i < targets.size(); i++) {
            targetTypes.add(targetType);
        }
        setRelationships(relationshipType, maxCardinality, minCardinality, isGeneric, isDerived, targets,
                targetTypes);
    }

    private void setOtherAttributes(Node node, Map<QName, String> otherAttributes) throws Exception {
        // store any 'other' attributes
        String attributeKeyPrefix = JCRConstants.SRAMP_OTHER_ATTRIBUTES + ":";
        for (QName qname : otherAttributes.keySet()) {
            String attributeKey = attributeKeyPrefix + qname.toString();
            String attributeValue = otherAttributes.get(qname);
            if (StringUtils.isEmpty(attributeValue)) {
                // Need to support no-value properties, but JCR will remove it if it's null.  Further, if it's an
                // empty string, the property existence query fails.  Therefore, use a placeholder that will eventually
                // be removed by JCRNodeToArtifactVisitor.
                attributeValue = JCRConstants.NO_VALUE;
            }
            node.setProperty(attributeKey, attributeValue);
        }
    }

    /**
     * Removes the relationship of the given type from the JCR node.
     * @param relationshipType
     * @throws Exception
     */
    private void removeRelationship(String relationshipType) throws RepositoryException, VersionException,
            LockException, ConstraintViolationException, AccessDeniedException, PathNotFoundException {
        String nodeName = JCRConstants.SRAMP_RELATIONSHIPS + ":" + relationshipType;
        if (this.jcrNode.hasNode(nodeName)) {
            this.jcrNode.getNode(nodeName).remove();
        }
    }

    /**
     * Will either find and return a relationship child node, or else will create a new
     * one and return that.  The provided information is added to the created node when
     * appropriate.
     * @param parentNode
     * @param relationshipType
     * @param maxCardinality
     * @param isGeneric
     * @param isDerived
     * @throws Exception
     */
    private static Node getOrCreateRelationshipNode(Node parentNode, String relationshipType, int maxCardinality,
            boolean isGeneric, boolean isDerived) throws Exception {
        Node relationshipNode = null;
        String nodeName = JCRConstants.SRAMP_RELATIONSHIPS + ":" + relationshipType;
        if (parentNode.hasNode(nodeName)) {
            relationshipNode = parentNode.getNode(nodeName);
        } else {
            relationshipNode = parentNode.addNode(nodeName, JCRConstants.SRAMP_RELATIONSHIP);
            relationshipNode.setProperty(JCRConstants.SRAMP_RELATIONSHIP_TYPE, relationshipType);
            if (maxCardinality != -1)
                relationshipNode.setProperty(JCRConstants.SRAMP_MAX_CARDINALITY, maxCardinality);
            relationshipNode.setProperty(JCRConstants.SRAMP_GENERIC, isGeneric);
            relationshipNode.setProperty(JCRConstants.SRAMP_DERIVED, isDerived);
        }
        return relationshipNode;
    }

    private static Node getOrCreateTargetNode(Node parentNode, String targetType, String targetUuid,
            Value targetValue) throws Exception {
        Node targetNode = null;
        String nodeName = "sramp-targets:" + targetUuid;
        if (parentNode.hasNode(nodeName)) {
            targetNode = parentNode.getNode(nodeName);
        } else {
            targetNode = parentNode.addNode(nodeName, JCRConstants.SRAMP_TARGET);
            targetNode.setProperty(JCRConstants.SRAMP_TARGET_TYPE, targetType);
            targetNode.setProperty(JCRConstants.SRAMP_TARGET_ARTIFACT, targetValue);
        }
        return targetNode;
    }

    /**
     * Sets the named property.  Only sets the value if it has changed.  Call this method
     * rather than directly setting the property on the node so that auditing can work
     * properly.
     * @param propertyName
     * @param propertyValue
     * @throws RepositoryException
     * @throws PathNotFoundException
     */
    protected void setProperty(String propertyName, String propertyValue)
            throws PathNotFoundException, RepositoryException {
        if (!this.jcrNode.hasProperty(propertyName)) {
            this.jcrNode.setProperty(propertyName, propertyValue);
        } else {
            Property prop = this.jcrNode.getProperty(propertyName);
            String currentValue = prop.getValue().getString();
            if (!currentValue.equals(propertyValue)) {
                prop.setValue(propertyValue);
            }
        }
    }

    /**
     * Sets the named property.  Only sets the value if it has changed.  Call this method
     * rather than directly setting the property on the node so that auditing can work
     * properly.
     * @param propertyName
     * @param propertyValue
     * @throws RepositoryException
     * @throws PathNotFoundException
     */
    protected void setProperty(String propertyName, boolean propertyValue)
            throws PathNotFoundException, RepositoryException {
        if (!this.jcrNode.hasProperty(propertyName)) {
            this.jcrNode.setProperty(propertyName, propertyValue);
        } else {
            Property prop = this.jcrNode.getProperty(propertyName);
            boolean currentValue = prop.getValue().getBoolean();
            if (currentValue != propertyValue) {
                prop.setValue(propertyValue);
            }
        }
    }

    /**
     * Sets the named property.  Only sets the value if it has changed.  Call this method
     * rather than directly setting the property on the node so that auditing can work
     * properly.
     * @param propertyName
     * @param propertyValue
     * @throws RepositoryException
     * @throws PathNotFoundException
     */
    protected void setProperty(String propertyName, String[] propertyValue)
            throws PathNotFoundException, RepositoryException {
        if (!this.jcrNode.hasProperty(propertyName)) {
            this.jcrNode.setProperty(propertyName, propertyValue);
        } else {
            Set<String> newValues = new HashSet<String>();
            for (String v : propertyValue) {
                newValues.add(v);
            }
            Property prop = this.jcrNode.getProperty(propertyName);
            Value[] currentValue = prop.getValues();
            boolean identical = newValues.size() == currentValue.length;
            for (Value value : currentValue) {
                if (!newValues.contains(value)) {
                    identical = false;
                    break;
                }
            }
            if (!identical) {
                this.jcrNode.setProperty(propertyName, propertyValue);
            }
        }
    }

    /**
      * @return the processRelationships
      */
    public boolean isProcessRelationships() {
        return processRelationships;
    }

    /**
     * @param processRelationships the processRelationships to set
     */
    public void setProcessRelationships(boolean processRelationships) {
        this.processRelationships = processRelationships;
    }

    /**
    * Interface used by this visitor to resolve JCR node references by s-ramp
    * UUID.  In other words, given an s-ramp UUID, this interface will create a
    * reference to the appropriate JCR node.
    *
    * @author eric.wittmann@redhat.com
    */
    public static interface JCRReferenceFactory {
        /**
         * Creates a reference value to another JCR node.
           * @param uuid UUID of target artifact
         * @return a reference Value
         * @throws org.artificer.common.ArtificerException
         */
        public Value createReference(String uuid) throws ArtificerException;

        public void trackNode(String uuid, Node node);
    }

}