/*
* 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);
}
}
}
}