Return-Path: X-Original-To: apmail-chemistry-commits-archive@www.apache.org Delivered-To: apmail-chemistry-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4CD65C320 for ; Wed, 23 May 2012 15:15:24 +0000 (UTC) Received: (qmail 8536 invoked by uid 500); 23 May 2012 15:15:24 -0000 Delivered-To: apmail-chemistry-commits-archive@chemistry.apache.org Received: (qmail 8491 invoked by uid 500); 23 May 2012 15:15:24 -0000 Mailing-List: contact commits-help@chemistry.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@chemistry.apache.org Delivered-To: mailing list commits@chemistry.apache.org Received: (qmail 8482 invoked by uid 99); 23 May 2012 15:15:24 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 23 May 2012 15:15:24 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 23 May 2012 15:15:08 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 1933E2388B43; Wed, 23 May 2012 15:14:46 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1341891 [3/5] - in /chemistry/opencmis/trunk: ./ chemistry-opencmis-android/ chemistry-opencmis-android/chemistry-opencmis-android-client/ chemistry-opencmis-android/chemistry-opencmis-android-client/src/ chemistry-opencmis-android/chemist... Date: Wed, 23 May 2012 15:14:44 -0000 To: commits@chemistry.apache.org From: jmpascal@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120523151446.1933E2388B43@eris.apache.org> Added: chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/LinkCache.java URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/LinkCache.java?rev=1341891&view=auto ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/LinkCache.java (added) +++ chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/LinkCache.java Wed May 23 15:14:42 2012 @@ -0,0 +1,343 @@ +/* + * 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.client.bindings.spi.atompub; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.chemistry.opencmis.client.bindings.cache.Cache; +import org.apache.chemistry.opencmis.client.bindings.cache.impl.CacheImpl; +import org.apache.chemistry.opencmis.client.bindings.cache.impl.ContentTypeCacheLevelImpl; +import org.apache.chemistry.opencmis.client.bindings.cache.impl.LruCacheLevelImpl; +import org.apache.chemistry.opencmis.client.bindings.cache.impl.MapCacheLevelImpl; +import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession; +import org.apache.chemistry.opencmis.commons.SessionParameter; +import org.apache.chemistry.opencmis.commons.impl.Constants; +import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; + +/** + * Link cache. + */ +public class LinkCache implements Serializable { + + private static final long serialVersionUID = 1L; + + private static final Set KNOWN_LINKS = new HashSet(); + + static { + KNOWN_LINKS.add(Constants.REL_ACL); + KNOWN_LINKS.add(Constants.REL_DOWN); + KNOWN_LINKS.add(Constants.REL_UP); + KNOWN_LINKS.add(Constants.REL_FOLDERTREE); + KNOWN_LINKS.add(Constants.REL_RELATIONSHIPS); + KNOWN_LINKS.add(Constants.REL_SELF); + KNOWN_LINKS.add(Constants.REL_ALLOWABLEACTIONS); + KNOWN_LINKS.add(Constants.REL_EDITMEDIA); + KNOWN_LINKS.add(Constants.REL_POLICIES); + KNOWN_LINKS.add(Constants.REL_VERSIONHISTORY); + KNOWN_LINKS.add(Constants.REL_WORKINGCOPY); + KNOWN_LINKS.add(AtomPubParser.LINK_REL_CONTENT); + } + + private static final int CACHE_SIZE_REPOSITORIES = 10; + private static final int CACHE_SIZE_TYPES = 100; + private static final int CACHE_SIZE_OBJECTS = 400; + + private final Cache linkCache; + private final Cache typeLinkCache; + private final Cache collectionLinkCache; + private final Cache templateCache; + private final Cache repositoryLinkCache; + + /** + * Constructor. + */ + public LinkCache(BindingSession session) { + int repCount = session.get(SessionParameter.CACHE_SIZE_REPOSITORIES, CACHE_SIZE_REPOSITORIES); + if (repCount < 1) { + repCount = CACHE_SIZE_REPOSITORIES; + } + + int typeCount = session.get(SessionParameter.CACHE_SIZE_TYPES, CACHE_SIZE_TYPES); + if (typeCount < 1) { + typeCount = CACHE_SIZE_TYPES; + } + + int objCount = session.get(SessionParameter.CACHE_SIZE_LINKS, CACHE_SIZE_OBJECTS); + if (objCount < 1) { + objCount = CACHE_SIZE_OBJECTS; + } + + linkCache = new CacheImpl("Link Cache"); + linkCache.initialize(new String[] { + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=" + repCount, // repository + LruCacheLevelImpl.class.getName() + " " + LruCacheLevelImpl.MAX_ENTRIES + "=" + objCount, // id + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=12", // rel + ContentTypeCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=3," + + MapCacheLevelImpl.SINGLE_VALUE + "=true" // type + }); + + typeLinkCache = new CacheImpl("Type Link Cache"); + typeLinkCache.initialize(new String[] { + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=" + repCount, // repository + LruCacheLevelImpl.class.getName() + " " + LruCacheLevelImpl.MAX_ENTRIES + "=" + typeCount, // id + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=12", // rel + ContentTypeCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=3," + + MapCacheLevelImpl.SINGLE_VALUE + "=true"// type + }); + + collectionLinkCache = new CacheImpl("Collection Link Cache"); + collectionLinkCache.initialize(new String[] { + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=" + repCount, // repository + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=8" // collection + }); + + templateCache = new CacheImpl("URI Template Cache"); + templateCache.initialize(new String[] { + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=" + repCount, // repository + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=6" // type + }); + + repositoryLinkCache = new CacheImpl("Repository Link Cache"); + repositoryLinkCache.initialize(new String[] { + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=" + repCount, // repository + MapCacheLevelImpl.class.getName() + " " + MapCacheLevelImpl.CAPACITY + "=6" // rel + }); + } + + /** + * Adds a link. + */ + public void addLink(String repositoryId, String id, String rel, String type, String link) { + if (KNOWN_LINKS.contains(rel)) { + linkCache.put(link, repositoryId, id, rel, type); + } else if (Constants.REL_ALTERNATE.equals(rel)) { + // use streamId instead of type as discriminating parameter + String streamId = extractStreamId(link); + if (streamId != null) { + linkCache.put(link, repositoryId, id, rel, streamId); + } + } + } + + /** + * Tries to extract a streamId from an alternate link. + */ + // this is not strictly in the spec + protected String extractStreamId(String link) { + int i = link.lastIndexOf('?'); + if (i > 0) { + String[] params = link.substring(i + 1).split("&"); + for (String param : params) { + String[] parts = param.split("=", 2); + if (parts[0].equals(Constants.PARAM_STREAM_ID) && parts.length == 2) { + return parts[1]; + } + } + } + return null; + } + + /** + * Removes all links of an object. + */ + public void removeLinks(String repositoryId, String id) { + linkCache.remove(repositoryId, id); + } + + /** + * Gets a link. + */ + public String getLink(String repositoryId, String id, String rel, String type) { + return (String) linkCache.get(repositoryId, id, rel, type); + } + + /** + * Gets a link. + */ + public String getLink(String repositoryId, String id, String rel) { + return getLink(repositoryId, id, rel, null); + } + + /** + * Checks a link. + */ + public int checkLink(String repositoryId, String id, String rel, String type) { + return linkCache.check(repositoryId, id, rel, type); + } + + /** + * Locks the link cache. + */ + public void lockLinks() { + linkCache.writeLock(); + } + + /** + * Unlocks the link cache. + */ + public void unlockLinks() { + linkCache.writeUnlock(); + } + + /** + * Adds a type link. + */ + public void addTypeLink(String repositoryId, String id, String rel, String type, String link) { + if (KNOWN_LINKS.contains(rel)) { + typeLinkCache.put(link, repositoryId, id, rel, type); + } + } + + /** + * Removes all links of a type. + */ + public void removeTypeLinks(String repositoryId, String id) { + typeLinkCache.remove(repositoryId, id); + } + + /** + * Gets a type link. + */ + public String getTypeLink(String repositoryId, String id, String rel, String type) { + return (String) typeLinkCache.get(repositoryId, id, rel, type); + } + + /** + * Locks the type link cache. + */ + public void lockTypeLinks() { + typeLinkCache.writeLock(); + } + + /** + * Unlocks the type link cache. + */ + public void unlockTypeLinks() { + typeLinkCache.writeUnlock(); + } + + /** + * Adds a collection. + */ + public void addCollection(String repositoryId, String collection, String link) { + collectionLinkCache.put(link, repositoryId, collection); + } + + /** + * Gets a collection. + */ + public String getCollection(String repositoryId, String collection) { + return (String) collectionLinkCache.get(repositoryId, collection); + } + + /** + * Adds an URI template. + */ + public void addTemplate(String repositoryId, String type, String link) { + templateCache.put(link, repositoryId, type); + } + + /** + * Gets an URI template and replaces place holders with the given + * parameters. + */ + public String getTemplateLink(String repositoryId, String type, Map parameters) { + String template = (String) templateCache.get(repositoryId, type); + if (template == null) { + return null; + } + + StringBuilder result = new StringBuilder(); + StringBuilder param = new StringBuilder(); + + boolean paramMode = false; + for (int i = 0; i < template.length(); i++) { + char c = template.charAt(i); + + if (paramMode) { + if (c == '}') { + paramMode = false; + + String paramValue = UrlBuilder.normalizeParameter(parameters.get(param.toString())); + if (paramValue != null) { + try { + result.append(URLEncoder.encode(paramValue, "UTF-8")); + } catch (UnsupportedEncodingException e) { + result.append(paramValue); + } + } + + param = new StringBuilder(); + } else { + param.append(c); + } + } else { + if (c == '{') { + paramMode = true; + } else { + result.append(c); + } + } + } + + return result.toString(); + } + + /** + * Adds a collection. + */ + public void addRepositoryLink(String repositoryId, String rel, String link) { + repositoryLinkCache.put(link, repositoryId, rel); + } + + /** + * Gets a collection. + */ + public String getRepositoryLink(String repositoryId, String rel) { + return (String) repositoryLinkCache.get(repositoryId, rel); + } + + /** + * Removes all entries of the given repository from the caches. + */ + public void clearRepository(String repositoryId) { + linkCache.remove(repositoryId); + typeLinkCache.remove(repositoryId); + collectionLinkCache.remove(repositoryId); + templateCache.remove(repositoryId); + repositoryLinkCache.remove(repositoryId); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Link Cache [link cache=" + linkCache + ", type link cache=" + typeLinkCache + + ", collection link cache=" + collectionLinkCache + ", repository link cache=" + repositoryLinkCache + + ", template cache=" + templateCache + "]"; + } +} Added: chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/MultiFilingServiceImpl.java URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/MultiFilingServiceImpl.java?rev=1341891&view=auto ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/MultiFilingServiceImpl.java (added) +++ chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/MultiFilingServiceImpl.java Wed May 23 15:14:42 2012 @@ -0,0 +1,96 @@ +/* + * 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.client.bindings.spi.atompub; + +import java.io.OutputStream; + +import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession; +import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpUtils; +import org.apache.chemistry.opencmis.commons.data.ExtensionsData; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; +import org.apache.chemistry.opencmis.commons.impl.Constants; +import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; +import org.apache.chemistry.opencmis.commons.spi.MultiFilingService; + +/** + * MultiFiling Service AtomPub client. + */ +public class MultiFilingServiceImpl extends AbstractAtomPubService implements MultiFilingService { + + /** + * Constructor. + */ + public MultiFilingServiceImpl(BindingSession session) { + setSession(session); + } + + public void addObjectToFolder(String repositoryId, String objectId, String folderId, Boolean allVersions, + ExtensionsData extension) { + if (objectId == null) { + throw new CmisInvalidArgumentException("Object id must be set!"); + } + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_ALL_VERSIONS, allVersions); + + // set up object and writer + final AtomEntryWriter entryWriter = new AtomEntryWriter(createIdObject(objectId)); + + // post addObjectToFolder request + post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + } + + public void removeObjectFromFolder(String repositoryId, String objectId, String folderId, ExtensionsData extension) { + if (objectId == null) { + throw new CmisInvalidArgumentException("Object id must be set!"); + } + + // find the link + String link = loadCollection(repositoryId, Constants.COLLECTION_UNFILED); + + if (link == null) { + throw new CmisObjectNotFoundException("Unknown repository or unfiling not supported!"); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_REMOVE_FROM, folderId); + + // set up object and writer + final AtomEntryWriter entryWriter = new AtomEntryWriter(createIdObject(objectId)); + + // post removeObjectFromFolder request + post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + } +} Added: chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/NavigationServiceImpl.java URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/NavigationServiceImpl.java?rev=1341891&view=auto ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/NavigationServiceImpl.java (added) +++ chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/NavigationServiceImpl.java Wed May 23 15:14:42 2012 @@ -0,0 +1,448 @@ +/* + * 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.client.bindings.spi.atompub; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomBase; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink; +import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpUtils; +import org.apache.chemistry.opencmis.commons.data.ExtensionsData; +import org.apache.chemistry.opencmis.commons.data.ObjectData; +import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer; +import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData; +import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList; +import org.apache.chemistry.opencmis.commons.data.ObjectList; +import org.apache.chemistry.opencmis.commons.data.ObjectParentData; +import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; +import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; +import org.apache.chemistry.opencmis.commons.impl.Constants; +import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderContainerImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectListImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl; +import org.apache.chemistry.opencmis.commons.spi.NavigationService; + +/** + * Navigation Service AtomPub client. + * + * @author Florian Müller + */ +public class NavigationServiceImpl extends AbstractAtomPubService implements NavigationService { + + /** + * Constructor. + */ + public NavigationServiceImpl(BindingSession session) { + setSession(session); + } + + public ObjectInFolderList getChildren(String repositoryId, String folderId, String filter, String orderBy, + Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, + Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) { + ObjectInFolderListImpl result = new ObjectInFolderListImpl(); + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_FILTER, filter); + url.addParameter(Constants.PARAM_ORDER_BY, orderBy); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions); + url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships); + url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter); + url.addParameter(Constants.PARAM_PATH_SEGMENT, includePathSegment); + url.addParameter(Constants.PARAM_MAX_ITEMS, maxItems); + url.addParameter(Constants.PARAM_SKIP_COUNT, skipCount); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // handle top level + for (AtomElement element : feed.getElements()) { + if (element.getObject() instanceof AtomLink) { + if (isNextLink(element)) { + result.setHasMoreItems(Boolean.TRUE); + } + } else if (isInt(NAME_NUM_ITEMS, element)) { + result.setNumItems((BigInteger) element.getObject()); + } + } + + // get the children + if (!feed.getEntries().isEmpty()) { + result.setObjects(new ArrayList(feed.getEntries().size())); + + for (AtomEntry entry : feed.getEntries()) { + ObjectInFolderDataImpl child = null; + String pathSegment = null; + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (isStr(NAME_PATH_SEGMENT, element)) { + pathSegment = (String) element.getObject(); + } else if (element.getObject() instanceof ObjectData) { + child = new ObjectInFolderDataImpl(); + child.setObject((ObjectData) element.getObject()); + } + } + } finally { + unlockLinks(); + } + + if (child != null) { + child.setPathSegment(pathSegment); + result.getObjects().add(child); + } + } + } + + return result; + } + + public List getDescendants(String repositoryId, String folderId, BigInteger depth, + String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, + String renditionFilter, Boolean includePathSegment, ExtensionsData extension) { + List result = new ArrayList(); + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_DEPTH, depth); + url.addParameter(Constants.PARAM_FILTER, filter); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions); + url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships); + url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter); + url.addParameter(Constants.PARAM_PATH_SEGMENT, includePathSegment); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // process tree + addDescendantsLevel(repositoryId, feed, result); + + return result; + } + + public ObjectData getFolderParent(String repositoryId, String folderId, String filter, ExtensionsData extension) { + ObjectData result = null; + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_UP, Constants.MEDIATYPE_ENTRY); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_UP, Constants.MEDIATYPE_ENTRY); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_FILTER, filter); + + // read + HttpUtils.Response resp = read(url); + + AtomBase base = parse(resp.getStream(), AtomBase.class); + + // get the entry + AtomEntry entry = null; + if (base instanceof AtomFeed) { + AtomFeed feed = (AtomFeed) base; + if (feed.getEntries().isEmpty()) { + throw new CmisRuntimeException("Parent feed is empty!"); + } + entry = feed.getEntries().get(0); + } else if (base instanceof AtomEntry) { + entry = (AtomEntry) base; + } else { + throw new CmisRuntimeException("Unexpected document!"); + } + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (element.getObject() instanceof ObjectData) { + result = (ObjectData) element.getObject(); + } + } + } finally { + unlockLinks(); + } + + return result; + } + + public List getFolderTree(String repositoryId, String folderId, BigInteger depth, + String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships, + String renditionFilter, Boolean includePathSegment, ExtensionsData extension) { + List result = new ArrayList(); + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_FOLDERTREE, Constants.MEDIATYPE_DESCENDANTS); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_FOLDERTREE, Constants.MEDIATYPE_DESCENDANTS); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_DEPTH, depth); + url.addParameter(Constants.PARAM_FILTER, filter); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions); + url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships); + url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter); + url.addParameter(Constants.PARAM_PATH_SEGMENT, includePathSegment); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // process tree + addDescendantsLevel(repositoryId, feed, result); + + return result; + } + + public List getObjectParents(String repositoryId, String objectId, String filter, + Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, + Boolean includeRelativePathSegment, ExtensionsData extension) { + List result = new ArrayList(); + + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_UP, Constants.MEDIATYPE_FEED); + + if (link == null) { + // root and unfiled objects have no UP link + return result; + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_FILTER, filter); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions); + url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships); + url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter); + url.addParameter(Constants.PARAM_RELATIVE_PATH_SEGMENT, includeRelativePathSegment); + + // read and parse + HttpUtils.Response resp = read(url); + + AtomBase base = parse(resp.getStream(), AtomBase.class); + + if (base instanceof AtomFeed) { + // it's a feed + AtomFeed feed = (AtomFeed) base; + + // walk through the feed + for (AtomEntry entry : feed.getEntries()) { + ObjectParentDataImpl objectParent = processParentEntry(entry, repositoryId); + + if (objectParent != null) { + result.add(objectParent); + } + } + } else if (base instanceof AtomEntry) { + // it's an entry + AtomEntry entry = (AtomEntry) base; + + ObjectParentDataImpl objectParent = processParentEntry(entry, repositoryId); + + if (objectParent != null) { + result.add(objectParent); + } + } + + return result; + } + + private ObjectParentDataImpl processParentEntry(AtomEntry entry, String repositoryId) { + ObjectParentDataImpl result = null; + String relativePathSegment = null; + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (element.getObject() instanceof ObjectData) { + result = new ObjectParentDataImpl((ObjectData) element.getObject()); + } else if (is(NAME_RELATIVE_PATH_SEGMENT, element)) { + relativePathSegment = (String) element.getObject(); + } + } + } finally { + unlockLinks(); + } + + if (result != null) { + result.setRelativePathSegment(relativePathSegment); + } + + return result; + } + + public ObjectList getCheckedOutDocs(String repositoryId, String folderId, String filter, String orderBy, + Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, + BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) { + ObjectListImpl result = new ObjectListImpl(); + + // find the link + String link = loadCollection(repositoryId, Constants.COLLECTION_CHECKEDOUT); + + if (link == null) { + throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!"); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_FOLDER_ID, folderId); + url.addParameter(Constants.PARAM_FILTER, filter); + url.addParameter(Constants.PARAM_ORDER_BY, orderBy); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions); + url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships); + url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter); + url.addParameter(Constants.PARAM_MAX_ITEMS, maxItems); + url.addParameter(Constants.PARAM_SKIP_COUNT, skipCount); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // handle top level + for (AtomElement element : feed.getElements()) { + if (element.getObject() instanceof AtomLink) { + if (isNextLink(element)) { + result.setHasMoreItems(Boolean.TRUE); + } + } else if (isInt(NAME_NUM_ITEMS, element)) { + result.setNumItems((BigInteger) element.getObject()); + } + } + + // get the documents + if (!feed.getEntries().isEmpty()) { + result.setObjects(new ArrayList(feed.getEntries().size())); + + for (AtomEntry entry : feed.getEntries()) { + ObjectData child = null; + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (element.getObject() instanceof ObjectData) { + child = (ObjectData) element.getObject(); + } + } + } finally { + unlockLinks(); + } + + if (child != null) { + result.getObjects().add(child); + } + } + } + + return result; + } + + // ---- internal ---- + + /** + * Adds descendants level recursively. + */ + private void addDescendantsLevel(String repositoryId, AtomFeed feed, List containerList) { + if ((feed == null) || (feed.getEntries().isEmpty())) { + return; + } + + // walk through the feed + for (AtomEntry entry : feed.getEntries()) { + ObjectInFolderDataImpl objectInFolder = null; + String pathSegment = null; + List childContainerList = new ArrayList(); + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (element.getObject() instanceof ObjectData) { + objectInFolder = new ObjectInFolderDataImpl((ObjectData) element.getObject()); + } else if (is(NAME_PATH_SEGMENT, element)) { + pathSegment = (String) element.getObject(); + } else if (element.getObject() instanceof AtomFeed) { + addDescendantsLevel(repositoryId, (AtomFeed) element.getObject(), childContainerList); + } + } + } finally { + unlockLinks(); + } + + if (objectInFolder != null) { + objectInFolder.setPathSegment(pathSegment); + ObjectInFolderContainerImpl childContainer = new ObjectInFolderContainerImpl(objectInFolder); + childContainer.setChildren(childContainerList); + containerList.add(childContainer); + } + } + } +} Added: chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/ObjectServiceImpl.java URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/ObjectServiceImpl.java?rev=1341891&view=auto ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/ObjectServiceImpl.java (added) +++ chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/ObjectServiceImpl.java Wed May 23 15:14:42 2012 @@ -0,0 +1,717 @@ +/* + * 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.client.bindings.spi.atompub; + +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomAllowableActions; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink; +import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpUtils; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.data.Acl; +import org.apache.chemistry.opencmis.commons.data.AllowableActions; +import org.apache.chemistry.opencmis.commons.data.ContentStream; +import org.apache.chemistry.opencmis.commons.data.ExtensionsData; +import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData; +import org.apache.chemistry.opencmis.commons.data.ObjectData; +import org.apache.chemistry.opencmis.commons.data.Properties; +import org.apache.chemistry.opencmis.commons.data.PropertyData; +import org.apache.chemistry.opencmis.commons.data.PropertyId; +import org.apache.chemistry.opencmis.commons.data.RenditionData; +import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; +import org.apache.chemistry.opencmis.commons.enums.UnfileObject; +import org.apache.chemistry.opencmis.commons.enums.VersioningState; +import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException; +import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; +import org.apache.chemistry.opencmis.commons.impl.Constants; +import org.apache.chemistry.opencmis.commons.impl.MimeHelper; +import org.apache.chemistry.opencmis.commons.impl.ReturnVersion; +import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl; +import org.apache.chemistry.opencmis.commons.spi.Holder; +import org.apache.chemistry.opencmis.commons.spi.ObjectService; + +/** + * Object Service AtomPub client. + */ +public class ObjectServiceImpl extends AbstractAtomPubService implements ObjectService { + + /** + * Constructor. + */ + public ObjectServiceImpl(BindingSession session) { + setSession(session); + } + + public String createDocument(String repositoryId, Properties properties, String folderId, + ContentStream contentStream, VersioningState versioningState, List policies, Acl addAces, + Acl removeAces, ExtensionsData extension) { + checkCreateProperties(properties); + + // find the link + String link = null; + + if (folderId == null) { + // Creation of unfiled objects via AtomPub is not defined in the + // CMIS 1.0 specification. This implementation follow the CMIS 1.1 + // draft and POSTs the document to the Unfiled collection. + + link = loadCollection(repositoryId, Constants.COLLECTION_UNFILED); + + if (link == null) { + throw new CmisObjectNotFoundException("Unknown repository or unfiling not supported!"); + } + } else { + link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + } + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_VERSIONIG_STATE, versioningState); + + // set up object and writer + ObjectDataImpl object = new ObjectDataImpl(); + object.setProperties(properties); + // TODO + // object.setPolicyIds(convertPolicyIds(policies)); + + String mediaType = null; + InputStream stream = null; + + if (contentStream != null) { + mediaType = contentStream.getMimeType(); + stream = contentStream.getStream(); + } + + final AtomEntryWriter entryWriter = new AtomEntryWriter(object, mediaType, stream); + + // post the new folder object + HttpUtils.Response resp = post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + + // parse the response + AtomEntry entry = parse(resp.getStream(), AtomEntry.class); + + // handle ACL modifications + handleAclModifications(repositoryId, entry, addAces, removeAces); + + return entry.getId(); + } + + public String createDocumentFromSource(String repositoryId, String sourceId, Properties properties, + String folderId, VersioningState versioningState, List policies, Acl addACEs, Acl removeACEs, + ExtensionsData extension) { + throw new CmisNotSupportedException("createDocumentFromSource is not supported by the AtomPub binding!"); + } + + public String createFolder(String repositoryId, Properties properties, String folderId, List policies, + Acl addAces, Acl removeAces, ExtensionsData extension) { + checkCreateProperties(properties); + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + } + + UrlBuilder url = new UrlBuilder(link); + + // set up object and writer + ObjectDataImpl object = new ObjectDataImpl(); + object.setProperties(properties); + // TODO + // object.setPolicyIds(convertPolicyIds(policies)); + + final AtomEntryWriter entryWriter = new AtomEntryWriter(object); + + // post the new folder object + HttpUtils.Response resp = post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + + // parse the response + AtomEntry entry = parse(resp.getStream(), AtomEntry.class); + + // handle ACL modifications + handleAclModifications(repositoryId, entry, addAces, removeAces); + + return entry.getId(); + } + + public String createPolicy(String repositoryId, Properties properties, String folderId, List policies, + Acl addAces, Acl removeAces, ExtensionsData extension) { + checkCreateProperties(properties); + + // find the link + String link = null; + + if (folderId == null) { + // Creation of unfiled objects via AtomPub is not defined in the + // CMIS 1.0 specification. This implementation follow the CMIS 1.1 + // draft and POSTs the policy to the Unfiled collection. + + link = loadCollection(repositoryId, Constants.COLLECTION_UNFILED); + + if (link == null) { + throw new CmisObjectNotFoundException("Unknown repository or unfiling not supported!"); + } + } else { + link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + } + } + + UrlBuilder url = new UrlBuilder(link); + + // set up object and writer + ObjectDataImpl object = new ObjectDataImpl(); + object.setProperties(properties); + // TODO + // object.setPolicyIds(convertPolicyIds(policies)); + + final AtomEntryWriter entryWriter = new AtomEntryWriter(object); + + // post the new folder object + HttpUtils.Response resp = post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + + // parse the response + AtomEntry entry = parse(resp.getStream(), AtomEntry.class); + + // handle ACL modifications + handleAclModifications(repositoryId, entry, addAces, removeAces); + + return entry.getId(); + } + + public String createRelationship(String repositoryId, Properties properties, List policies, Acl addAces, + Acl removeAces, ExtensionsData extension) { + checkCreateProperties(properties); + + // find source id + PropertyData sourceIdProperty = properties.getProperties().get(PropertyIds.SOURCE_ID); + if (!(sourceIdProperty instanceof PropertyId)) { + throw new CmisInvalidArgumentException("Source Id is not set!"); + } + + String sourceId = ((PropertyId) sourceIdProperty).getFirstValue(); + if (sourceId == null) { + throw new CmisInvalidArgumentException("Source Id is not set!"); + } + + // find the link + String link = loadLink(repositoryId, sourceId, Constants.REL_RELATIONSHIPS, Constants.MEDIATYPE_FEED); + + if (link == null) { + throwLinkException(repositoryId, sourceId, Constants.REL_RELATIONSHIPS, Constants.MEDIATYPE_FEED); + } + + UrlBuilder url = new UrlBuilder(link); + + // set up object and writer + ObjectDataImpl object = new ObjectDataImpl(); + object.setProperties(properties); + // TODO + // object.setPolicyIds(convertPolicyIds(policies)); + final AtomEntryWriter entryWriter = new AtomEntryWriter(object); + + // post the new folder object + HttpUtils.Response resp = post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + + // parse the response + AtomEntry entry = parse(resp.getStream(), AtomEntry.class); + + // handle ACL modifications + handleAclModifications(repositoryId, entry, addAces, removeAces); + + return entry.getId(); + } + + public void updateProperties(String repositoryId, Holder objectId, Holder changeToken, + Properties properties, ExtensionsData extension) { + // we need an object id + if ((objectId == null) || (objectId.getValue() == null) || (objectId.getValue().length() == 0)) { + throw new CmisInvalidArgumentException("Object id must be set!"); + } + + // find the link + String link = loadLink(repositoryId, objectId.getValue(), Constants.REL_SELF, Constants.MEDIATYPE_ENTRY); + + if (link == null) { + throwLinkException(repositoryId, objectId.getValue(), Constants.REL_SELF, Constants.MEDIATYPE_ENTRY); + } + + UrlBuilder url = new UrlBuilder(link); + if (changeToken != null) { + url.addParameter(Constants.PARAM_CHANGE_TOKEN, changeToken.getValue()); + } + + // set up object and writer + ObjectDataImpl object = new ObjectDataImpl(); + object.setProperties(properties); + // TODO + // object.setPolicyIds(convertPolicyIds(policies)); + + final AtomEntryWriter entryWriter = new AtomEntryWriter(object); + + // update + HttpUtils.Response resp = put(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + + // parse new entry + AtomEntry entry = parse(resp.getStream(), AtomEntry.class); + + // we expect a CMIS entry + if (entry.getId() == null) { + throw new CmisConnectionException("Received Atom entry is not a CMIS entry!"); + } + + // set object id + objectId.setValue(entry.getId()); + + if (changeToken != null) { + changeToken.setValue(null); // just in case + } + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (element.getObject() instanceof ObjectData) { + // extract new change token + if (changeToken != null) { + object = (ObjectDataImpl) element.getObject(); + + // TODO + /* + * if (object.getProperties() != null) { for + * (CmisProperty property : + * object.getProperties().getPropertyList()) { if + * (PropertyIds + * .CHANGE_TOKEN.equals(property.getPropertyDefinitionId + * ()) && (property instanceof CmisPropertyString)) { + * + * CmisPropertyString changeTokenProperty = + * (CmisPropertyString) property; if + * (!changeTokenProperty.getValue().isEmpty()) { + * changeToken + * .setValue(changeTokenProperty.getValue().get(0)); } + * + * break; } } } + */ + } + } + } + } finally { + unlockLinks(); + } + } + + public void deleteObject(String repositoryId, String objectId, Boolean allVersions, ExtensionsData extension) { + + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_SELF, Constants.MEDIATYPE_ENTRY); + + if (link == null) { + throwLinkException(repositoryId, objectId, Constants.REL_SELF, Constants.MEDIATYPE_ENTRY); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_ALL_VERSIONS, allVersions); + + delete(url); + } + + public FailedToDeleteData deleteTree(String repositoryId, String folderId, Boolean allVersions, + UnfileObject unfileObjects, Boolean continueOnFailure, ExtensionsData extension) { + + // find the link + String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS); + + if (link == null) { + link = loadLink(repositoryId, folderId, Constants.REL_FOLDERTREE, Constants.MEDIATYPE_DESCENDANTS); + } + + if (link == null) { + throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_ALL_VERSIONS, allVersions); + url.addParameter(Constants.PARAM_UNFILE_OBJECTS, unfileObjects); + url.addParameter(Constants.PARAM_CONTINUE_ON_FAILURE, continueOnFailure); + + // make the call + HttpUtils.Response resp = HttpUtils.invokeDELETE(url, getSession()); + + // check response code + if (resp.getResponseCode() == 200 || resp.getResponseCode() == 202 || resp.getResponseCode() == 204) { + return new FailedToDeleteDataImpl(); + } + + // If the server returned an internal server error, get the remaining + // children of the folder. We only retrieve the first level, since + // getDescendants() is not supported by all repositories. + if (resp.getResponseCode() == 500) { + link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link != null) { + url = new UrlBuilder(link); + // we only want the object ids + url.addParameter(Constants.PARAM_FILTER, "cmis:objectId"); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, false); + url.addParameter(Constants.PARAM_RELATIONSHIPS, IncludeRelationships.NONE); + url.addParameter(Constants.PARAM_RENDITION_FILTER, "cmis:none"); + url.addParameter(Constants.PARAM_PATH_SEGMENT, false); + // 1000 children should be enough to indicate a problem + url.addParameter(Constants.PARAM_MAX_ITEMS, 1000); + url.addParameter(Constants.PARAM_SKIP_COUNT, 0); + + // read and parse + resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // prepare result + FailedToDeleteDataImpl result = new FailedToDeleteDataImpl(); + List ids = new ArrayList(); + result.setIds(ids); + + // get the children ids + for (AtomEntry entry : feed.getEntries()) { + ids.add(entry.getId()); + } + + return result; + } + } + + throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null); + } + + public AllowableActions getAllowableActions(String repositoryId, String objectId, ExtensionsData extension) { + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_ALLOWABLEACTIONS, + Constants.MEDIATYPE_ALLOWABLEACTION); + + if (link == null) { + throwLinkException(repositoryId, objectId, Constants.REL_ALLOWABLEACTIONS, + Constants.MEDIATYPE_ALLOWABLEACTION); + } + + UrlBuilder url = new UrlBuilder(link); + + // read and parse + HttpUtils.Response resp = read(url); + AtomAllowableActions allowableActions = parse(resp.getStream(), AtomAllowableActions.class); + + return allowableActions.getAllowableActions(); + } + + public ContentStream getContentStream(String repositoryId, String objectId, String streamId, BigInteger offset, + BigInteger length, ExtensionsData extension) { + ContentStreamImpl result = new ContentStreamImpl(); + + // find the link + String link = null; + if (streamId != null) { + // use the alternate link per spec + link = loadLink(repositoryId, objectId, Constants.REL_ALTERNATE, streamId); + if (link != null) { + streamId = null; // we have a full URL now + } + } + if (link == null) { + link = loadLink(repositoryId, objectId, AtomPubParser.LINK_REL_CONTENT, null); + } + + if (link == null) { + throw new CmisConstraintException("No content stream"); + } + + UrlBuilder url = new UrlBuilder(link); + // using the content URL and adding a streamId param + // is not spec-compliant + url.addParameter(Constants.PARAM_STREAM_ID, streamId); + + // get the content + HttpUtils.Response resp = HttpUtils.invokeGET(url, getSession(), offset, length); + + // check response code + if ((resp.getResponseCode() != 200) && (resp.getResponseCode() != 206)) { + throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null); + } + + result.setFileName(null); + result.setLength(resp.getContentLength()); + result.setMimeType(resp.getContentTypeHeader()); + result.setStream(resp.getStream()); + + return result; + } + + public ObjectData getObject(String repositoryId, String objectId, String filter, Boolean includeAllowableActions, + IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds, + Boolean includeACL, ExtensionsData extension) { + + return getObjectInternal(repositoryId, IdentifierType.ID, objectId, ReturnVersion.THIS, filter, + includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeACL, extension); + } + + public ObjectData getObjectByPath(String repositoryId, String path, String filter, Boolean includeAllowableActions, + IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds, + Boolean includeACL, ExtensionsData extension) { + + return getObjectInternal(repositoryId, IdentifierType.PATH, path, ReturnVersion.THIS, filter, + includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeACL, extension); + } + + public Properties getProperties(String repositoryId, String objectId, String filter, ExtensionsData extension) { + ObjectData object = getObjectInternal(repositoryId, IdentifierType.ID, objectId, ReturnVersion.THIS, filter, + Boolean.FALSE, IncludeRelationships.NONE, "cmis:none", Boolean.FALSE, Boolean.FALSE, extension); + + return object.getProperties(); + } + + public List getRenditions(String repositoryId, String objectId, String renditionFilter, + BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) { + ObjectData object = getObjectInternal(repositoryId, IdentifierType.ID, objectId, ReturnVersion.THIS, + PropertyIds.OBJECT_ID, Boolean.FALSE, IncludeRelationships.NONE, renditionFilter, Boolean.FALSE, + Boolean.FALSE, extension); + + List result = object.getRenditions(); + if (result == null) { + result = Collections.emptyList(); + } + + return result; + } + + public void moveObject(String repositoryId, Holder objectId, String targetFolderId, String sourceFolderId, + ExtensionsData extension) { + if ((objectId == null) || (objectId.getValue() == null) || (objectId.getValue().length() == 0)) { + throw new CmisInvalidArgumentException("Object id must be set!"); + } + + if ((targetFolderId == null) || (targetFolderId.length() == 0) || (sourceFolderId == null) + || (sourceFolderId.length() == 0)) { + throw new CmisInvalidArgumentException("Source and target folder must be set!"); + } + + // find the link + String link = loadLink(repositoryId, targetFolderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + + if (link == null) { + throwLinkException(repositoryId, targetFolderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_SOURCE_FOLDER_ID, sourceFolderId); + + // set up object and writer + final AtomEntryWriter entryWriter = new AtomEntryWriter(createIdObject(objectId.getValue())); + + // post move request + HttpUtils.Response resp = post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + + // parse the response + AtomEntry entry = parse(resp.getStream(), AtomEntry.class); + + objectId.setValue(entry.getId()); + } + + public void setContentStream(String repositoryId, Holder objectId, Boolean overwriteFlag, + Holder changeToken, ContentStream contentStream, ExtensionsData extension) { + // we need an object id + if ((objectId == null) || (objectId.getValue() == null)) { + throw new CmisInvalidArgumentException("Object ID must be set!"); + } + + // we need content + if ((contentStream == null) || (contentStream.getStream() == null) || (contentStream.getMimeType() == null)) { + throw new CmisInvalidArgumentException("Content must be set!"); + } + + // find the link + String link = loadLink(repositoryId, objectId.getValue(), Constants.REL_EDITMEDIA, null); + + if (link == null) { + throwLinkException(repositoryId, objectId.getValue(), Constants.REL_EDITMEDIA, null); + } + + UrlBuilder url = new UrlBuilder(link); + if (changeToken != null) { + url.addParameter(Constants.PARAM_CHANGE_TOKEN, changeToken.getValue()); + } + url.addParameter(Constants.PARAM_OVERWRITE_FLAG, overwriteFlag); + + final InputStream stream = contentStream.getStream(); + + // Content-Disposition header for the filename + Map headers = null; + if (contentStream.getFileName() != null) { + headers = Collections + .singletonMap( + MimeHelper.CONTENT_DISPOSITION, + MimeHelper.encodeContentDisposition(MimeHelper.DISPOSITION_ATTACHMENT, + contentStream.getFileName())); + } + + // send content + HttpUtils.Response resp = put(url, contentStream.getMimeType(), headers, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + int b; + byte[] buffer = new byte[4096]; + + while ((b = stream.read(buffer)) > -1) { + out.write(buffer, 0, b); + } + + stream.close(); + } + }); + + // check response code further + if ((resp.getResponseCode() != 200) && (resp.getResponseCode() != 201) && (resp.getResponseCode() != 204)) { + throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null); + } + + objectId.setValue(null); + if (changeToken != null) { + changeToken.setValue(null); + } + } + + public void deleteContentStream(String repositoryId, Holder objectId, Holder changeToken, + ExtensionsData extension) { + // we need an object id + if ((objectId == null) || (objectId.getValue() == null)) { + throw new CmisInvalidArgumentException("Object ID must be set!"); + } + + // find the link + String link = loadLink(repositoryId, objectId.getValue(), Constants.REL_EDITMEDIA, null); + + if (link == null) { + throwLinkException(repositoryId, objectId.getValue(), Constants.REL_EDITMEDIA, null); + } + + UrlBuilder url = new UrlBuilder(link); + if (changeToken != null) { + url.addParameter(Constants.PARAM_CHANGE_TOKEN, changeToken.getValue()); + } + + delete(url); + + objectId.setValue(null); + if (changeToken != null) { + changeToken.setValue(null); + } + } + + // ---- internal ---- + + private static void checkCreateProperties(Properties properties) { + if ((properties == null) || (properties.getProperties() == null)) { + throw new CmisInvalidArgumentException("Properties must be set!"); + } + + if (!properties.getProperties().containsKey(PropertyIds.OBJECT_TYPE_ID)) { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_TYPE_ID + " must be set!"); + } + + if (properties.getProperties().containsKey(PropertyIds.OBJECT_ID)) { + throw new CmisInvalidArgumentException("Property " + PropertyIds.OBJECT_ID + " must not be set!"); + } + } + + /** + * Handles ACL modifications of newly created objects. + */ + private void handleAclModifications(String repositoryId, AtomEntry entry, Acl addAces, Acl removeAces) { + if (!isAclMergeRequired(addAces, removeAces)) { + return; + } + + Acl originalAces = null; + + // walk through the entry and find the current ACL + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof ObjectData) { + // extract current ACL + ObjectData object = (ObjectData) element.getObject(); + originalAces = object.getAcl(); + + break; + } + } + + if (originalAces != null) { + // merge and update ACL + Acl newACL = mergeAcls(originalAces, addAces, removeAces); + if (newACL != null) { + updateAcl(repositoryId, entry.getId(), newACL, null); + } + } + } +} Added: chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/PolicyServiceImpl.java URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/PolicyServiceImpl.java?rev=1341891&view=auto ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/PolicyServiceImpl.java (added) +++ chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/PolicyServiceImpl.java Wed May 23 15:14:42 2012 @@ -0,0 +1,185 @@ +/* + * 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.client.bindings.spi.atompub; + +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink; +import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpUtils; +import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.data.ExtensionsData; +import org.apache.chemistry.opencmis.commons.data.ObjectData; +import org.apache.chemistry.opencmis.commons.data.PropertyData; +import org.apache.chemistry.opencmis.commons.data.PropertyId; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; +import org.apache.chemistry.opencmis.commons.impl.Constants; +import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; +import org.apache.chemistry.opencmis.commons.spi.PolicyService; + +/** + * Policy Service AtomPub client. + */ +public class PolicyServiceImpl extends AbstractAtomPubService implements PolicyService { + + /** + * Constructor. + */ + public PolicyServiceImpl(BindingSession session) { + setSession(session); + } + + public void applyPolicy(String repositoryId, String policyId, String objectId, ExtensionsData extension) { + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_POLICIES, Constants.MEDIATYPE_FEED); + + if (link == null) { + throwLinkException(repositoryId, objectId, Constants.REL_POLICIES, Constants.MEDIATYPE_FEED); + } + + UrlBuilder url = new UrlBuilder(link); + + // set up object and writer + final AtomEntryWriter entryWriter = new AtomEntryWriter(createIdObject(objectId)); + + // post applyPolicy request + post(url, Constants.MEDIATYPE_ENTRY, new HttpUtils.Output() { + public void write(OutputStream out) throws Exception { + entryWriter.write(out); + } + }); + } + + public List getAppliedPolicies(String repositoryId, String objectId, String filter, + ExtensionsData extension) { + List result = new ArrayList(); + + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_POLICIES, Constants.MEDIATYPE_FEED); + + if (link == null) { + throwLinkException(repositoryId, objectId, Constants.REL_POLICIES, Constants.MEDIATYPE_FEED); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_FILTER, filter); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // get the policies + if (!feed.getEntries().isEmpty()) { + for (AtomEntry entry : feed.getEntries()) { + ObjectData policy = null; + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof ObjectData) { + policy = (ObjectData) element.getObject(); + } + } + + if (policy != null) { + result.add(policy); + } + } + } + + return result; + } + + public void removePolicy(String repositoryId, String policyId, String objectId, ExtensionsData extension) { + // we need a policy id + if (policyId == null) { + throw new CmisInvalidArgumentException("Policy id must be set!"); + } + + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_POLICIES, Constants.MEDIATYPE_FEED); + + if (link == null) { + throwLinkException(repositoryId, objectId, Constants.REL_POLICIES, Constants.MEDIATYPE_FEED); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_FILTER, PropertyIds.OBJECT_ID); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // find the policy + String policyLink = null; + boolean found = false; + + if (!feed.getEntries().isEmpty()) { + for (AtomEntry entry : feed.getEntries()) { + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + AtomLink atomLink = (AtomLink) element.getObject(); + if (Constants.REL_SELF.equals(atomLink.getRel())) { + policyLink = atomLink.getHref(); + } + } else if (element.getObject() instanceof ObjectData) { + String id = findIdProperty((ObjectData) element.getObject()); + if (policyId.equals(id)) { + found = true; + } + } + } + + if (found) { + break; + } + } + } + + // if found, delete it + if (found && (policyLink != null)) { + delete(new UrlBuilder(policyLink)); + } + } + + /** + * Finds the id property within a CMIS object. + */ + private static String findIdProperty(ObjectData object) { + if ((object == null) || (object.getProperties() == null)) { + return null; + } + + for (PropertyData property : object.getProperties().getPropertyList()) { + if (PropertyIds.OBJECT_ID.equals(property.getId()) && (property instanceof PropertyId)) { + List values = ((PropertyId) property).getValues(); + if (values.size() == 1) { + return values.get(0); + } + } + } + + return null; + } +} Added: chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/RelationshipServiceImpl.java URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/RelationshipServiceImpl.java?rev=1341891&view=auto ============================================================================== --- chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/RelationshipServiceImpl.java (added) +++ chemistry/opencmis/trunk/chemistry-opencmis-android/chemistry-opencmis-android-client/src/main/java/org/apache/chemistry/opencmis/client/bindings/spi/atompub/RelationshipServiceImpl.java Wed May 23 15:14:42 2012 @@ -0,0 +1,120 @@ +/* + * 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.client.bindings.spi.atompub; + +import java.math.BigInteger; +import java.util.ArrayList; + +import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed; +import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink; +import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpUtils; +import org.apache.chemistry.opencmis.commons.data.ExtensionsData; +import org.apache.chemistry.opencmis.commons.data.ObjectData; +import org.apache.chemistry.opencmis.commons.data.ObjectList; +import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection; +import org.apache.chemistry.opencmis.commons.impl.Constants; +import org.apache.chemistry.opencmis.commons.impl.UrlBuilder; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectListImpl; +import org.apache.chemistry.opencmis.commons.spi.RelationshipService; + +/** + * Relationship Service AtomPub client. + */ +public class RelationshipServiceImpl extends AbstractAtomPubService implements RelationshipService { + + /** + * Constructor. + */ + public RelationshipServiceImpl(BindingSession session) { + setSession(session); + } + + public ObjectList getObjectRelationships(String repositoryId, String objectId, Boolean includeSubRelationshipTypes, + RelationshipDirection relationshipDirection, String typeId, String filter, Boolean includeAllowableActions, + BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) { + ObjectListImpl result = new ObjectListImpl(); + + // find the link + String link = loadLink(repositoryId, objectId, Constants.REL_RELATIONSHIPS, Constants.MEDIATYPE_FEED); + + if (link == null) { + throwLinkException(repositoryId, objectId, Constants.REL_RELATIONSHIPS, Constants.MEDIATYPE_FEED); + } + + UrlBuilder url = new UrlBuilder(link); + url.addParameter(Constants.PARAM_SUB_RELATIONSHIP_TYPES, includeSubRelationshipTypes); + url.addParameter(Constants.PARAM_RELATIONSHIP_DIRECTION, relationshipDirection); + url.addParameter(Constants.PARAM_TYPE_ID, typeId); + url.addParameter(Constants.PARAM_FILTER, filter); + url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions); + url.addParameter(Constants.PARAM_MAX_ITEMS, maxItems); + url.addParameter(Constants.PARAM_SKIP_COUNT, skipCount); + + // read and parse + HttpUtils.Response resp = read(url); + AtomFeed feed = parse(resp.getStream(), AtomFeed.class); + + // handle top level + for (AtomElement element : feed.getElements()) { + if (element.getObject() instanceof AtomLink) { + if (isNextLink(element)) { + result.setHasMoreItems(Boolean.TRUE); + } + } else if (isInt(NAME_NUM_ITEMS, element)) { + result.setNumItems((BigInteger) element.getObject()); + } + } + + // get the children + if (!feed.getEntries().isEmpty()) { + result.setObjects(new ArrayList(feed.getEntries().size())); + + for (AtomEntry entry : feed.getEntries()) { + ObjectData relationship = null; + + lockLinks(); + try { + // clean up cache + removeLinks(repositoryId, entry.getId()); + + // walk through the entry + for (AtomElement element : entry.getElements()) { + if (element.getObject() instanceof AtomLink) { + addLink(repositoryId, entry.getId(), (AtomLink) element.getObject()); + } else if (element.getObject() instanceof ObjectData) { + relationship = (ObjectData) element.getObject(); + } + } + } finally { + unlockLinks(); + } + + if (relationship != null) { + result.getObjects().add(relationship); + } + } + + } + + return result; + } +}