/*
* 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.server;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.artificer.atom.visitors.ArtifactContentTypeVisitor;
import org.artificer.common.ArtifactContent;
import org.artificer.common.ArtifactType;
import org.artificer.common.ArtifactTypeEnum;
import org.artificer.common.ArtifactVerifier;
import org.artificer.common.ArtificerConstants;
import org.artificer.common.error.ArtificerNotFoundException;
import org.artificer.common.error.ArtificerUserException;
import org.artificer.common.visitors.ArtifactVisitorHelper;
import org.artificer.events.EventProducer;
import org.artificer.events.EventProducerFactory;
import org.artificer.integration.ArchiveContext;
import org.artificer.integration.ExtensionFactory;
import org.artificer.repository.PersistenceManager;
import org.artificer.server.core.api.ArtifactService;
import org.artificer.server.i18n.Messages;
import org.artificer.server.mime.MimeTypes;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactEnum;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.DocumentArtifactType;
import javax.ejb.Remote;
import javax.ejb.Stateful;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* @author Brett Meyer.
*/
@Stateful(name = "ArtifactService")
@Remote(ArtifactService.class)
// Required so that artificer-repository-hibernate can control the transactions during EJB calls.
@TransactionManagement(TransactionManagementType.BEAN)
public class ArtifactServiceImpl extends AbstractServiceImpl implements ArtifactService {
@Override
public BaseArtifactType create(BaseArtifactType artifact) throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(artifact);
return create(artifactType, artifact);
}
@Override
public BaseArtifactType create(ArtifactType artifactType, BaseArtifactType artifact) throws Exception {
ArtifactVerifier verifier = new ArtifactVerifier(artifactType);
ArtifactVisitorHelper.visitArtifact(verifier, artifact);
verifier.throwError();
if (artifactType.isDerived()) {
throw ArtificerUserException.derivedArtifactCreate(artifactType.getArtifactType());
}
if (artifactType.isDocument()) {
throw new ArtificerUserException(Messages.i18n.format("INVALID_DOCARTY_CREATE"));
}
PersistenceManager persistenceManager = persistenceManager();
// store the content
BaseArtifactType persistedArtifact = persistenceManager.persistArtifact(artifact, null);
Set<EventProducer> eventProducers = EventProducerFactory.getEventProducers();
for (EventProducer eventProducer : eventProducers) {
eventProducer.artifactCreated(persistedArtifact);
}
return persistedArtifact;
}
@Override
public BaseArtifactType upload(String model, String type, String fileName, InputStream is)
throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(model, type, true);
return upload(artifactType, fileName, is);
}
@Override
public BaseArtifactType upload(String fileName, InputStream is) throws Exception {
return upload(null, fileName, is);
}
@Override
public BaseArtifactType upload(ArtifactType artifactType, String fileName, InputStream is)
throws Exception {
// Pick a reasonable file name if Slug is not present
if (fileName == null) {
if (artifactType.getArtifactType() == ArtifactTypeEnum.Document) {
fileName = "newartifact.bin";
} else if (artifactType.getArtifactType() == ArtifactTypeEnum.XmlDocument) {
fileName = "newartifact.xml";
} else {
fileName = "newartifact." + artifactType.getArtifactType().getModel();
}
}
ArtifactContent content = null;
ArchiveContext archiveContext = null;
try {
content = new ArtifactContent(fileName, is);
if (ExtensionFactory.isArchive(content)) {
// Artifact is an archive. Create the context used throughout the process.
archiveContext = ArchiveContext.createArchiveContext(content);
if (artifactType == null) {
// Attempt to auto-detect the archive type.
artifactType = ExtensionFactory.detect(content, archiveContext);
}
} else {
if (artifactType == null) {
// Attempt to auto-detect the artifact type.
artifactType = ExtensionFactory.detect(content);
}
}
if (artifactType == null) {
// Early exit. No detector wanted it, and we don't return general Documents.
return null;
}
if (artifactType.isDerived()) {
throw ArtificerUserException.derivedArtifactCreate(artifactType.getArtifactType());
}
if (!artifactType.isDocument()) {
throw new ArtificerUserException(Messages.i18n.format("INVALID_DOCARTY_CREATE"));
}
PersistenceManager persistenceManager = persistenceManager();
// Important to do this *after* creating ArtifactContent. Tika does not clone the InputStream!
String mimeType = MimeTypes.determineMimeType(fileName, content.getInputStream(), artifactType);
artifactType.setMimeType(mimeType);
BaseArtifactType artifact = artifactType.newArtifactInstance();
artifact.setName(fileName);
BatchCreate creates = new BatchCreate();
if (archiveContext != null) {
// If it's an archive, expand it and upload through a batch (necessary for adequate relationship processing).
// The parent UUID is necessary for the expandedFromDocument relationship.
String parentUuid = UUID.randomUUID().toString();
artifact.setUuid(parentUuid);
// Expand (building up the batch).
// Set the artifact in the context for the type detectors to use.
archiveContext.setArchiveArtifactType(artifactType);
Collection<File> subFiles = archiveContext.expand();
for (File subFile : subFiles) {
String pathInArchive = archiveContext.stripWorkDir(subFile.getAbsolutePath());
ArtifactContent subArtifactContent = new ArtifactContent(pathInArchive, subFile);
if (ExtensionFactory.allowExpansionFromArchive(subArtifactContent, archiveContext)) {
ArtifactType subArtifactType = ExtensionFactory.detect(subArtifactContent, archiveContext);
// detectors do not accept everything...
if (subArtifactType != null) {
String subMimeType = MimeTypes.determineMimeType(subFile.getName(),
subArtifactContent.getInputStream(), subArtifactType);
subArtifactType.setMimeType(subMimeType);
BaseArtifactType subArtifact = subArtifactType.newArtifactInstance();
subArtifact.setName(subFile.getName());
// set relevant properties/relationships
subArtifact.getOtherAttributes().put(
ArtificerConstants.ARTIFICER_EXPANDED_FROM_ARCHIVE_UUID_QNAME, parentUuid);
subArtifact.getOtherAttributes().put(
ArtificerConstants.ARTIFICER_EXPANDED_FROM_ARCHIVE_PATH_QNAME, pathInArchive);
creates.add(subArtifact, subArtifactContent, subArtifactContent.getPath());
}
}
}
}
// Persist the primary artifact or archive.
artifact = persistenceManager.persistArtifact(artifact, content);
doUploadEvent(artifact);
// Persist the batch, if there was one.
List<BaseArtifactType> results = creates.execute(persistenceManager);
for (BaseArtifactType result : results) {
doUploadEvent(result);
}
return artifact;
} finally {
if (content != null) {
content.cleanup();
}
if (archiveContext != null) {
archiveContext.cleanup();
}
}
}
private void doUploadEvent(BaseArtifactType artifact) {
Set<EventProducer> eventProducers = EventProducerFactory.getEventProducers();
for (EventProducer eventProducer : eventProducers) {
eventProducer.artifactCreated(artifact);
}
}
@Override
public BaseArtifactType upload(String model, String type, String fileName, byte[] contentBytes)
throws Exception {
return upload(model, type, fileName, new ByteArrayInputStream(contentBytes));
}
@Override
public BaseArtifactType upload(String fileName, byte[] contentBytes) throws Exception {
return upload(fileName, new ByteArrayInputStream(contentBytes));
}
@Override
public BaseArtifactType upload(ArtifactType artifactType, String fileName, byte[] contentBytes)
throws Exception {
return upload(artifactType, fileName, new ByteArrayInputStream(contentBytes));
}
@Override
public void updateMetaData(String model, String type, String uuid, BaseArtifactType updatedArtifact)
throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(model, type, null);
updateMetaData(artifactType, uuid, updatedArtifact);
}
@Override
public void updateMetaData(BaseArtifactType updatedArtifact) throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(updatedArtifact);
updateMetaData(artifactType, updatedArtifact.getUuid(), updatedArtifact);
}
@Override
public void updateMetaData(ArtifactType artifactType, String uuid,
BaseArtifactType updatedArtifact) throws Exception {
PersistenceManager persistenceManager = persistenceManager();
BaseArtifactType oldArtifact = persistenceManager.getArtifact(uuid, artifactType);
if (oldArtifact == null) {
throw ArtificerNotFoundException.artifactNotFound(uuid);
}
ArtifactVerifier verifier = new ArtifactVerifier(oldArtifact, artifactType);
ArtifactVisitorHelper.visitArtifact(verifier, updatedArtifact);
verifier.throwError();
updatedArtifact = persistenceManager.updateArtifact(updatedArtifact, artifactType);
Set<EventProducer> eventProducers = EventProducerFactory.getEventProducers();
for (EventProducer eventProducer : eventProducers) {
eventProducer.artifactUpdated(updatedArtifact, oldArtifact);
}
}
@Override
public BaseArtifactType addComment(ArtifactType artifactType, String uuid, String text) throws Exception {
return persistenceManager().addComment(uuid, artifactType, text);
}
@Override
public BaseArtifactType getMetaData(String model, String type, String uuid) throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(model, type, false);
return getMetaData(artifactType, uuid);
}
@Override
public BaseArtifactType getMetaData(ArtifactType artifactType, String uuid) throws Exception {
PersistenceManager persistenceManager = persistenceManager();
// Get the artifact by UUID
// TODO: The last extendedDocFix check should not be necessary. However, since we
// don't know whether or not the artifact has content prior to calling ArtifactType.valueOf, this is
// necessary. It would be better if we could somehow get the artifact without knowing the artifact type
// ahead of time (ie, purely use the JCR property).
BaseArtifactType artifact = persistenceManager.getArtifact(uuid, artifactType);
if (artifact == null || (!artifactType.getArtifactType().getApiType().equals(artifact.getArtifactType())
&& !(artifactType.getArtifactType().equals(ArtifactTypeEnum.ExtendedArtifactType) && artifact.getArtifactType().equals(BaseArtifactEnum.EXTENDED_DOCUMENT)))) {
throw ArtificerNotFoundException.artifactNotFound(uuid);
}
return artifact;
}
@Override
public InputStream getContent(String model, String type, String uuid) throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(model, type, true);
BaseArtifactType artifact = getMetaData(artifactType, uuid);
return getContent(artifactType, artifact);
}
@Override
public InputStream getContent(ArtifactType artifactType, String uuid) throws Exception {
BaseArtifactType artifact = getMetaData(artifactType, uuid);
return getContent(artifactType, artifact);
}
@Override
public InputStream getContent(ArtifactType artifactType, BaseArtifactType artifact) throws Exception {
if (!(artifact instanceof DocumentArtifactType)) {
throw ArtificerNotFoundException.contentNotFound(artifact.getUuid());
}
DocumentArtifactType documentArtifact = (DocumentArtifactType) artifact;
if (documentArtifact.getContentSize() == 0 || StringUtils.isEmpty(documentArtifact.getContentHash())) {
throw ArtificerNotFoundException.contentNotFound(artifact.getUuid());
}
PersistenceManager persistenceManager = persistenceManager();
ArtifactContentTypeVisitor ctVizzy = new ArtifactContentTypeVisitor();
ArtifactVisitorHelper.visitArtifact(ctVizzy, artifact);
javax.ws.rs.core.MediaType mediaType = ctVizzy.getContentType();
artifactType.setMimeType(mediaType.toString());
return persistenceManager.getArtifactContent(artifact.getUuid(), artifactType);
}
@Override
public byte[] getContentBytes(String model, String type, String uuid) throws Exception {
return IOUtils.toByteArray(getContent(model, type, uuid));
}
@Override
public byte[] getContentBytes(ArtifactType artifactType, String uuid) throws Exception {
return IOUtils.toByteArray(getContent(artifactType, uuid));
}
@Override
public byte[] getContentBytes(ArtifactType artifactType, BaseArtifactType artifact) throws Exception {
return IOUtils.toByteArray(getContent(artifactType, artifact));
}
@Override
public void delete(String model, String type, String uuid) throws Exception {
delete(model, type, uuid, false);
}
@Override
public void delete(String model, String type, String uuid, boolean force) throws Exception {
ArtifactType artifactType = ArtifactType.valueOf(model, type, null);
delete(artifactType, uuid, force);
}
@Override
public void delete(ArtifactType artifactType, String uuid) throws Exception {
delete(artifactType, uuid, false);
}
@Override
public void delete(ArtifactType artifactType, String uuid, boolean force) throws Exception {
if (artifactType.isDerived()) {
throw ArtificerUserException.derivedArtifactDelete(artifactType.getArtifactType());
}
PersistenceManager persistenceManager = persistenceManager();
// Delete the artifact by UUID
BaseArtifactType artifact = persistenceManager.deleteArtifact(uuid, artifactType, force);
Set<EventProducer> eventProducers = EventProducerFactory.getEventProducers();
for (EventProducer eventProducer : eventProducers) {
eventProducer.artifactDeleted(artifact);
}
}
}