blob: 9f576a7fd4e269264de9a1252d45a200f1a47ac9 [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.
*
* Contributors:
* Florian Mueller
* Florent Guillaume, Nuxeo
*/
package org.apache.chemistry.opencmis.server.impl.atompub;
import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNotEmpty;
import java.math.BigInteger;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.xml.stream.XMLStreamException;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.impl.Constants;
import org.apache.chemistry.opencmis.commons.impl.ReturnVersion;
import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
import org.apache.chemistry.opencmis.commons.server.CmisService;
import org.apache.chemistry.opencmis.commons.server.LinkInfo;
import org.apache.chemistry.opencmis.commons.server.NamespaceDefinitions;
import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
import org.apache.chemistry.opencmis.commons.server.RenditionInfo;
import org.apache.chemistry.opencmis.server.shared.AbstractServiceCall;
import org.apache.chemistry.opencmis.server.shared.Dispatcher;
public abstract class AbstractAtomPubServiceCall extends AbstractServiceCall {
public static final String RESOURCE_CHILDREN = "children";
public static final String RESOURCE_DESCENDANTS = "descendants";
public static final String RESOURCE_FOLDERTREE = "foldertree";
public static final String RESOURCE_TYPE = "type";
public static final String RESOURCE_TYPES = "types";
public static final String RESOURCE_TYPESDESC = "typedesc";
public static final String RESOURCE_ENTRY = "entry";
public static final String RESOURCE_PARENT = "parent";
public static final String RESOURCE_PARENTS = "parents";
public static final String RESOURCE_VERSIONS = "versions";
public static final String RESOURCE_ALLOWABLEACIONS = "allowableactions";
public static final String RESOURCE_ACL = "acl";
public static final String RESOURCE_POLICIES = "policies";
public static final String RESOURCE_RELATIONSHIPS = "relationships";
public static final String RESOURCE_OBJECTBYID = "id";
public static final String RESOURCE_OBJECTBYPATH = "path";
public static final String RESOURCE_QUERY = "query";
public static final String RESOURCE_CHECKEDOUT = "checkedout";
public static final String RESOURCE_UNFILED = "unfiled";
public static final String RESOURCE_CHANGES = "changes";
public static final String RESOURCE_CONTENT = "content";
public static final String RESOURCE_BULK_UPDATE = "update";
public static final String REPOSITORY_PLACEHOLDER = "{repositoryId}";
public static final BigInteger PAGE_SIZE = BigInteger.valueOf(100);
public static final String TYPE_AUTHOR = "unknown";
/**
* Compiles the base URL for links, collections and templates.
*/
public UrlBuilder compileBaseUrl(HttpServletRequest request, String repositoryId) {
String baseUrl = (String) request.getAttribute(Dispatcher.BASE_URL_ATTRIBUTE);
if (baseUrl != null) {
int repIdPos = baseUrl.indexOf(REPOSITORY_PLACEHOLDER);
if (repIdPos < 0) {
return new UrlBuilder(baseUrl);
} else {
return new UrlBuilder(baseUrl.substring(0, repIdPos) + repositoryId
+ baseUrl.substring(repIdPos + REPOSITORY_PLACEHOLDER.length()));
}
}
UrlBuilder url = new UrlBuilder(request.getScheme(), request.getServerName(), request.getServerPort(), null);
url.addPath(request.getContextPath());
url.addPath(request.getServletPath());
if (repositoryId != null) {
url.addPathSegment(repositoryId);
}
return url;
}
/**
* Compiles a URL for links, collections and templates.
*/
public String compileUrl(UrlBuilder baseUrl, String resource, String id) {
return compileUrlBuilder(baseUrl, resource, id).toString();
}
/**
* Compiles a URL for links, collections and templates.
*/
public UrlBuilder compileUrlBuilder(UrlBuilder baseUrl, String resource, String id) {
UrlBuilder url = new UrlBuilder(baseUrl);
url.addPathSegment(resource);
if (id != null) {
url.addParameter("id", id);
}
return url;
}
// -------------------------------------------------------------------------
// --- namespaces ---
// -------------------------------------------------------------------------
public Map<String, String> getNamespaces(Object obj) {
if (obj instanceof NamespaceDefinitions) {
return ((NamespaceDefinitions) obj).getNamespaces();
}
return null;
}
// -------------------------------------------------------------------------
// --- entry builder ---
// -------------------------------------------------------------------------
/**
* Writes the a object entry.
*/
public void writeObjectEntry(CmisService service, AtomEntry entry, ObjectData object,
List<ObjectInFolderContainer> children, String repositoryId, String pathSegment,
String relativePathSegment, UrlBuilder baseUrl, boolean isRoot, CmisVersion cmisVersion)
throws XMLStreamException {
if (object == null) {
throw new CmisRuntimeException("Object not set!");
}
ObjectInfo info = service.getObjectInfo(repositoryId, object.getId());
if (info == null) {
throw new CmisRuntimeException("Object Info not found for: " + object.getId());
}
// start
entry.startEntry(isRoot);
// write object
String contentSrc = null;
if (info.hasContent()) {
UrlBuilder contentSrcBuilder = compileUrlBuilder(baseUrl, RESOURCE_CONTENT, info.getId());
if (info.getFileName() != null) {
contentSrcBuilder.addPathSegment(info.getFileName());
}
contentSrc = contentSrcBuilder.toString();
}
entry.writeObject(object, info, contentSrc, info.getContentType(), pathSegment, relativePathSegment,
cmisVersion);
// write links
entry.writeServiceLink(baseUrl.toString(), repositoryId);
entry.writeSelfLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()), info.getId());
entry.writeEnclosureLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()));
entry.writeEditLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()));
entry.writeDescribedByLink(compileUrl(baseUrl, RESOURCE_TYPE, info.getTypeId()));
entry.writeAllowableActionsLink(compileUrl(baseUrl, RESOURCE_ALLOWABLEACIONS, info.getId()));
if (info.hasParent()) {
if (info.getBaseType() == BaseTypeId.CMIS_FOLDER) {
entry.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENT, info.getId()), Constants.MEDIATYPE_ENTRY);
} else {
entry.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENTS, info.getId()), Constants.MEDIATYPE_FEED);
}
}
if (info.getBaseType() == BaseTypeId.CMIS_FOLDER) {
entry.writeDownLink(compileUrl(baseUrl, RESOURCE_CHILDREN, info.getId()), Constants.MEDIATYPE_FEED);
if (info.supportsDescendants()) {
entry.writeDownLink(compileUrl(baseUrl, RESOURCE_DESCENDANTS, info.getId()),
Constants.MEDIATYPE_DESCENDANTS);
}
if (info.supportsFolderTree()) {
entry.writeFolderTreeLink(compileUrl(baseUrl, RESOURCE_FOLDERTREE, info.getId()));
}
}
if (info.getVersionSeriesId() != null) {
UrlBuilder vsUrl = compileUrlBuilder(baseUrl, RESOURCE_VERSIONS, info.getId());
vsUrl.addParameter(Constants.PARAM_VERSION_SERIES_ID, info.getVersionSeriesId());
entry.writeVersionHistoryLink(vsUrl.toString());
}
if (!info.isCurrentVersion()) {
UrlBuilder cvUrl = compileUrlBuilder(baseUrl, RESOURCE_ENTRY, info.getId());
cvUrl.addParameter(Constants.PARAM_RETURN_VERSION, ReturnVersion.LATEST);
entry.writeEditLink(cvUrl.toString());
}
if (info.getBaseType() == BaseTypeId.CMIS_DOCUMENT) {
entry.writeEditMediaLink(compileUrl(baseUrl, RESOURCE_CONTENT, info.getId()), info.getContentType());
}
if (info.getWorkingCopyId() != null) {
entry.writeWorkingCopyLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getWorkingCopyId()));
}
if (info.getWorkingCopyOriginalId() != null) {
entry.writeViaLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getWorkingCopyOriginalId()));
}
if (info.getRenditionInfos() != null) {
for (RenditionInfo ri : info.getRenditionInfos()) {
UrlBuilder rurl = compileUrlBuilder(baseUrl, RESOURCE_CONTENT, info.getId());
rurl.addParameter(Constants.PARAM_STREAM_ID, ri.getId());
entry.writeAlternateLink(rurl.toString(), ri.getContenType(), ri.getKind(), ri.getTitle(),
ri.getLength());
}
}
if (info.hasAcl()) {
entry.writeAclLink(compileUrl(baseUrl, RESOURCE_ACL, info.getId()));
}
if (info.supportsPolicies()) {
entry.writePoliciesLink(compileUrl(baseUrl, RESOURCE_POLICIES, info.getId()));
}
if (info.supportsRelationships()) {
entry.writeRelationshipsLink(compileUrl(baseUrl, RESOURCE_RELATIONSHIPS, info.getId()));
}
if (info.getRelationshipSourceIds() != null) {
for (String id : info.getRelationshipSourceIds()) {
entry.writeRelationshipSourceLink(compileUrl(baseUrl, RESOURCE_ENTRY, id));
}
}
if (info.getRelationshipTargetIds() != null) {
for (String id : info.getRelationshipTargetIds()) {
entry.writeRelationshipTargetLink(compileUrl(baseUrl, RESOURCE_ENTRY, id));
}
}
if (info.getAdditionalLinks() != null) {
for (LinkInfo linkInfo : info.getAdditionalLinks()) {
entry.writeLink(linkInfo.getRel(), linkInfo.getHref(), linkInfo.getType(), linkInfo.getId());
}
}
// write children
if (isNotEmpty(children)) {
writeObjectChildren(service, entry, info, children, repositoryId, baseUrl, cmisVersion);
}
// we are done
entry.endEntry();
}
/**
* Writes the a object entry in a content changes list.
*
* Content changes objects need special treatment because some of them could
* have been deleted and an object info cannot be generated.
*/
public void writeContentChangesObjectEntry(CmisService service, AtomEntry entry, ObjectData object,
List<ObjectInFolderContainer> children, String repositoryId, String pathSegment,
String relativePathSegment, UrlBuilder baseUrl, boolean isRoot, CmisVersion cmisVersion)
throws XMLStreamException {
if (object == null) {
throw new CmisRuntimeException("Object not set!");
}
ObjectInfo info = null;
try {
info = service.getObjectInfo(repositoryId, object.getId());
} catch (Exception e) {
// ignore all exceptions
}
if (info != null) {
writeObjectEntry(service, entry, object, children, repositoryId, pathSegment, relativePathSegment, baseUrl,
isRoot, cmisVersion);
return;
}
// start delete object entry
entry.startEntry(isRoot);
// write object
entry.writeDeletedObject(object, cmisVersion);
// write links
entry.writeServiceLink(baseUrl.toString(), repositoryId);
// we are done
entry.endEntry();
}
/**
* Writes an objects entry children feed.
*/
public void writeObjectChildren(CmisService service, AtomEntry entry, ObjectInfo folderInfo,
List<ObjectInFolderContainer> children, String repositoryId, UrlBuilder baseUrl, CmisVersion cmisVersion)
throws XMLStreamException {
// start
AtomFeed feed = new AtomFeed(entry.getWriter());
feed.startChildren();
feed.startFeed(false);
// write basic Atom feed elements
feed.writeFeedElements(folderInfo.getId(), folderInfo.getAtomId(), folderInfo.getCreatedBy(),
folderInfo.getName(), folderInfo.getLastModificationDate(), null, null);
// write links
feed.writeServiceLink(baseUrl.toString(), repositoryId);
feed.writeSelfLink(compileUrl(baseUrl, RESOURCE_DESCENDANTS, folderInfo.getId()), null);
feed.writeViaLink(compileUrl(baseUrl, RESOURCE_ENTRY, folderInfo.getId()));
feed.writeDownLink(compileUrl(baseUrl, RESOURCE_CHILDREN, folderInfo.getId()), Constants.MEDIATYPE_FEED);
feed.writeDownLink(compileUrl(baseUrl, RESOURCE_FOLDERTREE, folderInfo.getId()),
Constants.MEDIATYPE_DESCENDANTS);
feed.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENTS, folderInfo.getId()), Constants.MEDIATYPE_FEED);
for (ObjectInFolderContainer container : children) {
if ((container != null) && (container.getObject() != null)) {
writeObjectEntry(service, entry, container.getObject().getObject(), container.getChildren(),
repositoryId, container.getObject().getPathSegment(), null, baseUrl, false, cmisVersion);
}
}
// we are done
feed.endFeed();
feed.endChildren();
}
/**
* Writes the a type entry.
*/
public void writeTypeEntry(AtomEntry entry, TypeDefinition type, List<TypeDefinitionContainer> children,
String repositoryId, UrlBuilder baseUrl, boolean isRoot, CmisVersion cmisVersion) throws XMLStreamException {
// start
entry.startEntry(isRoot);
// write type
entry.writeType(type, cmisVersion);
// write links
entry.writeServiceLink(baseUrl.toString(), repositoryId);
entry.writeSelfLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()), type.getId());
entry.writeEnclosureLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()));
if (type.getParentTypeId() != null) {
entry.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getParentTypeId()), Constants.MEDIATYPE_ENTRY);
}
UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null);
downLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
entry.writeDownLink(downLink.toString(), Constants.MEDIATYPE_CHILDREN);
UrlBuilder downLink2 = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null);
downLink2.addParameter(Constants.PARAM_TYPE_ID, type.getId());
entry.writeDownLink(downLink2.toString(), Constants.MEDIATYPE_DESCENDANTS);
entry.writeDescribedByLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getBaseTypeId().value()));
// write children
if (isNotEmpty(children)) {
writeTypeChildren(entry, type, children, repositoryId, baseUrl, cmisVersion);
}
// we are done
entry.endEntry();
}
/**
* Writes the a type entry children feed.
*/
private void writeTypeChildren(AtomEntry entry, TypeDefinition type, List<TypeDefinitionContainer> children,
String repositoryId, UrlBuilder baseUrl, CmisVersion cmisVersion) throws XMLStreamException {
// start
AtomFeed feed = new AtomFeed(entry.getWriter());
feed.startChildren();
feed.startFeed(false);
// write basic Atom feed elements
feed.writeFeedElements(type.getId(), null, TYPE_AUTHOR, type.getDisplayName(), new GregorianCalendar(), null,
null);
feed.writeServiceLink(baseUrl.toString(), repositoryId);
UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null);
selfLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
feed.writeSelfLink(selfLink.toString(), type.getId());
feed.writeViaLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()));
UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null);
downLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
feed.writeDownLink(downLink.toString(), Constants.MEDIATYPE_FEED);
if (type.getParentTypeId() != null) {
feed.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getParentTypeId()), Constants.MEDIATYPE_ENTRY);
}
// write tree
for (TypeDefinitionContainer container : children) {
if ((container != null) && (container.getTypeDefinition() != null)) {
writeTypeEntry(entry, container.getTypeDefinition(), container.getChildren(), repositoryId, baseUrl,
false, cmisVersion);
}
}
// write extensions
feed.writeExtensions(type);
// we are done
feed.endFeed();
feed.endChildren();
}
}