| /* |
| * 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.server.impl.atompub; |
| |
| import java.math.BigInteger; |
| import java.util.Collections; |
| import java.util.GregorianCalendar; |
| import java.util.List; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.chemistry.opencmis.commons.data.RepositoryCapabilities; |
| import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; |
| import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; |
| import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer; |
| import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; |
| import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges; |
| import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery; |
| import org.apache.chemistry.opencmis.commons.enums.CmisVersion; |
| import org.apache.chemistry.opencmis.commons.impl.Constants; |
| import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; |
| import org.apache.chemistry.opencmis.commons.server.CallContext; |
| import org.apache.chemistry.opencmis.commons.server.CmisService; |
| import org.apache.chemistry.opencmis.server.impl.CallContextImpl; |
| import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory; |
| |
| /** |
| * Repository Service operations. |
| */ |
| public class RepositoryService { |
| |
| /** |
| * Renders the service document. |
| */ |
| public static class GetRepositories extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert request != null; |
| assert response != null; |
| |
| // get parameters |
| repositoryId = getStringParameter(request, Constants.PARAM_REPOSITORY_ID); |
| |
| // execute |
| List<RepositoryInfo> infoDataList = null; |
| |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| if (repositoryId == null) { |
| infoDataList = service.getRepositoryInfos(null); |
| } else { |
| infoDataList = Collections.singletonList(service.getRepositoryInfo(repositoryId, null)); |
| if (context instanceof CallContextImpl) { |
| ((CallContextImpl) context).put(CallContext.REPOSITORY_ID, repositoryId); |
| } |
| } |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| |
| // set headers |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(Constants.MEDIATYPE_SERVICE); |
| |
| // write XML |
| ServiceDocument serviceDoc = new ServiceDocument(); |
| |
| serviceDoc.startDocument(response.getOutputStream(), getNamespaces(service)); |
| serviceDoc.startServiceDocument(); |
| |
| if (infoDataList != null) { |
| CmisVersion cmisVersion = context.getCmisVersion(); |
| for (RepositoryInfo infoData : infoDataList) { |
| if (infoData == null) { |
| continue; |
| } |
| |
| String repId = infoData.getId(); |
| UrlBuilder baseUrl = compileBaseUrl(request, repId); |
| |
| boolean supportsQuery = false; |
| boolean supportsUnFiling = false; |
| boolean supportsMultifiling = false; |
| boolean supportsFolderTree = false; |
| boolean supportsRootDescendants = false; |
| boolean supportsChanges = false; |
| |
| if (infoData.getCapabilities() != null) { |
| RepositoryCapabilities cap = infoData.getCapabilities(); |
| |
| if (cap.getQueryCapability() != null) { |
| supportsQuery = (cap.getQueryCapability() != CapabilityQuery.NONE); |
| } |
| |
| if (cap.isUnfilingSupported() != null) { |
| supportsUnFiling = cap.isUnfilingSupported(); |
| } |
| |
| if (cap.isMultifilingSupported() != null) { |
| supportsMultifiling = cap.isMultifilingSupported(); |
| } |
| |
| if (cap.isGetFolderTreeSupported() != null) { |
| supportsFolderTree = cap.isGetFolderTreeSupported(); |
| } |
| |
| if (cap.isGetDescendantsSupported() != null) { |
| supportsRootDescendants = cap.isGetDescendantsSupported(); |
| } |
| |
| if (cap.getChangesCapability() != null) { |
| supportsChanges = (cap.getChangesCapability() != CapabilityChanges.NONE); |
| } |
| } |
| |
| serviceDoc.startWorkspace(infoData.getId()); |
| |
| // add collections |
| |
| // - root collection |
| serviceDoc.writeCollection(compileUrl(baseUrl, RESOURCE_CHILDREN, infoData.getRootFolderId()), |
| Constants.COLLECTION_ROOT, "Root Collection", Constants.MEDIATYPE_ENTRY, |
| Constants.MEDIATYPE_CMISATOM); |
| |
| // - types collection |
| serviceDoc.writeCollection(compileUrl(baseUrl, RESOURCE_TYPES, null), Constants.COLLECTION_TYPES, |
| "Types Collection", ""); |
| |
| // - query collection |
| if (supportsQuery) { |
| serviceDoc.writeCollection(compileUrl(baseUrl, RESOURCE_QUERY, null), |
| Constants.COLLECTION_QUERY, "Query Collection", Constants.MEDIATYPE_QUERY); |
| } |
| |
| // - checked out collection collection |
| serviceDoc.writeCollection(compileUrl(baseUrl, RESOURCE_CHECKEDOUT, null), |
| Constants.COLLECTION_CHECKEDOUT, "Checked Out Collection", Constants.MEDIATYPE_CMISATOM); |
| |
| // - unfiled collection collection |
| if (supportsUnFiling || supportsMultifiling) { |
| serviceDoc.writeCollection(compileUrl(baseUrl, RESOURCE_UNFILED, null), |
| Constants.COLLECTION_UNFILED, "Unfiled Collection", Constants.MEDIATYPE_CMISATOM); |
| } |
| |
| // - bulk update collection |
| serviceDoc.writeCollection(compileUrl(baseUrl, RESOURCE_BULK_UPDATE, null), |
| Constants.COLLECTION_BULK_UPDATE, "Bulk Update Collection", Constants.MEDIATYPE_CMISATOM); |
| |
| // add repository info |
| serviceDoc.writeRepositoryInfo(infoData, cmisVersion); |
| |
| // add links |
| |
| // - types descendants |
| serviceDoc.writeLink(Constants.REP_REL_TYPEDESC, compileUrl(baseUrl, RESOURCE_TYPESDESC, null), |
| Constants.MEDIATYPE_FEED, null); |
| |
| // - folder tree |
| if (supportsFolderTree) { |
| serviceDoc.writeLink(Constants.REP_REL_FOLDERTREE, |
| compileUrl(baseUrl, RESOURCE_FOLDERTREE, infoData.getRootFolderId()), |
| Constants.MEDIATYPE_DESCENDANTS, null); |
| } |
| |
| // - root descendants |
| if (supportsRootDescendants) { |
| serviceDoc.writeLink(Constants.REP_REL_ROOTDESC, |
| compileUrl(baseUrl, RESOURCE_DESCENDANTS, infoData.getRootFolderId()), |
| Constants.MEDIATYPE_DESCENDANTS, infoData.getRootFolderId()); |
| } |
| |
| // - changes |
| if (supportsChanges) { |
| serviceDoc.writeLink(Constants.REP_REL_CHANGES, compileUrl(baseUrl, RESOURCE_CHANGES, null), |
| Constants.MEDIATYPE_FEED, null); |
| } |
| |
| // add URI templates |
| |
| // - object by id |
| String url = compileUrl(baseUrl, RESOURCE_OBJECTBYID, null) |
| + "?id={id}&filter={filter}&includeAllowableActions={includeAllowableActions}" |
| + "&includeACL={includeACL}&includePolicyIds={includePolicyIds}" |
| + "&includeRelationships={includeRelationships}&renditionFilter={renditionFilter}"; |
| serviceDoc.writeUriTemplate(url, Constants.TEMPLATE_OBJECT_BY_ID, Constants.MEDIATYPE_ENTRY); |
| |
| // - object by path |
| url = compileUrl(baseUrl, RESOURCE_OBJECTBYPATH, null) |
| + "?path={path}&filter={filter}&includeAllowableActions={includeAllowableActions}" |
| + "&includeACL={includeACL}&includePolicyIds={includePolicyIds}" |
| + "&includeRelationships={includeRelationships}&renditionFilter={renditionFilter}"; |
| serviceDoc.writeUriTemplate(url, Constants.TEMPLATE_OBJECT_BY_PATH, Constants.MEDIATYPE_ENTRY); |
| |
| // - type by id |
| url = compileUrl(baseUrl, RESOURCE_TYPE, null) + "?id={id}"; |
| serviceDoc.writeUriTemplate(url, Constants.TEMPLATE_TYPE_BY_ID, Constants.MEDIATYPE_ENTRY); |
| |
| // - query |
| if (supportsQuery) { |
| url = compileUrl(baseUrl, RESOURCE_QUERY, null) |
| + "?q={q}&searchAllVersions={searchAllVersions}&includeAllowableActions={includeAllowableActions}" |
| + "&includeRelationships={includeRelationships}&maxItems={maxItems}&skipCount={skipCount}"; |
| serviceDoc.writeUriTemplate(url, Constants.TEMPLATE_QUERY, Constants.MEDIATYPE_FEED); |
| } |
| |
| serviceDoc.endWorkspace(); |
| } |
| } |
| |
| serviceDoc.endServiceDocument(); |
| serviceDoc.endDocument(); |
| } |
| } |
| |
| /** |
| * Renders a type children collection. |
| */ |
| public static class GetTypeChildren extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert repositoryId != null; |
| assert request != null; |
| assert response != null; |
| |
| // get parameters |
| String typeId = getStringParameter(request, Constants.PARAM_TYPE_ID); |
| boolean includePropertyDefinitions = getBooleanParameter(request, Constants.PARAM_PROPERTY_DEFINITIONS, |
| false); |
| BigInteger maxItems = getBigIntegerParameter(request, Constants.PARAM_MAX_ITEMS); |
| BigInteger skipCount = getBigIntegerParameter(request, Constants.PARAM_SKIP_COUNT); |
| |
| // execute |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| TypeDefinitionList typeList = service.getTypeChildren(repositoryId, typeId, includePropertyDefinitions, |
| maxItems, skipCount, null); |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| |
| BigInteger numItems = (typeList == null ? null : typeList.getNumItems()); |
| Boolean hasMoreItems = (typeList == null ? null : typeList.hasMoreItems()); |
| |
| String parentTypeId = null; |
| String typeName = "Type Children"; |
| |
| // in order to get the parent type, we need the type definition of |
| // this type as well |
| if (typeId != null) { |
| TypeDefinition typeDefinition = service.getTypeDefinition(repositoryId, typeId, null); |
| |
| parentTypeId = (typeDefinition == null ? null : typeDefinition.getParentTypeId()); |
| typeName = (typeDefinition == null ? typeId : typeDefinition.getDisplayName()); |
| } |
| |
| // write XML |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(Constants.MEDIATYPE_FEED); |
| |
| AtomFeed feed = new AtomFeed(); |
| feed.startDocument(response.getOutputStream(), getNamespaces(service)); |
| feed.startFeed(true); |
| |
| // write basic Atom feed elements |
| feed.writeFeedElements(typeId, null, TYPE_AUTHOR, typeName, new GregorianCalendar(), null, numItems); |
| |
| // write links |
| UrlBuilder baseUrl = compileBaseUrl(request, repositoryId); |
| |
| feed.writeServiceLink(baseUrl.toString(), repositoryId); |
| |
| UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null); |
| selfLink.addParameter(Constants.PARAM_TYPE_ID, typeId); |
| selfLink.addParameter(Constants.PARAM_PROPERTY_DEFINITIONS, includePropertyDefinitions); |
| selfLink.addParameter(Constants.PARAM_MAX_ITEMS, maxItems); |
| selfLink.addParameter(Constants.PARAM_SKIP_COUNT, skipCount); |
| feed.writeSelfLink(selfLink.toString(), typeId); |
| |
| feed.writeViaLink(compileUrl(baseUrl, RESOURCE_TYPE, typeId)); |
| |
| UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null); |
| downLink.addParameter(Constants.PARAM_TYPE_ID, typeId); |
| feed.writeDownLink(downLink.toString(), Constants.MEDIATYPE_DESCENDANTS); |
| |
| if (parentTypeId != null) { |
| feed.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, parentTypeId), Constants.MEDIATYPE_ENTRY); |
| } |
| |
| // write paging links |
| UrlBuilder pagingUrl = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null); |
| pagingUrl.addParameter(Constants.PARAM_TYPE_ID, typeId); |
| pagingUrl.addParameter(Constants.PARAM_PROPERTY_DEFINITIONS, includePropertyDefinitions); |
| feed.writePagingLinks(pagingUrl, maxItems, skipCount, numItems, hasMoreItems, PAGE_SIZE); |
| |
| // write collection |
| UrlBuilder collectionUrl = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null); |
| collectionUrl.addParameter(Constants.PARAM_TYPE_ID, typeId); |
| feed.writeCollection(collectionUrl.toString(), null, "Types Collection", ""); |
| |
| // write type entries |
| if ((typeList != null) && (typeList.getList() != null)) { |
| AtomEntry entry = new AtomEntry(feed.getWriter()); |
| for (TypeDefinition type : typeList.getList()) { |
| writeTypeEntry(entry, type, null, repositoryId, baseUrl, false, context.getCmisVersion()); |
| } |
| } |
| |
| // write extensions |
| feed.writeExtensions(typeList); |
| |
| // we are done |
| feed.endFeed(); |
| feed.endDocument(); |
| } |
| } |
| |
| /** |
| * Renders a type descendants feed. |
| */ |
| public static class GetTypeDescendants extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert repositoryId != null; |
| assert request != null; |
| assert response != null; |
| |
| // get parameters |
| String typeId = getStringParameter(request, Constants.PARAM_TYPE_ID); |
| BigInteger depth = getBigIntegerParameter(request, Constants.PARAM_DEPTH); |
| boolean includePropertyDefinitions = getBooleanParameter(request, Constants.PARAM_PROPERTY_DEFINITIONS, |
| false); |
| |
| // execute |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| List<TypeDefinitionContainer> typeTree = service.getTypeDescendants(repositoryId, typeId, depth, |
| includePropertyDefinitions, null); |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| |
| String parentTypeId = null; |
| String typeName = "Type Children"; |
| |
| // in order to get the parent type, we need the type definition of |
| // this type as well |
| if (typeId != null) { |
| TypeDefinition typeDefinition = service.getTypeDefinition(repositoryId, typeId, null); |
| |
| parentTypeId = (typeDefinition == null ? null : typeDefinition.getParentTypeId()); |
| typeName = (typeDefinition == null ? typeId : typeDefinition.getDisplayName()); |
| } |
| |
| // write XML |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(Constants.MEDIATYPE_FEED); |
| |
| AtomFeed feed = new AtomFeed(); |
| feed.startDocument(response.getOutputStream(), getNamespaces(service)); |
| feed.startFeed(true); |
| |
| // write basic Atom feed elements |
| feed.writeFeedElements(typeId, null, TYPE_AUTHOR, typeName, new GregorianCalendar(), null, null); |
| |
| // write links |
| UrlBuilder baseUrl = compileBaseUrl(request, repositoryId); |
| |
| feed.writeServiceLink(baseUrl.toString(), repositoryId); |
| |
| UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null); |
| selfLink.addParameter(Constants.PARAM_TYPE_ID, typeId); |
| selfLink.addParameter(Constants.PARAM_DEPTH, depth); |
| selfLink.addParameter(Constants.PARAM_PROPERTY_DEFINITIONS, includePropertyDefinitions); |
| feed.writeSelfLink(selfLink.toString(), typeId); |
| |
| feed.writeViaLink(compileUrl(baseUrl, RESOURCE_TYPE, typeId)); |
| |
| UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null); |
| downLink.addParameter(Constants.PARAM_TYPE_ID, typeId); |
| feed.writeDownLink(downLink.toString(), Constants.MEDIATYPE_FEED); |
| |
| if (parentTypeId != null) { |
| feed.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, parentTypeId), Constants.MEDIATYPE_ENTRY); |
| } |
| |
| // write tree |
| if (typeTree != null) { |
| AtomEntry entry = new AtomEntry(feed.getWriter()); |
| |
| for (TypeDefinitionContainer container : typeTree) { |
| if ((container != null) && (container.getTypeDefinition() != null)) { |
| writeTypeEntry(entry, container.getTypeDefinition(), container.getChildren(), repositoryId, |
| baseUrl, false, context.getCmisVersion()); |
| } |
| } |
| } |
| |
| // we are done |
| feed.endFeed(); |
| feed.endDocument(); |
| } |
| } |
| |
| /** |
| * Renders a type definition. |
| */ |
| public static class GetTypeDefinition extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert repositoryId != null; |
| assert request != null; |
| assert response != null; |
| |
| // get parameters |
| String typeId = getStringParameter(request, Constants.PARAM_ID); |
| |
| // execute |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| TypeDefinition type = service.getTypeDefinition(repositoryId, typeId, null); |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| |
| // write XML |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(Constants.MEDIATYPE_ENTRY); |
| |
| AtomEntry entry = new AtomEntry(); |
| entry.startDocument(response.getOutputStream(), getNamespaces(service)); |
| writeTypeEntry(entry, type, null, repositoryId, compileBaseUrl(request, repositoryId), true, |
| context.getCmisVersion()); |
| entry.endDocument(); |
| } |
| } |
| |
| /** |
| * Creates a type. |
| */ |
| public static class CreateType extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert repositoryId != null; |
| assert request != null; |
| assert response != null; |
| |
| // parse entry |
| TempStoreOutputStreamFactory streamFactory = (TempStoreOutputStreamFactory) context |
| .get(CallContext.STREAM_FACTORY); |
| AtomEntryParser parser = new AtomEntryParser(streamFactory); |
| parser.parse(request.getInputStream()); |
| |
| // execute |
| TypeDefinition newType = null; |
| try { |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| newType = service.createType(repositoryId, parser.getTypeDefinition(), null); |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| } finally { |
| parser.release(); |
| } |
| |
| // set headers |
| UrlBuilder baseUrl = compileBaseUrl(request, repositoryId); |
| |
| response.setStatus(HttpServletResponse.SC_CREATED); |
| response.setContentType(Constants.MEDIATYPE_ENTRY); |
| response.setHeader("Location", compileUrl(baseUrl, RESOURCE_TYPE, newType.getId())); |
| |
| // write XML |
| AtomEntry entry = new AtomEntry(); |
| entry.startDocument(response.getOutputStream(), getNamespaces(service)); |
| writeTypeEntry(entry, newType, null, repositoryId, compileBaseUrl(request, repositoryId), true, |
| context.getCmisVersion()); |
| entry.endDocument(); |
| } |
| } |
| |
| /** |
| * Updates a type. |
| */ |
| public static class UpdateType extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert repositoryId != null; |
| assert request != null; |
| assert response != null; |
| |
| // parse entry |
| TempStoreOutputStreamFactory streamFactory = (TempStoreOutputStreamFactory) context |
| .get(CallContext.STREAM_FACTORY); |
| AtomEntryParser parser = new AtomEntryParser(streamFactory); |
| parser.parse(request.getInputStream()); |
| |
| // execute |
| TypeDefinition newType = null; |
| try { |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| newType = service.updateType(repositoryId, parser.getTypeDefinition(), null); |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| } finally { |
| parser.release(); |
| } |
| |
| // set headers |
| UrlBuilder baseUrl = compileBaseUrl(request, repositoryId); |
| |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(Constants.MEDIATYPE_ENTRY); |
| response.setHeader("Location", compileUrl(baseUrl, RESOURCE_TYPE, newType.getId())); |
| |
| // write XML |
| AtomEntry entry = new AtomEntry(); |
| entry.startDocument(response.getOutputStream(), getNamespaces(service)); |
| writeTypeEntry(entry, newType, null, repositoryId, compileBaseUrl(request, repositoryId), true, |
| context.getCmisVersion()); |
| entry.endDocument(); |
| } |
| } |
| |
| /** |
| * Deletes a type. |
| */ |
| public static class DeleteType extends AbstractAtomPubServiceCall { |
| public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request, |
| HttpServletResponse response) throws Exception { |
| assert context != null; |
| assert service != null; |
| assert repositoryId != null; |
| assert request != null; |
| assert response != null; |
| |
| // get parameters |
| String typeId = getStringParameter(request, Constants.PARAM_ID); |
| |
| // execute |
| if (stopBeforeService(service)) { |
| return; |
| } |
| |
| service.deleteType(repositoryId, typeId, null); |
| |
| if (stopAfterService(service)) { |
| return; |
| } |
| |
| // set headers |
| response.setStatus(HttpServletResponse.SC_NO_CONTENT); |
| } |
| } |
| } |