# GovernanceArtifactManager.java

    /*
     *  Copyright (c) 2005-2009, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  WSO2 Inc. 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.wso2.carbon.governance.api.common;

    import org.apache.axiom.om.OMAbstractFactory;
    import org.apache.axiom.om.OMElement;
    import org.apache.axiom.om.OMNamespace;
    import org.apache.axiom.om.OMText;
    import org.apache.commons.lang.ArrayUtils;
    import org.apache.commons.lang.StringUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.wso2.carbon.governance.api.common.dataobjects.GovernanceArtifact;
    import org.wso2.carbon.governance.api.common.dataobjects.GovernanceArtifactImpl;
    import org.wso2.carbon.governance.api.exception.GovernanceException;
    import org.wso2.carbon.governance.api.generic.dataobjects.GenericArtifact;
    import org.wso2.carbon.governance.api.util.GovernanceArtifactConfiguration;
    import org.wso2.carbon.governance.api.util.GovernanceUtils;
    import org.wso2.carbon.registry.common.TermData;
    import org.wso2.carbon.registry.core.Association;
    import org.wso2.carbon.registry.core.Registry;
    import org.wso2.carbon.registry.core.Resource;
    import org.wso2.carbon.registry.core.exceptions.RegistryException;
    import org.wso2.carbon.registry.core.pagination.Paginate;
    import org.wso2.carbon.registry.core.utils.RegistryUtils;
    import org.wso2.carbon.registry.extensions.utils.CommonConstants;

    import javax.xml.namespace.QName;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.LinkedHashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    import java.util.UUID;

    /**
     * Base Manager Functionality which can be used by any Artifact Manager instance.
     */
    @SuppressWarnings("unused")
    public class GovernanceArtifactManager {

        private static final Log log = LogFactory.getLog(GovernanceArtifactManager.class);
        private Registry registry;
        private String mediaType;
        private String artifactNameAttribute;
        private String artifactNamespaceAttribute;
        private String artifactElementRoot;
        private String artifactElementNamespace;
        private String pathExpression;
        private String lifecycle;
        private List<Association> relationshipDefinitions;
        private List<Map> validationAttributes;

        /**
         * Constructor accepting an instance of the registry, and also details on the type of manager.
         *
         * @param registry                   the instance of the registry.
         * @param mediaType                  the media type of resources being saved or fetched.
         * @param artifactNameAttribute      the attribute that specifies the name of the artifact.
         * @param artifactNamespaceAttribute the attribute that specifies the namespace of the artifact.
         * @param artifactElementRoot        the attribute that specifies the root artifact element.
         * @param artifactElementNamespace   the attribute that specifies the artifact element's
         *                                   namespace.
         * @param pathExpression             the expression that can be used to compute where to store
         *                                   the artifact.
         * @param relationshipDefinitions    the relationship definitions for the types of associations
         *                                   that will be created when the artifact gets updated.
         */
        public GovernanceArtifactManager(Registry registry, String mediaType,
                                         String artifactNameAttribute,
                                         String artifactNamespaceAttribute, String artifactElementRoot,
                                         String artifactElementNamespace, String pathExpression,
                                         Association[] relationshipDefinitions) {
            this.registry = registry;
            this.mediaType = mediaType;
            this.artifactNameAttribute = artifactNameAttribute;
            this.artifactNamespaceAttribute = artifactNamespaceAttribute;
            this.artifactElementRoot = artifactElementRoot;
            this.artifactElementNamespace = artifactElementNamespace;
            this.pathExpression = pathExpression;
            this.relationshipDefinitions = Arrays.asList(relationshipDefinitions);
        }

        /**
         * Constructor accepting an instance of the registry, and also details on the type of manager.
         *
         * @param registry                   the instance of the registry.
         * @param mediaType                  the media type of resources being saved or fetched.
         * @param artifactNameAttribute      the attribute that specifies the name of the artifact.
         * @param artifactNamespaceAttribute the attribute that specifies the namespace of the artifact.
         * @param artifactElementRoot        the attribute that specifies the root artifact element.
         * @param artifactElementNamespace   the attribute that specifies the artifact element's
         *                                   namespace.
         * @param pathExpression             the expression that can be used to compute where to store
         *                                   the artifact.
         * @param lifecycle                  the lifecycle name which associated with the artifacts
         * @param validationAttributes       the validations for artifact attributes
         * @param relationshipDefinitions    the relationship definitions for the types of associations
         *                                   that will be created when the artifact gets updated.
         *
         */
        public GovernanceArtifactManager(Registry registry, String mediaType,
                                         String artifactNameAttribute,
                                         String artifactNamespaceAttribute, String artifactElementRoot,
                                         String artifactElementNamespace, String pathExpression,
                                         String lifecycle, List<Map> validationAttributes,
                                         Association[] relationshipDefinitions) {
            this.registry = registry;
            this.mediaType = mediaType;
            this.artifactNameAttribute = artifactNameAttribute;
            this.artifactNamespaceAttribute = artifactNamespaceAttribute;
            this.artifactElementRoot = artifactElementRoot;
            this.artifactElementNamespace = artifactElementNamespace;
            this.pathExpression = pathExpression;
            this.lifecycle = lifecycle;
            this.validationAttributes = validationAttributes;
            this.relationshipDefinitions = Arrays.asList(relationshipDefinitions);
        }

        /**
         * Creates a new artifact from the given qualified name.
         *
         * @return the artifact added.
         * @throws GovernanceException if the operation failed.
         */
        public GovernanceArtifact newGovernanceArtifact() throws GovernanceException {
            List<String> uniqueAttributes = GovernanceUtils.getUniqueAttributesNames(registry, mediaType);
            return GovernanceArtifactImpl.create(registry, UUID.randomUUID().toString(), uniqueAttributes);
        }

        /**
         * Creates a new artifact from the given content.
         *
         * @param content the artifact content.
         *
         * @return the artifact added.
         * @throws GovernanceException if the operation failed.
         */
        public GovernanceArtifact newGovernanceArtifact(OMElement content) throws GovernanceException {
            List<String> uniqueAttributes = GovernanceUtils.getUniqueAttributesNames(registry, mediaType);
            return GovernanceArtifactImpl.create(registry, UUID.randomUUID().toString(), content, uniqueAttributes);
        }

        /**
         * Adds the given artifact to the registry. Please do not use this method to update an existing
         * artifact use the update method instead. If this method is used to update an existing
         * artifact, all existing properties (such as lifecycle details) will be removed from the
         * existing artifact.
         *
         * @param artifact the artifact.
         *
         * @throws GovernanceException if the operation failed.
         */
        public void addGovernanceArtifact(GovernanceArtifact artifact) throws GovernanceException {
            // adding the attributes for name, namespace + artifact
            if (artifact.getQName() == null || artifact.getQName().getLocalPart() == null) {
                String msg = "A valid qualified name was not set for this artifact";
                log.error(msg);
                throw new GovernanceException(msg);
            }
            String artifactName = artifact.getQName().getLocalPart();
            if (artifactNameAttribute != null) {
                if (StringUtils.isNotEmpty(artifactName)) {
                    artifact.setAttributes(artifactNameAttribute, new String[]{artifactName});
                }
            }
            String namespace = artifact.getQName().getNamespaceURI();
            if (artifactNamespaceAttribute != null && StringUtils.isNotEmpty(namespace)) {
                artifact.setAttributes(artifactNamespaceAttribute, new String[]{namespace});
            } else if (artifactNamespaceAttribute != null) {
                namespace = artifact.getAttribute(artifactNamespaceAttribute);
            }
            setQName(artifact, artifactName, namespace);

            validateArtifact(artifact);

            ((GovernanceArtifactImpl)artifact).associateRegistry(registry);
            boolean succeeded = false;
            Resource resource = null;
            String path = null;
            try {
                registry.beginTransaction();
                resource = registry.newResource();

                resource.setMediaType(mediaType);
                setContent(artifact, resource);
                // the artifact will not actually stored in the tmp path.
                path = GovernanceUtils.getPathFromPathExpression(
                        pathExpression, artifact);

                if(registry.resourceExists(path)){
                    throw new GovernanceException("Governance artifact " + artifactName + " already exists at " + path);
                }

                String artifactId = artifact.getId();
                resource.setUUID(artifactId);
                registry.put(path, resource);
                String updatedPath = GovernanceUtils.getArtifactPath(registry, artifactId);
                if(updatedPath != null && !path.equals(updatedPath)){
                    path = updatedPath;
                }
                if (lifecycle != null){
                    String[] lifeCycles = lifecycle.split(",");
                    ArrayUtils.reverse(lifeCycles);
                    for (String attachingLifeCycle : lifeCycles) {
                        if (StringUtils.isNotEmpty(attachingLifeCycle)) {
                            registry.associateAspect(path, attachingLifeCycle);
                        }
                    }
                }

                ((GovernanceArtifactImpl)artifact).updatePath();
    //            artifact.setId(resource.getUUID()); //This is done to get the UUID of a existing resource.
                addRelationships(path, artifact);

                succeeded = true;
            }
            catch (RegistryException e) {
                String msg;
                if (artifact.getPath() != null) {
                    msg = "Failed to add artifact: artifact id: " + artifact.getId() +
                            ", path: " + artifact.getPath() + ". " + e.getMessage();
                } else {
                    msg = "Failed to add artifact: artifact id: " + artifact.getId() +
                            ". " + e.getMessage();
                }
                log.error(msg, e);
                throw new GovernanceException(msg, e);
            } finally {
                if (succeeded) {
                    try {
                        registry.commitTransaction();
                    } catch (RegistryException e) {
                        String msg ;
                        if (artifact.getPath() != null) {
                            msg = "Error in committing transactions. Failed to add artifact: artifact " +
                                    "id: " + artifact.getId() + ", path: " + artifact.getPath() + ".";
                        } else {
                            msg = "Error in committing transactions. Failed to add artifact: artifact " +
                                    "id: " + artifact.getId() + ".";
                        }
                        log.error(msg, e);
                    }
                } else {
                    try {
                        registry.rollbackTransaction();
                    } catch (RegistryException e) {
                        String msg =
                                "Error in rolling back transactions. Failed to add artifact: " +
                                        "artifact id: " + artifact.getId() + ", path: " +
                                        artifact.getPath() + ".";
                        log.error(msg, e);
                    }
                }
            }
        }

        private void setQName(GovernanceArtifact artifact, String artifactName, String namespace) throws GovernanceException {
            if (StringUtils.isNotEmpty(artifactNamespaceAttribute) && StringUtils.isNotEmpty(artifactNameAttribute)) {
                QName qname = new QName(namespace, artifactName);
                artifact.setQName(qname);
            } else if (StringUtils.isNotEmpty(artifactNameAttribute)) {
                QName qname = new QName(artifactName);
                artifact.setQName(qname);
            }
        }

        private void addRelationships(String path, GovernanceArtifact artifact)
                throws RegistryException {
            Map<String, AssociationInteger> typeMap =
                    new LinkedHashMap<String, AssociationInteger>();
            for (Association relationship : relationshipDefinitions) {
                String type = relationship.getAssociationType();
                String source = relationship.getSourcePath();
                String target = relationship.getDestinationPath();
                if (typeMap.containsKey(type)) {
                    AssociationInteger associationInteger = typeMap.get(type);
                    if (source == null) {
                        if (associationInteger.getInteger() < 0) {
                            associationInteger.setInteger(0);
                        }
                        for (String targetPath :
                                GovernanceUtils.getPathsFromPathExpression(target, artifact)) {
                            if (registry.resourceExists(targetPath)) {
                                associationInteger.getAssociations().add(new Association(path, targetPath, type));
                            } else {
                                if (log.isDebugEnabled()) {
                                    log.debug("Can not add association. Resource does not exist at" + targetPath);
                                }
                            }
                        }
                    } else if (target == null) {
                        if (associationInteger.getInteger() > 0) {
                            associationInteger.setInteger(0);
                        }
                        for (String sourcePath :
                                GovernanceUtils.getPathsFromPathExpression(source, artifact)) {
                            if (registry.resourceExists(sourcePath)) {
                                associationInteger.getAssociations().add(new Association(sourcePath, path, type));
                            } else {
                                if (log.isDebugEnabled()) {
                                    log.debug("Can not add association. Resource does not exist at" + sourcePath);
                                }
                            }
                        }
                    }
                } else {
                    AssociationInteger associationInteger = new AssociationInteger();
                    if (source == null) {
                        associationInteger.setInteger(1);
                        for (String targetPath :
                                GovernanceUtils.getPathsFromPathExpression(target, artifact)) {
                            if (registry.resourceExists(targetPath)) {
                                associationInteger.getAssociations().add(new Association(path, targetPath, type));
                            } else {
                                if (log.isDebugEnabled()) {
                                    log.debug("Can not add association. Resource does not exist at" + targetPath);
                                }
                            }
                        }
                    } else if (target == null) {
                        associationInteger.setInteger(-1);
                        for (String sourcePath :
                                GovernanceUtils.getPathsFromPathExpression(source, artifact)) {
                            if (registry.resourceExists(sourcePath)) {
                                associationInteger.getAssociations().add(new Association(sourcePath, path, type));
                            } else {
                                if (log.isDebugEnabled()) {
                                    log.debug("Can not add association. Resource does not exist at" + sourcePath);
                                }
                            }
                        }
                    }
                    typeMap.put(type, associationInteger);
                }
            }
            for (Map.Entry<String, AssociationInteger> e : typeMap.entrySet()) {
                AssociationInteger value = e.getValue();
                List<Association> associations = value.getAssociations();
                fixAssociations(path, e.getKey(), value.getInteger() >= 0, value.getInteger() <= 0,
                        associations.toArray(new Association[associations.size()]));
            }
        }

        private void fixAssociations(String path, String type, boolean isSource, boolean isTarget,
                                     Association[] toAdd)
                throws RegistryException {
            final String SEPARATOR = ":";
            // Get the existing association list which is related to the current operation
            Set<String> existingSet = new HashSet<String>();
            for (Association association : registry.getAllAssociations(path)) {
                if (type.equals(association.getAssociationType()) &&
                        ((isSource && association.getSourcePath().equals(path)) ||
                                (isTarget && association.getDestinationPath().equals(path)))) {
                    existingSet.add(association.getSourcePath() + SEPARATOR +
                            association.getDestinationPath() +
                            SEPARATOR + association.getAssociationType());
                }
            }

            // Get the updated association list from the projectGroup object
            Set<String> updatedSet = new HashSet<String>();
            for (Association association : toAdd) {
                updatedSet.add(association.getSourcePath() + SEPARATOR +
                        association.getDestinationPath() +
                        SEPARATOR + association.getAssociationType());
            }

            Set<String> removedAssociations = new HashSet<String>(existingSet);
            removedAssociations.removeAll(updatedSet);

            Set<String> newAssociations = new HashSet<String>(updatedSet);
            newAssociations.removeAll(existingSet);

            for (String removedAssociation : removedAssociations) {
                String[] params = removedAssociation.split(SEPARATOR);
                registry.removeAssociation(params[0], params[1], params[2]);
            }

            for (String newAssociation : newAssociations) {
                String[] params = newAssociation.split(SEPARATOR);
                registry.addAssociation(params[0], params[1], params[2]);
            }
        }

        /**
         * Updates the given artifact on the registry.
         * @param artifact the artifact.
         *
         * @throws GovernanceException if the operation failed.
         */
        public void updateGovernanceArtifact(GovernanceArtifact artifact) throws GovernanceException {
            boolean succeeded = false;
            try {
                registry.beginTransaction();

                String artifactName = artifact.getQName().getLocalPart();
                if (artifactNameAttribute != null) {
                    if (StringUtils.isNotEmpty(artifactName)) {
                        artifact.setAttributes(artifactNameAttribute, new String[]{artifactName});
                    } else {
                        artifactName = artifact.getAttribute(artifactNameAttribute);
                    }
                }
                String namespace = artifact.getQName().getNamespaceURI();
                if (artifactNamespaceAttribute != null && StringUtils.isNotEmpty(namespace)) {
                    artifact.setAttributes(artifactNamespaceAttribute, new String[]{namespace});
                } else if (artifactNamespaceAttribute != null) {
                    namespace = artifact.getAttribute(artifactNamespaceAttribute);
                }
                setQName(artifact, artifactName, namespace);
                validateArtifact(artifact);

                GovernanceArtifact oldArtifact = getGovernanceArtifact(artifact.getId());
                // first check for the old artifact and remove it.
                String oldPath = null;
                boolean needsToMove = false;
                if (oldArtifact != null) {
                    QName oldName = oldArtifact.getQName();
                    if (!oldName.equals(artifact.getQName())) {
                        String temp = oldArtifact.getPath();
                        // then it is analogue to moving the resource for the new location
                        // so just delete the old path
                        needsToMove = true;
                        //registry.delete(temp);
                    } else {
                        oldPath = oldArtifact.getPath();
                    }
                } else {
                    throw new GovernanceException("No artifact found for the artifact id :" + artifact.getId() + ".");
                }

                String artifactId =  artifact.getId();
                Resource resource = registry.newResource();
                resource.setMediaType(mediaType);
                setContent(artifact, resource);

                String path;
                if (oldPath != null) {
                    path = oldPath;
                } else {
                    path = GovernanceUtils.getPathFromPathExpression(pathExpression, artifact);
                }
                if (needsToMove){
                    if (isInbuiltMediatype()) {
                        registry.delete(oldArtifact.getPath());
                    } else if (!oldArtifact.getPath().equals(path)) {
                        registry.move(oldArtifact.getPath(), path);
                    }

                }

                if (registry.resourceExists(path)) {
                    Resource oldResource = registry.get(path);
                    Properties properties = (Properties) oldResource.getProperties().clone();

                    // first clone existing properties
                    resource.setProperties(properties);

                    // then set updated properties
                    // Stop the attributes been added as properties
                    /*String[] attributeKeys = artifact.getAttributeKeys();
                    if (attributeKeys != null) {
                        for (String aggregatedKey : attributeKeys) {
                            if (!aggregatedKey.equals(artifactNameAttribute) &&
                                    !aggregatedKey.equals(artifactNamespaceAttribute)) {
                                resource.setProperty(aggregatedKey,
                                        Arrays.asList(artifact.getAttributes(aggregatedKey)));
                            }
                        }
                    }*/

                    //persisting resource description at artifact update
                    String description = oldResource.getDescription();
                    if(description != null) {
                        resource.setDescription(description);
                    }

                    String oldContent;
                    Object content = oldResource.getContent();
                    if (content instanceof String) {
                        oldContent = (String) content;
                    } else {
                        oldContent = new String((byte[]) content);
                    }
                    String newContent;
                    content = resource.getContent();
                    if (content instanceof String) {
                        newContent = (String) content;
                    } else {
                        newContent = new String((byte[]) content);
                    }
                    if (newContent.equals(oldContent)) {
                        artifact.setId(oldResource.getUUID());
                        addRelationships(path, artifact);
                        succeeded = true;
                        return;
                    }
                }
                resource.setUUID(artifactId);
                registry.put(path, resource);
    //            artifact.setId(resource.getUUID()); //This is done to get the UUID of a existing resource.
                addRelationships(path, artifact);
                ((GovernanceArtifactImpl)artifact).updatePath(artifactId);
                succeeded = true;
            } catch (RegistryException e) {
                if (e instanceof GovernanceException) {
                    throw (GovernanceException) e;
                }
                String msg;
                if (artifact.getPath() != null) {
                    msg = "Error in updating the artifact, artifact id: " + artifact.getId() +
                            ", artifact path: " + artifact.getPath() + "." + e.getMessage() + ".";
                } else {
                    msg = "Error in updating the artifact, artifact id: " + artifact.getId() +
                            "." + e.getMessage() + ".";
                }
                log.error(msg, e);
                throw new GovernanceException(msg, e);
            } finally {
                if (succeeded) {
                    try {
                        registry.commitTransaction();
                    } catch (RegistryException e) {
                        String msg;
                        if (artifact.getPath() != null) {
                            msg = "Error in committing transactions. Update artifact failed: artifact " +
                                    "id: " + artifact.getId() + ", path: " + artifact.getPath() + ".";
                        } else {
                            msg = "Error in committing transactions. Update artifact failed: artifact " +
                                    "id: " + artifact.getId() + ".";
                        }

                        log.error(msg, e);
                    }
                } else {
                    try {
                        registry.rollbackTransaction();
                    } catch (RegistryException e) {
                        String msg =
                                "Error in rolling back transactions. Update artifact failed: " +
                                        "artifact id: " + artifact.getId() + ", path: " +
                                        artifact.getPath() + ".";
                        log.error(msg, e);
                    }
                }
            }
        }

        private boolean isInbuiltMediatype() {
            return CommonConstants.REST_SERVICE_MEDIA_TYPE.equals(mediaType) ||
                    CommonConstants.SOAP_SERVICE_MEDIA_TYPE.equals(mediaType) || CommonConstants.SERVICE_MEDIA_TYPE.equals(
                    mediaType);
        }

        /**
         * Fetches the given artifact on the registry.
         *
         * @param artifactId the identifier of the artifact.
         *
         * @return the artifact.
         * @throws GovernanceException if the operation failed.
         */
        public GovernanceArtifact getGovernanceArtifact(String artifactId) throws GovernanceException {
            return GovernanceUtils.retrieveGovernanceArtifactById(registry, artifactId);
        }

        /**
         * Removes the given artifact from the registry.
         *
         * @param artifactId the identifier of the artifact.
         *
         * @throws GovernanceException if the operation failed.
         */
        public void removeGovernanceArtifact(String artifactId) throws GovernanceException {
            GovernanceUtils.removeArtifact(registry, artifactId);
        }

        /**
         * Sets content of the given artifact to the given resource on the registry.
         *
         * @param artifact the artifact.
         * @param resource the content resource.
         * @throws GovernanceException if the operation failed.
         */
        public void setContent(Resource resource, GovernanceArtifact artifact) throws
                GovernanceException {
            this.setContent(artifact, resource);
        }

        /**
         * Sets content of the given artifact to the given resource on the registry.
         *
         * @param artifact the artifact.
         * @param resource the content resource.
         *
         * @throws GovernanceException if the operation failed.
         */
        protected void setContent(GovernanceArtifact artifact, Resource resource) throws
                GovernanceException {
            try {
                if (artifact instanceof GenericArtifact) {
                    Object content = ((GenericArtifact) artifact).getContent();
                    if (content != null) {
                        setContentAndProperties(artifact, resource, content);
                        return;
                    }
                }

                OMNamespace namespace = OMAbstractFactory.getOMFactory().createOMNamespace(artifactElementNamespace, "");
                Map<String, HashMap> mainElementMap = new HashMap<String, HashMap>();
                final String defaultUUID = UUID.randomUUID().toString();
                String[] attributeKeys = artifact.getAttributeKeys();
                if (attributeKeys != null) {
                    for (String aggregatedKey : attributeKeys) {
                        String[] keys = aggregatedKey.split("_");
                        String key = null;
                        for (int i = 0; i < keys.length; i++) {
                            key = keys[i];
                            if (mainElementMap.get(key) == null) {
                                mainElementMap.put(key, new HashMap<String, String[]>());
                            }

                            // Handing the situations where we don't have '_' in aggregatedKey
                            // and assume we hare having only one '_" in aggregatedKey and not more
                            if (keys.length > 1) {
                                break;
                            }
                        }
                        String[] attributeValues = artifact.getAttributes(aggregatedKey);
                        String elementName = keys[keys.length - 1];
                        if (keys.length > 1) {
                            mainElementMap.get(key).put(elementName, attributeValues);
                        } else {
                            mainElementMap.get(key).put(defaultUUID, attributeValues);
                        }
                    }

                }

                OMElement contentElement = OMAbstractFactory.getOMFactory().createOMElement(artifactElementRoot, namespace);

                Iterator it = mainElementMap.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry<String, HashMap> pairs = (Map.Entry)it.next();

                    Map<String, Map>  subElementMap = pairs.getValue();
                    Iterator subit = subElementMap.entrySet().iterator();
                    int size = 0;
                    boolean isOptionText =  false;
                    while (subit.hasNext()) {
                        Map.Entry<String, String[]> subpairs = (Map.Entry) subit.next();
                        if (size < subpairs.getValue().length) {
                            size = subpairs.getValue().length;
                        }
                        // TODO : This is temporary fix for option-text related issue, removing option-text next major release.
                        if (subpairs.getKey().endsWith("entry")) {
                            isOptionText = true;
                        }
                    }

                    // TODO : This is temporary fix for option-text related issue, removing option-text next major release.
                    if (isOptionText) {
                        OMElement keyElement = OMAbstractFactory.getOMFactory().createOMElement(pairs.getKey(), namespace);
                        for (int i = 0; i < size; i++) {
                            keyElement = getSubElementContent(subElementMap, namespace, defaultUUID, keyElement, i);
                        }
                        contentElement.addChild(keyElement);
                    } else {
                        for (int i = 0; i < size; i++) {
                            OMElement keyElement = OMAbstractFactory.getOMFactory().createOMElement(pairs.getKey(), namespace);
                            keyElement = getSubElementContent(subElementMap, namespace, defaultUUID, keyElement, i);
                            contentElement.addChild(keyElement);
                        }
                    }
                }
                String updatedContent = GovernanceUtils.serializeOMElement(contentElement);
                setContentAndProperties(artifact, resource, updatedContent);

            } catch (RegistryException e) {
                String msg;
                if (artifact.getPath() != null) {
                    msg = "Error in saving attributes for the artifact. id: " + artifact.getId() +
                            ", path: " + artifact.getPath() + ".";
                } else {
                    msg = "Error in saving attributes for the artifact. id: " + artifact.getId() + ".";
                }
                log.error(msg, e);
                throw new GovernanceException(msg, e);
            }
        }

        private void setContentAndProperties(GovernanceArtifact artifact, Resource resource, Object content)
                throws RegistryException {
            resource.setContent(content);
            String[] propertyKeys = artifact.getPropertyKeys();
            boolean hasSourceProperty = false;
            if (propertyKeys != null) {
                for (String propertyKey : propertyKeys) {
                    if ("resource.source".equals(propertyKey)) {
                        hasSourceProperty = true;
                    }
                    String[] propertyValues = artifact.getAttributes(propertyKey);
                    resource.setProperty(propertyKey, Arrays.asList(propertyValues));
                }
            }
            if (!hasSourceProperty) {
                //TODO ERROR
                resource.setProperty("resource.source", "remote");
            }

            // Stop the attributes been added as properties
          /*  String[] attributeKeys = artifact.getAttributeKeys();
            if (attributeKeys != null) {
                for (String aggregatedKey : attributeKeys) {
                    if (!aggregatedKey.equals(artifactNameAttribute) &&
                            !aggregatedKey.equals(artifactNamespaceAttribute)) {
                        resource.setProperty(aggregatedKey,
                                Arrays.asList(artifact.getAttributes(aggregatedKey)));
                    }
                }
            }*/
        }

        /**
         * Finds all artifacts matching the given filter criteria.
         *
         * @param criteria the filter criteria to be matched.
         *
         * @return the artifacts that match.
         * @throws GovernanceException if the operation failed.
         */
        public GovernanceArtifact[] findGovernanceArtifacts(Map<String, List<String>> criteria)
                throws GovernanceException {
            List<GovernanceArtifact> artifacts;
            artifacts = GovernanceUtils.findGovernanceArtifacts(criteria != null ? criteria :
                    Collections.<String, List<String>>emptyMap(), registry, mediaType);
            if (artifacts != null) {
                return artifacts.toArray(new GovernanceArtifact[artifacts.size()]);
            } else {
                return new GovernanceArtifact[0];
            }
        }

        /**
         * Find all possible terms and its count for the given facet field and query criteria
         * @param criteria the filter criteria to be matched
         * @param facetField field used for faceting
         * @param authRequired authorization required flag
         * @return term results
         * @throws GovernanceException
         */
        public TermData[] getTermData(Map<String, List<String>> criteria, String facetField, boolean authRequired) throws GovernanceException {
            List<TermData> termsList;
            termsList = GovernanceUtils.getTermDataList((criteria != null) ? criteria :
                    Collections.<String, List<String>>emptyMap(), facetField, mediaType, authRequired);
            if (termsList != null) {
                return termsList.toArray(new TermData[termsList.size()]);
            } else {
                return new TermData[0];
            }
        }

        /**
         * Finds and returns all GovernanceArtifacts that match the search query.
         *
         * @param query The query to search artifacts
         * @return Array of artifacts that match the query string
         * @throws GovernanceException if the operation failed
         */
        public GovernanceArtifact[] findGovernanceArtifacts(String query) throws GovernanceException {
            List<GovernanceArtifact> artifacts;
            artifacts = GovernanceUtils.findGovernanceArtifacts(query, registry, mediaType);
            if (artifacts != null) {
                return artifacts.toArray(new GovernanceArtifact[artifacts.size()]);
            } else {
                return new GovernanceArtifact[0];
            }
        }

        /**
         * Finds all artifacts matching the given filter criteria.
         *
         * @param criteria the filter criteria to be matched.
         *
         * @return the artifacts that match.
         * @throws GovernanceException if the operation failed.
         */
        public GovernanceArtifact[] findGovernanceArtifacts(GovernanceArtifactFilter criteria)
                throws GovernanceException {
            List<GovernanceArtifact> artifacts = new ArrayList<GovernanceArtifact>();
            for (GovernanceArtifact artifact : getAllGovernanceArtifacts()) {
                if (artifact != null) {
                    if (criteria.matches(artifact)) {
                        artifacts.add(artifact);
                    }
                }
            }
            return artifacts.toArray(new GovernanceArtifact[artifacts.size()]);
        }

        /**
         * Finds all artifacts of a given type on the registry.
         *
         * @return all artifacts of the given type on the registry.
         * @throws GovernanceException if the operation failed.
         */
        public GovernanceArtifact[] getAllGovernanceArtifacts() throws GovernanceException {
            List<String> paths = getPaginatedGovernanceArtifacts();
            return getGovernanceArtifacts(paths.toArray(new String[paths.size()]));
        }

        @Paginate("getPaginatedGovernanceArtifacts")
        public List<String> getPaginatedGovernanceArtifacts() throws GovernanceException {
            List<String> paths =
                    Arrays.asList(GovernanceUtils.getResultPaths(registry,
                            mediaType));
            Collections.sort(paths, new Comparator<String>() {
                public int compare(String o1, String o2) {
                    Long l1 = -1l;
                    Long l2 = -1l;

                    String temp1 = RegistryUtils.getParentPath(o1);
                    String temp2 = RegistryUtils.getParentPath(o2);
                    try {
                        l1 = Long.parseLong(
                                RegistryUtils.getResourceName(temp1));
                        l2 = Long.parseLong(
                                RegistryUtils.getResourceName(temp2));
                    } catch (NumberFormatException ignore) {

                    }

                    // First order by name
                    int result = RegistryUtils.getResourceName(temp1).compareToIgnoreCase(
                            RegistryUtils.getResourceName(temp2));
                    if (result != 0) {
                        return result;
                    }
                    // Then order by namespace
                    result = temp1.compareToIgnoreCase(temp2);
                    if (result != 0) {
                        return result;
                    }
                    // Finally by version
                    return l2.compareTo(l1);
                }
            });
            return paths;
        }

        /**
         * Finds all identifiers of the artifacts on the registry.
         *
         * @return an array of identifiers of the artifacts.
         * @throws GovernanceException if the operation failed.
         */
        public String[] getAllGovernanceArtifactIds() throws GovernanceException {
            GovernanceArtifact[] artifacts = getAllGovernanceArtifacts();
            String[] artifactIds = new String[artifacts.length];
            for (int i = 0; i < artifacts.length; i++) {
                artifactIds[i] = artifacts[i].getId();
            }
            return artifactIds;
        }

        private static class AssociationInteger {

            private List<Association> associations = new LinkedList<Association>();
            private Integer integer;

            public List<Association> getAssociations() {
                return associations;
            }

            public Integer getInteger() {
                return integer;
            }

            public void setInteger(Integer integer) {
                this.integer = integer;
            }
        }

        /**
         * Retrieve all the governance artifacts which associated with the given lifecycle
         *
         * @param lcName Name of the lifecycle
         *
         * @return GovernanceArtifact array
         * @throws GovernanceException
         */
        public GovernanceArtifact[] getAllGovernanceArtifactsByLifecycle(String lcName) throws GovernanceException {
            String[] paths = GovernanceUtils.getAllArtifactPathsByLifecycle(registry, lcName, mediaType);
            if (paths == null) {
                return new GovernanceArtifact[0];
            }
            return getGovernanceArtifacts(paths);
        }

        /**
         * Retrieve all the governance artifacts which associated with the given lifecycle in the given lifecycle state
         *
         * @param lcName  Name of the lifecycle
         * @param lcState Name of the current lifecycle state
         *
         * @return GovernanceArtifact array
         * @throws GovernanceException
         */
        public GovernanceArtifact[] getAllGovernanceArtifactsByLIfecycleStatus(String lcName, String lcState)
                throws GovernanceException {
            String[] paths = GovernanceUtils.getAllArtifactPathsByLifecycleState(registry, lcName, lcState, mediaType);
            if (paths == null) {
                return new GovernanceArtifact[0];
            }
            return getGovernanceArtifacts(paths);
        }

        private void validateArtifact(GovernanceArtifact artifact)
                throws GovernanceException{
            if(validationAttributes == null){
                return;
            }
            Map<String, Object> map;
            for (int i=0; i<validationAttributes.size(); ++i) {
                map = validationAttributes.get(i);
                String value = "";
                String prop = (String)map.get("properties");
                List<String> keys = (List<String>)map.get("keys");

                if (prop != null && "unbounded".equals(prop)) {
                    //assume there are only 1 key
                    String[] values = artifact.getAttributes((String)keys.get(0));
                    if (values != null) {
                        for (int j=0; j<values.length; ++j) {
                            if (map.containsKey("isMandatory") && (boolean)map.get("isMandatory") &&
                                (values[j] == null || "".equals(values[j]))) {
                                //return an exception to stop adding artifact
                                throw new GovernanceException((String) map.get("name") + " is a required field, " +
                                                              "Please provide a value for this parameter.");
                            }
                            if (map.containsKey("regexp") && !values[j].matches((String)map.get("regexp"))) {
                                //return an exception to stop adding artifact
                                throw new GovernanceException((String)map.get("name") + " doesn't match regex: " +
                                        (String)map.get("regexp"));
                            }
                        }
                    }
                } else {
                    for (int j=0; j<keys.size(); ++j) {
                        String v = artifact.getAttribute(keys.get(j));
                        if (j != 0) value += ":";
                        value += (v == null ? "" : v);
                    }
                    if (map.containsKey("isMandatory") && (boolean)map.get("isMandatory") &&
                        (value == null || "".equals(value))) {
                        //return an exception to stop adding artifact
                        throw new GovernanceException((String) map.get("name") + " is a required field, " +
                                                      "Please provide a value for this parameter.");
                    }
                    if (map.containsKey("regexp") && value != null && !value.equals("") &&
                        !value.matches((String)map.get("regexp"))) {
                        //return an exception to stop adding artifact
                        throw new GovernanceException((String)map.get("name") + " doesn't match regex: " +
                                (String)map.get("regexp"));
                    }
                }
            }
        }

        private OMElement getSubElementContent (Map<String, Map> subElementMap, OMNamespace namespace, String defaultUUID, OMElement keyElement, int posotion) {
            Iterator iterator = subElementMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String[]> subpairs = (Map.Entry)iterator.next();
                String value ;
                try {
                    value = subpairs.getValue()[posotion];
                } catch (Exception ex) {
                    value = null;
                }

                //We have defaultUUID for non '_' scenario in this case remove defaultUUID and append it to the
                //parent element.
                if (subpairs.getKey().equals(defaultUUID)) {
                    OMText textElement = OMAbstractFactory.getOMFactory().createOMText(value);
                    keyElement.addChild(textElement);
                } else {
                    OMElement subkeyElement = OMAbstractFactory.getOMFactory().createOMElement(subpairs.getKey(), namespace);
                    OMText textElement = OMAbstractFactory.getOMFactory().createOMText(value);
                    subkeyElement.addChild(textElement);
                    keyElement.addChild(subkeyElement);
                }
            }
            return keyElement;
        }

        /**
         * This method is used to fetch all the {@link GovernanceArtifact} for a given set of paths.
         * If there are exceptions in retrieving artifacts for a given path, we ignore those exceptions.
         * If all the paths failed due to errors, then we throw a {@link GovernanceException}
         *
         * @param paths Array of paths of the governance artifacts
         * @return Array of {@link GovernanceArtifact} for the given paths
         * @throws GovernanceException if fetching artifacts for all the given paths failed
         */
        private GovernanceArtifact[] getGovernanceArtifacts(String[] paths) throws GovernanceException {
            int errorCount = 0;

            List<GovernanceArtifact> artifactList = new ArrayList<GovernanceArtifact>();
            for (String path : paths) {
                try {
                    artifactList.add(GovernanceUtils.retrieveGovernanceArtifactByPath(registry, path));
                } catch (GovernanceException e) {
                    // We do not through any exception here. Only logging is done.
                    // We increase the error count for each error. If all the paths failed, then we throw an error
                    errorCount++;
                    log.error("Error occurred while retrieving governance artifact by path : " + path, e);
                }
            }
            if (errorCount != 0 && errorCount == paths.length) {
                // This means that all the paths have failed. So we throw an error.
                throw new GovernanceException("Error occurred while retrieving all the governance artifacts");
            }
            return artifactList.toArray(new GovernanceArtifact[artifactList.size()]);
        }

        private boolean addDefaultAttributeIfNotExists(final GovernanceArtifact artifact, Resource resource, final String artifactName) throws GovernanceException {
            GovernanceArtifact[] governanceArtifacts = searchArtifactsByGroupingAttribute(artifact, mediaType, artifactName);

            if(governanceArtifacts != null && governanceArtifacts.length == 0) {
                resource.addProperty("default", "true");
                return true;
            }

            return false;
        }

        private void addDefaultAttributeToAssociations(final GovernanceArtifact artifact) throws GovernanceException {
            try {
                if(mediaType.equals("application/vnd.wso2-soap-service+xml")) {

                    Association[] associations = registry.getAllAssociations(artifact.getPath());

                    for(Association association : associations) {
                        String destinationPath = association.getDestinationPath();
                        if(destinationPath.contains("wsdl")) {
                            String[] subPaths = destinationPath.split("/");
                            final String artifactName = subPaths[subPaths.length - 1];
                            GovernanceArtifact[] governanceArtifacts = searchArtifactsByGroupingAttribute(artifact, CommonConstants.WSDL_MEDIA_TYPE, artifactName);

                            if(governanceArtifacts != null && governanceArtifacts.length == 0) {
                                Resource wsdlResource = registry.get(destinationPath);
                                wsdlResource.addProperty("default", "true");
                                registry.put(destinationPath, wsdlResource);
                            }
                        }
                    }
                }
            } catch(RegistryException ex) {
                log.error("An error occurred while retrieving association of the resource " + artifact.getPath(), ex);
            }
        }

        private GovernanceArtifact[] searchArtifactsByGroupingAttribute(final GovernanceArtifact artifact, String mediaType, final String artifactName) throws GovernanceException {
            if(GovernanceUtils.getAttributeSearchService() == null) {
                return null;
            }

            Map<String, List<String>> listMap = new HashMap<String, List<String>>();

            GovernanceArtifactConfiguration artifactConfiguration ;
            String groupingAttribute = null ;

            try {
                artifactConfiguration = GovernanceUtils.getArtifactConfigurationByMediaType(registry, mediaType);

                if(artifactConfiguration != null) {
                    groupingAttribute = artifactConfiguration.getGroupingAttribute();
                } else {
                    log.debug("Artifact type with media type " + mediaType + " doesn't exist");
                }
            } catch(RegistryException ex) {
                log.error("An error occurred while retrieving the artifact configuration ", ex);
                return null;
            }

            if(groupingAttribute != null) {
                if(groupingAttribute.equals(CommonConstants.SERVICE_NAME_ATTRIBUTE)) {
                    listMap.put(groupingAttribute, new ArrayList<String>() {{
                        add(artifactName);
                    }});
                } else if(groupingAttribute.equals("overview_version")) {
                    listMap.put(groupingAttribute, new ArrayList<String>() {{
                        add(artifact.getAttribute("overview_version"));
                    }});
                }
            } else {
                listMap.put(CommonConstants.SERVICE_NAME_ATTRIBUTE, new ArrayList<String>() {{
                    add(artifactName);
                }});
            }

            return findGovernanceArtifacts(listMap);
        }

        /**
         * Check whether GovernanceArtifact is exists in the Registry without loading whole artifact into memory.
         * This method only work for Configurable Governance Artifacts and doe not work for Content Artifacts such
         * as WSDL, WADL, Swagger, XMLSchema etc.
         *
         * @param artifact GovernanceArtifact to check it's existence.
         * @return true or false
         * @throws GovernanceException if the operation failed.
         */
        public boolean isExists(GovernanceArtifact artifact) throws GovernanceException {
            String path = GovernanceUtils.getPathFromPathExpression(
                    pathExpression, artifact);
            try {
                return registry.resourceExists(path);
            } catch (RegistryException e) {
                throw new GovernanceException(e);
            }
        }

        public void removeGenericArtifact(GenericArtifact artifact) throws GovernanceException {
            String path = GovernanceUtils.getPathFromPathExpression(
                    pathExpression, artifact);
            if (path != null) {
                try {
                    GovernanceUtils.removeArtifactFromPath(registry, path);
                } catch (RegistryException e) {
                    throw new GovernanceException(e);
                }
            }
        }

    }