org.artificer.repository.hibernate.HibernatePersistenceManager.java Source code

Java tutorial

Introduction

Here is the source code for org.artificer.repository.hibernate.HibernatePersistenceManager.java

Source

/*
 * Copyright 2014 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.hibernate;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.artificer.common.ArtifactContent;
import org.artificer.common.ArtifactType;
import org.artificer.common.ArtificerConfig;
import org.artificer.common.ArtificerException;
import org.artificer.common.error.ArtificerConflictException;
import org.artificer.common.error.ArtificerNotFoundException;
import org.artificer.common.error.ArtificerServerException;
import org.artificer.common.ontology.ArtificerOntology;
import org.artificer.common.ontology.ArtificerOntologyClass;
import org.artificer.common.query.RelationshipType;
import org.artificer.integration.ExtensionFactory;
import org.artificer.integration.artifactbuilder.ArtifactBuilder;
import org.artificer.integration.artifactbuilder.RelationshipContext;
import org.artificer.repository.AbstractPersistenceManager;
import org.artificer.repository.ClassificationHelper;
import org.artificer.repository.hibernate.audit.ArtificerAuditEntry;
import org.artificer.repository.hibernate.audit.HibernateAuditor;
import org.artificer.repository.hibernate.data.HibernateEntityToSrampVisitor;
import org.artificer.repository.hibernate.data.SrampToHibernateEntityRelationshipsVisitor;
import org.artificer.repository.hibernate.data.SrampToHibernateEntityVisitor;
import org.artificer.repository.hibernate.entity.ArtificerArtifact;
import org.artificer.repository.hibernate.entity.ArtificerComment;
import org.artificer.repository.hibernate.entity.ArtificerDocumentArtifact;
import org.artificer.repository.hibernate.entity.ArtificerRelationship;
import org.artificer.repository.hibernate.entity.ArtificerStoredQuery;
import org.artificer.repository.hibernate.file.FileManagerFactory;
import org.hibernate.Session;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.StoredQuery;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @author Brett Meyer.
 */
public class HibernatePersistenceManager extends AbstractPersistenceManager {

    private final ClassificationHelper classificationHelper = this;

    @Override
    public void login(String username, String password) {
        // not used
    }

    @Override
    public List<BaseArtifactType> persistBatch(final List<BatchItem> items) throws ArtificerException {
        try {
            return new HibernateUtil.HibernateTask<List<BaseArtifactType>>() {
                @Override
                protected List<BaseArtifactType> doExecute(EntityManager entityManager) throws Exception {
                    HibernateRelationshipFactory relationshipFactory = new HibernateRelationshipFactory();
                    List<BaseArtifactType> rval = new ArrayList<>(items.size());
                    for (BatchItem item : items) {
                        BaseArtifactType artifact = doPersistArtifact(item.baseArtifactType, item.content,
                                relationshipFactory, entityManager);
                        rval.add(artifact);
                    }
                    return rval;
                }
            }.execute();
        } catch (ArtificerException ae) {
            throw ae;
        } catch (Throwable t) {
            throw new ArtificerServerException(t);
        }
    }

    @Override
    public BaseArtifactType persistArtifact(final BaseArtifactType srampArtifact, final ArtifactContent content)
            throws ArtificerException {
        return persistArtifact(srampArtifact, content, new HibernateRelationshipFactory());
    }

    private BaseArtifactType persistArtifact(final BaseArtifactType srampArtifact, final ArtifactContent content,
            final HibernateRelationshipFactory relationshipFactory) throws ArtificerException {
        try {
            return new HibernateUtil.HibernateTask<BaseArtifactType>() {
                @Override
                protected BaseArtifactType doExecute(EntityManager entityManager) throws Exception {
                    return doPersistArtifact(srampArtifact, content, relationshipFactory, entityManager);
                }
            }.execute();
        } catch (ArtificerException ae) {
            throw ae;
        } catch (Throwable t) {
            throw new ArtificerServerException(t);
        }
    }

    private BaseArtifactType doPersistArtifact(final BaseArtifactType srampArtifact, final ArtifactContent content,
            final HibernateRelationshipFactory relationshipFactory, EntityManager entityManager) throws Exception {
        final ArtifactType artifactType = ArtifactType.valueOf(srampArtifact);

        if (StringUtils.isBlank(srampArtifact.getUuid())) {
            srampArtifact.setUuid(UUID.randomUUID().toString());
        } else {
            // TODO: ugh -- ugly
            try {
                HibernateUtil.getArtifact(srampArtifact.getUuid(), entityManager, false);
                throw ArtificerConflictException.artifactConflict(srampArtifact.getUuid());
            } catch (ArtificerNotFoundException e) {
                // do nothing
            }
        }

        List<ArtifactBuilder> artifactBuilders = ExtensionFactory.createArtifactBuilders(srampArtifact, content);

        // First, need to run the artifact builders to both set metadata on srampArtifact, as well as create
        // the derived artifacts
        List<BaseArtifactType> derivedSrampArtifacts = new ArrayList<>();
        for (ArtifactBuilder artifactBuilder : artifactBuilders) {
            artifactBuilder.buildArtifacts(srampArtifact, content);
            derivedSrampArtifacts.addAll(artifactBuilder.getDerivedArtifacts());
        }

        // S-RAMP -> Hibernate
        ArtificerArtifact artificerArtifact = SrampToHibernateEntityVisitor.visit(srampArtifact, artifactType,
                classificationHelper);

        // documents
        if (artifactType.isDocument()) {
            ArtificerDocumentArtifact artificerDocumentArtifact = (ArtificerDocumentArtifact) artificerArtifact;
            processDocument(artificerDocumentArtifact, content);
            if (content != null) {
                // sets info on the artifact, so call prior to persisting
                FileManagerFactory.getInstance().write(artificerDocumentArtifact, content, entityManager);
            }
        }

        // persist and track the primary
        entityManager.persist(artificerArtifact);
        relationshipFactory.trackEntity(artificerArtifact.getUuid(), artificerArtifact);

        Map<BaseArtifactType, ArtificerArtifact> artificerDerivedArtifacts = new HashMap<>();
        for (BaseArtifactType derivedSrampArtifact : derivedSrampArtifacts) {
            // persist and track each derived
            ArtifactType derivedSrampArtifactType = ArtifactType.valueOf(derivedSrampArtifact);
            ArtificerArtifact artificerDerivedArtifact = SrampToHibernateEntityVisitor.visit(derivedSrampArtifact,
                    derivedSrampArtifactType, classificationHelper);

            // Handle derivation here, rather than in the relationship visitor, in case it's an *extended*
            // derived artifact (ie, no 'relatedDocument' field).
            artificerDerivedArtifact.setDerived(true);
            artificerDerivedArtifact.setDerivedFrom(artificerArtifact);
            artificerArtifact.getDerivedArtifacts().add(artificerDerivedArtifact);

            artificerDerivedArtifacts.put(derivedSrampArtifact, artificerDerivedArtifact);
            entityManager.persist(artificerDerivedArtifact);
            relationshipFactory.trackEntity(artificerDerivedArtifact.getUuid(), artificerDerivedArtifact);
        }

        // build the relationships
        RelationshipContext relationshipContext = new HibernateRelationshipContext(entityManager);
        for (ArtifactBuilder artifactBuilder : artifactBuilders) {
            artifactBuilder.buildRelationships(relationshipContext);
        }

        // S-RAMP relationships -> Hibernate relationships
        SrampToHibernateEntityRelationshipsVisitor.visit(srampArtifact, artificerArtifact, relationshipFactory,
                entityManager);
        for (BaseArtifactType derivedSrampArtifact : artificerDerivedArtifacts.keySet()) {
            ArtificerArtifact artificerDerivedArtifact = artificerDerivedArtifacts.get(derivedSrampArtifact);
            SrampToHibernateEntityRelationshipsVisitor.visit(derivedSrampArtifact, artificerDerivedArtifact,
                    relationshipFactory, entityManager);
        }

        // auditing
        if (ArtificerConfig.isAuditingEnabled()) {
            entityManager.persist(HibernateAuditor.createAddEntry(artificerArtifact));
            for (ArtificerArtifact derivedArtifact : artificerDerivedArtifacts.values()) {
                entityManager.persist(HibernateAuditor.createAddEntry(derivedArtifact));
            }
        }

        return HibernateEntityToSrampVisitor.visit(artificerArtifact, artifactType, true);
    }

    @Override
    public BaseArtifactType getArtifact(final String uuid, final ArtifactType artifactType)
            throws ArtificerException {
        return new HibernateUtil.HibernateTask<BaseArtifactType>() {
            @Override
            protected BaseArtifactType doExecute(EntityManager entityManager) throws Exception {
                try {
                    ArtificerArtifact artifact = HibernateUtil.getArtifact(uuid, entityManager, true);
                    return HibernateEntityToSrampVisitor.visit(artifact, artifactType, true);
                } catch (ArtificerNotFoundException e) {
                    return null;
                }
            }
        }.execute();
    }

    @Override
    public InputStream getArtifactContent(final String uuid, ArtifactType artifactType) throws ArtificerException {
        return new HibernateUtil.HibernateTask<InputStream>() {
            @Override
            protected InputStream doExecute(EntityManager entityManager) throws Exception {
                ArtificerDocumentArtifact artifact = (ArtificerDocumentArtifact) HibernateUtil.getArtifact(uuid,
                        entityManager, false);
                return FileManagerFactory.getInstance().read(artifact);
            }
        }.execute();
    }

    @Override
    public BaseArtifactType updateArtifact(final BaseArtifactType srampArtifact, final ArtifactType artifactType)
            throws ArtificerException {
        ArtificerArtifact artificerArtifact = new HibernateUtil.HibernateTask<ArtificerArtifact>() {
            @Override
            protected ArtificerArtifact doExecute(EntityManager entityManager) throws Exception {
                ArtificerArtifact artificerArtifact = HibernateUtil.getArtifact(srampArtifact.getUuid(),
                        entityManager, true);

                HibernateUtil.evict(ArtificerArtifact.class, artificerArtifact.getId(), entityManager);

                HibernateAuditor differ = null;
                if (ArtificerConfig.isAuditingEnabled()) {
                    differ = new HibernateAuditor(artificerArtifact);
                }

                SrampToHibernateEntityVisitor.visit(srampArtifact, artificerArtifact, artifactType,
                        classificationHelper);
                HibernateRelationshipFactory relationshipFactory = new HibernateRelationshipFactory();
                SrampToHibernateEntityRelationshipsVisitor.visit(srampArtifact, artificerArtifact,
                        relationshipFactory, entityManager);

                if (ArtificerConfig.isAuditingEnabled()) {
                    ArtificerAuditEntry auditEntry = differ.diff(artificerArtifact);
                    entityManager.persist(auditEntry);
                }

                return artificerArtifact;
            }
        }.execute();

        try {
            return HibernateEntityToSrampVisitor.visit(artificerArtifact, artifactType, true);
        } catch (ArtificerException ae) {
            throw ae;
        } catch (Throwable t) {
            throw new ArtificerServerException(t);
        }
    }

    @Override
    public BaseArtifactType addComment(final String uuid, final ArtifactType artifactType, final String text)
            throws ArtificerException {
        return new HibernateUtil.HibernateTask<BaseArtifactType>() {
            @Override
            protected BaseArtifactType doExecute(EntityManager entityManager) throws Exception {
                ArtificerArtifact artifact = HibernateUtil.getArtifact(uuid, entityManager, false);

                HibernateUtil.evict(ArtificerArtifact.class, artifact.getId(), entityManager);

                ArtificerComment comment = new ArtificerComment();
                comment.setCreatedBy(HibernateEntityFactory.user());
                comment.setText(text);
                comment.setArtifact(artifact);
                entityManager.persist(comment);

                return HibernateEntityToSrampVisitor.visit(artifact, artifactType, true);
            }
        }.execute();
    }

    @Override
    public BaseArtifactType deleteArtifact(final String uuid, final ArtifactType artifactType, final boolean force)
            throws ArtificerException {
        return new HibernateUtil.HibernateTask<BaseArtifactType>() {
            @Override
            protected BaseArtifactType doExecute(EntityManager entityManager) throws Exception {
                ArtificerArtifact artifact = HibernateUtil.getArtifact(uuid, entityManager, false);

                deleteArtifact(artifact, force, entityManager);

                for (ArtificerArtifact expandedArtifact : artifact.getExpandedArtifacts()) {
                    deleteArtifact(expandedArtifact, force, entityManager);
                }

                return HibernateEntityToSrampVisitor.visit(artifact, artifactType, true);
            }
        }.execute();
    }

    private void deleteArtifact(ArtificerArtifact artifact, boolean force, EntityManager entityManager)
            throws ArtificerException {
        List<Long> targetedArtifacts = new ArrayList<>();
        targetedArtifacts.add(artifact.getId());
        for (ArtificerArtifact derivedArtifact : artifact.getDerivedArtifacts()) {
            targetedArtifacts.add(derivedArtifact.getId());
        }

        if (force) {
            // delete all relationships targeting this artifact or its derived artifacts
            Query query = entityManager.createQuery(
                    "SELECT r FROM ArtificerRelationship r INNER JOIN r.targets ts INNER JOIN ts.target t WHERE t.id IN :targetedArtifacts");
            query.setParameter("targetedArtifacts", targetedArtifacts);
            List<ArtificerRelationship> relationships = query.getResultList();
            for (ArtificerRelationship relationship : relationships) {
                entityManager.remove(relationship);
                HibernateUtil.evict(ArtificerRelationship.class, relationship.getId(), entityManager);
            }
        } else {
            // if any non-trashed generic/modeled relationships target this artifact or its derived artifacts, exception
            Query query = entityManager.createQuery(
                    "SELECT r FROM ArtificerRelationship r INNER JOIN r.owner o INNER JOIN r.targets ts INNER JOIN ts.target t WHERE o.trashed = false AND t.id IN :targetedArtifacts AND (r.type=:type1 OR r.type=:type2)");
            query.setParameter("targetedArtifacts", targetedArtifacts);
            query.setParameter("type1", RelationshipType.GENERIC);
            query.setParameter("type2", RelationshipType.MODELED);
            if (query.getResultList().size() > 0) {
                throw ArtificerConflictException.relationshipConstraint(artifact.getUuid());
            }
        }

        artifact.setTrashed(true);
        HibernateUtil.evict(ArtificerArtifact.class, artifact.getId(), entityManager);
        if (ArtificerConfig.isAuditingEnabled()) {
            HibernateAuditor.createDeleteEntry(artifact);
        }
        for (ArtificerArtifact derivedArtifact : artifact.getDerivedArtifacts()) {
            derivedArtifact.setTrashed(true);
            HibernateUtil.evict(ArtificerArtifact.class, derivedArtifact.getId(), entityManager);
            if (ArtificerConfig.isAuditingEnabled()) {
                HibernateAuditor.createDeleteEntry(derivedArtifact);
            }
        }
    }

    @Override
    public ArtificerOntology persistOntology(final ArtificerOntology ontology) throws ArtificerException {
        if (StringUtils.isBlank(ontology.getUuid())) {
            ontology.setUuid(UUID.randomUUID().toString());
        }

        new HibernateUtil.HibernateTask<Void>() {
            @Override
            protected Void doExecute(EntityManager entityManager) throws Exception {
                // Don't trust users to properly set both sides of the association...
                for (ArtificerOntologyClass rootClass : ontology.getRootClasses()) {
                    if (rootClass.getRoot() == null) {
                        rootClass.setRoot(ontology);
                    }
                }

                entityManager.persist(ontology);
                return null;
            }
        }.execute();

        return ontology;
    }

    @Override
    public ArtificerOntology getOntology(final String uuid) throws ArtificerException {
        return new HibernateUtil.HibernateTask<ArtificerOntology>() {
            @Override
            protected ArtificerOntology doExecute(EntityManager entityManager) throws Exception {
                return HibernateUtil.getOntology(uuid, entityManager);
            }
        }.execute();
    }

    @Override
    public List<ArtificerOntology> getOntologies() throws ArtificerException {
        return new HibernateUtil.HibernateTask<List<ArtificerOntology>>() {
            @Override
            protected List<ArtificerOntology> doExecute(EntityManager entityManager) throws Exception {
                // MUST be LEFT JOIN!  When creating a new ontology from the UI, rootClasses will be empty!
                Query q = entityManager.createQuery(
                        "SELECT DISTINCT o FROM ArtificerOntology o LEFT JOIN FETCH o.rootClasses ORDER BY o.label ASC");
                // TODO: Until https://hibernate.atlassian.net/browse/HHH-1523, JOIN FETCH cannot be used in
                // conjunction with the query cache + 2LC!
                //                q.unwrap(org.hibernate.Query.class).setCacheable(true);
                return q.getResultList();
            }
        }.execute();
    }

    @Override
    public void updateOntology(final ArtificerOntology ontology) throws ArtificerException {
        new HibernateUtil.HibernateTask<Void>() {
            @Override
            protected Void doExecute(EntityManager entityManager) throws Exception {
                ArtificerOntology persistedOntology = HibernateUtil.getOntology(ontology.getUuid(), entityManager);

                HibernateUtil.evict(ArtificerOntology.class, ontology.getSurrogateId(), entityManager);

                // Set the surrogate ID
                ontology.setSurrogateId(persistedOntology.getSurrogateId());

                // Don't trust users to properly set both sides of the association...
                for (ArtificerOntologyClass rootClass : ontology.getRootClasses()) {
                    if (rootClass.getRoot() == null) {
                        rootClass.setRoot(ontology);
                    }
                }
                entityManager.merge(ontology);
                return null;
            }
        }.execute();
    }

    @Override
    public void deleteOntology(final String uuid) throws ArtificerException {
        new HibernateUtil.HibernateTask<Void>() {
            @Override
            protected Void doExecute(EntityManager entityManager) throws Exception {
                ArtificerOntology ontology = HibernateUtil.getOntology(uuid, entityManager);
                HibernateUtil.evict(ArtificerOntology.class, ontology.getSurrogateId(), entityManager);
                // Orphan removal is not honored by JPQL, so we need to manually delete using #remove.
                entityManager.remove(ontology);
                return null;
            }
        }.execute();
    }

    @Override
    public StoredQuery persistStoredQuery(final StoredQuery srampStoredQuery) throws ArtificerException {
        // Validate the name
        if (StringUtils.isBlank(srampStoredQuery.getQueryName())) {
            throw ArtificerConflictException.storedQueryConflict();
        }

        // Check if a stored query with the given name already exists.
        try {
            getStoredQuery(srampStoredQuery.getQueryName());
            throw ArtificerConflictException.storedQueryConflict(srampStoredQuery.getQueryName());
        } catch (ArtificerNotFoundException e) {
            // do nothing -- success
        }

        new HibernateUtil.HibernateTask<Void>() {
            @Override
            protected Void doExecute(EntityManager entityManager) throws Exception {
                ArtificerStoredQuery artificerStoredQuery = HibernateEntityFactory.storedQuery(srampStoredQuery);
                entityManager.persist(artificerStoredQuery);
                return null;
            }
        }.execute();
        return srampStoredQuery;
    }

    @Override
    public void updateStoredQuery(final String queryName, final StoredQuery srampStoredQuery)
            throws ArtificerException {
        new HibernateUtil.HibernateTask<Void>() {
            @Override
            protected Void doExecute(EntityManager entityManager) throws Exception {
                // The name may have changed, so we need to look it up and modify, rather than just persist
                // what we're handed.
                ArtificerStoredQuery artificerStoredQuery = HibernateUtil.getStoredQuery(queryName, entityManager);
                HibernateUtil.evict(ArtificerStoredQuery.class, artificerStoredQuery.getQueryName(), entityManager);
                HibernateEntityFactory.processStoredQuery(artificerStoredQuery, srampStoredQuery);
                entityManager.merge(artificerStoredQuery);
                return null;
            }
        }.execute();
    }

    @Override
    public StoredQuery getStoredQuery(final String queryName) throws ArtificerException {
        return new HibernateUtil.HibernateTask<StoredQuery>() {
            @Override
            protected StoredQuery doExecute(EntityManager entityManager) throws Exception {
                ArtificerStoredQuery storedQuery = HibernateUtil.getStoredQuery(queryName, entityManager);
                return HibernateEntityFactory.storedQuery(storedQuery);
            }
        }.execute();
    }

    @Override
    public List<StoredQuery> getStoredQueries() throws ArtificerException {
        return new HibernateUtil.HibernateTask<List<StoredQuery>>() {
            @Override
            protected List<StoredQuery> doExecute(EntityManager entityManager) throws Exception {
                Query q = entityManager.createQuery("FROM ArtificerStoredQuery asq ORDER BY asq.queryName ASC");
                q.unwrap(org.hibernate.Query.class).setCacheable(true);
                List<ArtificerStoredQuery> storedQueries = q.getResultList();
                return HibernateEntityFactory.storedQueries(storedQueries);
            }
        }.execute();
    }

    @Override
    public void deleteStoredQuery(final String queryName) throws ArtificerException {
        new HibernateUtil.HibernateTask<Void>() {
            @Override
            protected Void doExecute(EntityManager entityManager) throws Exception {
                // Orphan removal is not honored by JPQL, so we need to manually delete using #remove.
                ArtificerStoredQuery storedQuery = entityManager.find(ArtificerStoredQuery.class, queryName);
                if (storedQuery == null) {
                    throw ArtificerNotFoundException.storedQueryNotFound(queryName);
                }

                entityManager.remove(storedQuery);
                HibernateUtil.evict(ArtificerStoredQuery.class, storedQuery.getQueryName(), entityManager);

                return null;
            }
        }.execute();
    }

    @Override
    public void printArtifactGraph(String uuid, ArtifactType type) {

    }

    @Override
    public void startup() {
        // nothing to do
    }

    @Override
    public void shutdown() {
        // nothing to do
    }

    private void processDocument(ArtificerDocumentArtifact artificerArtifact, ArtifactContent content)
            throws Exception {
        InputStream inputStream = null;
        try {
            if (content != null) {
                artificerArtifact.setContentSize(content.getSize());
                inputStream = content.getInputStream();
                String sha1Hash = DigestUtils.shaHex(inputStream);
                artificerArtifact.setContentHash(sha1Hash);
            } else {
                artificerArtifact.setContentSize(0);
                artificerArtifact.setContentHash("");
            }
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
        }
    }
}