blob: ceb754637d9e4fae35d7b3f373d29c6f791e3dfe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.apache.chemistry.opencmis.jcr;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Set;
import javax.jcr.Binary;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
import org.apache.chemistry.opencmis.jcr.type.JcrTypeHandlerManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Instances of this class represent a cmis:document backed by an underlying JCR
* <code>Node</code>.
*/
public abstract class JcrDocument extends JcrNode {
private static final Logger LOG = LoggerFactory.getLogger(JcrDocument.class);
public static final String MIME_UNKNOWN = "application/octet-stream";
protected JcrDocument(Node node, JcrTypeManager typeManager, PathManager pathManager,
JcrTypeHandlerManager typeHandlerManager) {
super(node, typeManager, pathManager, typeHandlerManager);
}
/**
* @return <code>true</code> iff the document is checked out
*/
public boolean isDocumentCheckedOut() {
try {
return getNode().isCheckedOut();
} catch (RepositoryException e) {
LOG.debug(e.getMessage(), e);
throw new CmisRuntimeException(e.getMessage(), e);
}
}
/**
* See CMIS 1.0 section 2.2.4.10 getContentStream
*
* @throws CmisObjectNotFoundException
* @throws CmisRuntimeException
*/
public ContentStream getContentStream() {
try {
Node contentNode = getContextNode();
Property data = contentNode.getProperty(Property.JCR_DATA);
// compile data
ContentStreamImpl result = new ContentStreamImpl();
result.setFileName(getNodeName());
result.setLength(BigInteger.valueOf(data.getLength()));
result.setMimeType(getPropertyOrElse(contentNode, Property.JCR_MIMETYPE, MIME_UNKNOWN));
result.setStream(new BufferedInputStream(data.getBinary().getStream())); // stream
// closed
// by
// consumer
return result;
} catch (PathNotFoundException e) {
LOG.debug(e.getMessage(), e);
throw new CmisObjectNotFoundException(e.getMessage(), e);
} catch (RepositoryException e) {
LOG.debug(e.getMessage(), e);
throw new CmisRuntimeException(e.getMessage(), e);
}
}
/**
* See CMIS 1.0 section 2.2.4.16 setContentStream
*
* @throws CmisStorageException
*/
public JcrNode setContentStream(ContentStream contentStream, boolean overwriteFlag) {
try {
// get content node. For version series this is *not* the same as
// the
// context node. See CMIS-438.
Node contentNode = getNode().getNode(Node.JCR_CONTENT);
Property data = contentNode.getProperty(Property.JCR_DATA);
// check overwrite
if (!overwriteFlag && data.getLength() != 0) {
throw new CmisContentAlreadyExistsException("Content already exists!");
}
JcrVersionBase jcrVersion = isVersionable() ? asVersion() : null;
boolean autoCheckout = jcrVersion != null && !jcrVersion.isCheckedOut();
if (autoCheckout) {
jcrVersion.checkout();
}
// write content, if available
Binary binary = contentStream == null || contentStream.getStream() == null ? JcrBinary.EMPTY
: new JcrBinary(new BufferedInputStream(contentStream.getStream()));
try {
contentNode.setProperty(Property.JCR_DATA, binary);
if (contentStream != null && contentStream.getMimeType() != null) {
contentNode.setProperty(Property.JCR_MIMETYPE, contentStream.getMimeType());
}
} finally {
binary.dispose();
}
contentNode.getSession().save();
if (autoCheckout) {
// auto versioning -> return new version created by checkin
return jcrVersion.checkin(null, null, "auto checkout");
} else if (jcrVersion != null) {
// the node is checked out -> return pwc.
return jcrVersion.getPwc();
} else {
// non versionable -> return this
return this;
}
} catch (RepositoryException e) {
LOG.debug(e.getMessage(), e);
throw new CmisStorageException(e.getMessage(), e);
} catch (IOException e) {
LOG.debug(e.getMessage(), e);
throw new CmisStorageException(e.getMessage(), e);
}
}
// ------------------------------------------< protected >---
/**
* @return the value of the <code>cmis:isLatestVersion</code> property
* @throws RepositoryException
*/
protected abstract boolean isLatestVersion() throws RepositoryException;
/**
* @return the value of the <code>cmis:isMajorVersion</code> property
* @throws RepositoryException
*/
protected abstract boolean isMajorVersion() throws RepositoryException;
/**
* @return the value of the <code>cmis:isLatestMajorVersion</code> property
* @throws RepositoryException
*/
protected abstract boolean isLatestMajorVersion() throws RepositoryException;
/**
* @return the value of the <code>cmis:versionLabel</code> property
* @throws RepositoryException
*/
protected abstract String getVersionLabel() throws RepositoryException;
/**
* @return the value of the <code>cmis:isVersionSeriesCheckedOut</code>
* property
* @throws RepositoryException
*/
protected abstract boolean isCheckedOut() throws RepositoryException;
/**
* @return the value of the <code>cmis:versionSeriesCheckedOutId</code>
* property
* @throws RepositoryException
*/
protected abstract String getCheckedOutId() throws RepositoryException;
/**
* @return the value of the <code>cmis:versionSeriesCheckedOutBy</code>
* property
* @throws RepositoryException
*/
protected abstract String getCheckedOutBy() throws RepositoryException;
/**
* @return the value of the <code>cmis:checkinComment</code> property
* @throws RepositoryException
*/
protected abstract String getCheckInComment() throws RepositoryException;
protected boolean getIsImmutable() {
return false;
}
@Override
protected void compileProperties(PropertiesImpl properties, Set<String> filter, ObjectInfoImpl objectInfo)
throws RepositoryException {
super.compileProperties(properties, filter, objectInfo);
objectInfo.setHasContent(true);
objectInfo.setHasParent(true);
objectInfo.setSupportsDescendants(false);
objectInfo.setSupportsFolderTree(false);
String typeId = getTypeIdInternal();
Node contextNode = getContextNode();
// mutability
addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_IMMUTABLE, getIsImmutable());
// content stream
long length = getPropertyLength(contextNode, Property.JCR_DATA);
addPropertyInteger(properties, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, length);
// mime type
String mimeType = getPropertyOrElse(contextNode, Property.JCR_MIMETYPE, MIME_UNKNOWN);
addPropertyString(properties, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE, mimeType);
objectInfo.setContentType(mimeType);
// file name
String fileName = getNodeName();
addPropertyString(properties, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, fileName);
objectInfo.setFileName(fileName);
addPropertyId(properties, typeId, filter, PropertyIds.CONTENT_STREAM_ID, getObjectId() + "/stream");
// versioning
addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_LATEST_VERSION, isLatestVersion());
addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_MAJOR_VERSION, isMajorVersion());
addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, isLatestMajorVersion());
addPropertyString(properties, typeId, filter, PropertyIds.VERSION_LABEL, getVersionLabel());
addPropertyId(properties, typeId, filter, PropertyIds.VERSION_SERIES_ID, getVersionSeriesId());
addPropertyString(properties, typeId, filter, PropertyIds.CHECKIN_COMMENT, getCheckInComment());
boolean isCheckedOut = isCheckedOut();
addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, isCheckedOut);
if (isCheckedOut) {
addPropertyId(properties, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, getCheckedOutId());
addPropertyString(properties, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, getCheckedOutBy());
}
}
@Override
protected Set<Action> compileAllowableActions(Set<Action> aas) {
Set<Action> result = super.compileAllowableActions(aas);
setAction(result, Action.CAN_GET_CONTENT_STREAM, true);
setAction(result, Action.CAN_SET_CONTENT_STREAM, true);
setAction(result, Action.CAN_DELETE_CONTENT_STREAM, true);
setAction(result, Action.CAN_GET_RENDITIONS, false);
return result;
}
@Override
protected BaseTypeId getBaseTypeId() {
return BaseTypeId.CMIS_DOCUMENT;
}
}