You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2013/05/11 23:19:48 UTC

svn commit: r1481420 [1/7] - in /chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src: main/java/org/apache/chemistry/opencmis/server/impl/ main/java/org/apache/chemistry/opencmis/server/impl/atompub/ main/java/org/...

Author: fmui
Date: Sat May 11 21:19:48 2013
New Revision: 1481420

URL: http://svn.apache.org/r1481420
Log:
OpenCMIS server refactoring

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AbstractAtomPubServiceCall.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/AbstractBrowserServiceCall.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/BrowserCallContextImpl.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/AbstractCmisHttpServlet.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/AbstractServiceCall.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/ServiceCall.java
Removed:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AtomPubUtils.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/BrowserBindingUtils.java
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/CallContextImpl.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AclService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/CmisAtomPubServlet.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/DiscoveryService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/MultiFilingService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/NavigationService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/ObjectService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/PolicyService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/RelationshipService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/RepositoryService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/VersioningService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/AclService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/CmisBrowserBindingServlet.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/DiscoveryService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/MultiFilingService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/NavigationService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/ObjectService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/PolicyService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/RelationshipService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/RepositoryService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/VersioningService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/token/AbstractSimpleTokenHandler.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/browser/token/SimpleTokenHandler.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/webservices/AbstractService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/Dispatcher.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/shared/HttpUtils.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/test/java/org/apache/chemistry/opencmis/server/impl/ProxyRequestTest.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/CallContextImpl.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/CallContextImpl.java?rev=1481420&r1=1481419&r2=1481420&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/CallContextImpl.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/CallContextImpl.java Sat May 11 21:19:48 2013
@@ -21,10 +21,17 @@ package org.apache.chemistry.opencmis.se
 import java.io.File;
 import java.math.BigInteger;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
 import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
 import org.apache.chemistry.opencmis.commons.server.CallContext;
+import org.apache.chemistry.opencmis.commons.server.CmisServiceFactory;
+import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory;
 
 /**
  * Implementation of the {@link CallContext} interface.
@@ -35,10 +42,92 @@ public class CallContextImpl implements 
     private final boolean objectInfoRequired;
     private final Map<String, Object> parameter = new HashMap<String, Object>();
 
-    public CallContextImpl(String binding, String repositoryId, boolean objectInfoRequired) {
+    public CallContextImpl(String binding, CmisVersion cmisVersion, String repositoryId, ServletContext servletContext,
+            HttpServletRequest request, HttpServletResponse response, CmisServiceFactory factory,
+            ThresholdOutputStreamFactory streamFactory) {
         this.binding = binding;
-        this.objectInfoRequired = objectInfoRequired;
+        this.objectInfoRequired = BINDING_ATOMPUB.equals(binding);
         put(REPOSITORY_ID, repositoryId);
+
+        // CMIS version
+        put(CallContext.CMIS_VERSION, cmisVersion);
+
+        // servlet context and HTTP servlet request and response
+        put(CallContext.SERVLET_CONTEXT, servletContext);
+        put(CallContext.HTTP_SERVLET_REQUEST, request);
+        put(CallContext.HTTP_SERVLET_RESPONSE, response);
+
+        if (streamFactory != null) {
+            put(TEMP_DIR, streamFactory.getTempDir());
+            put(MEMORY_THRESHOLD, streamFactory.getMemoryThreshold());
+            put(MAX_CONTENT_SIZE, streamFactory.getMaxContentSize());
+            put(ENCRYPT_TEMP_FILE, streamFactory.isEncrypted());
+            put(STREAM_FACTORY, streamFactory);
+        } else if (factory != null) {
+            put(TEMP_DIR, factory.getTempDirectory());
+            put(MEMORY_THRESHOLD, factory.getMemoryThreshold());
+            put(MAX_CONTENT_SIZE, -1);
+            put(ENCRYPT_TEMP_FILE, false);
+        }
+    }
+
+    public void setRange(String rangeHeader) {
+        if (rangeHeader == null) {
+            return;
+        }
+
+        rangeHeader = rangeHeader.trim().toLowerCase(Locale.ENGLISH);
+
+        if (rangeHeader.length() > 6 && rangeHeader.startsWith("bytes=") && rangeHeader.indexOf(',') == -1
+                && rangeHeader.charAt(6) != '-') {
+            BigInteger offset = null;
+            BigInteger length = null;
+
+            int ds = rangeHeader.indexOf('-');
+            if (ds > 6) {
+                try {
+                    String firstBytePosStr = rangeHeader.substring(6, ds);
+                    if (firstBytePosStr.length() > 0) {
+                        offset = new BigInteger(firstBytePosStr);
+                    }
+
+                    if (!rangeHeader.endsWith("-")) {
+                        String lastBytePosStr = rangeHeader.substring(ds + 1);
+                        if (offset == null) {
+                            length = (new BigInteger(lastBytePosStr)).add(BigInteger.ONE);
+                        } else {
+                            length = (new BigInteger(lastBytePosStr)).subtract(offset).add(BigInteger.ONE);
+                        }
+                    }
+
+                    if (offset != null) {
+                        put(OFFSET, offset);
+                    }
+                    if (length != null) {
+                        put(LENGTH, length);
+                    }
+                } catch (NumberFormatException e) {
+                    // invalid Range header must be ignored
+                }
+            }
+        }
+    }
+
+    public void setAcceptLanguage(String acceptLanguageHeader) {
+        if (acceptLanguageHeader == null) {
+            return;
+        }
+
+        String[] locale = acceptLanguageHeader.split("-");
+        put(LOCALE_ISO639_LANGUAGE, locale[0].trim());
+        if (locale.length > 1) {
+            int x = locale[1].indexOf(',');
+            if (x == -1) {
+                put(LOCALE_ISO3166_COUNTRY, locale[1].trim());
+            } else {
+                put(LOCALE_ISO3166_COUNTRY, locale[1].substring(0, x).trim());
+            }
+        }
     }
 
     public String getBinding() {

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AbstractAtomPubServiceCall.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AbstractAtomPubServiceCall.java?rev=1481420&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AbstractAtomPubServiceCall.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AbstractAtomPubServiceCall.java Sat May 11 21:19:48 2013
@@ -0,0 +1,432 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contributors:
+ *     Florian Mueller
+ *     Florent Guillaume, Nuxeo
+ */
+package org.apache.chemistry.opencmis.server.impl.atompub;
+
+import java.math.BigInteger;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.chemistry.opencmis.commons.data.ObjectData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.chemistry.opencmis.commons.impl.Constants;
+import org.apache.chemistry.opencmis.commons.impl.ReturnVersion;
+import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
+import org.apache.chemistry.opencmis.commons.server.CmisService;
+import org.apache.chemistry.opencmis.commons.server.NamespaceDefinitions;
+import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
+import org.apache.chemistry.opencmis.commons.server.RenditionInfo;
+import org.apache.chemistry.opencmis.server.shared.AbstractServiceCall;
+import org.apache.chemistry.opencmis.server.shared.Dispatcher;
+
+public abstract class AbstractAtomPubServiceCall extends AbstractServiceCall {
+
+    public static final String RESOURCE_CHILDREN = "children";
+    public static final String RESOURCE_DESCENDANTS = "descendants";
+    public static final String RESOURCE_FOLDERTREE = "foldertree";
+    public static final String RESOURCE_TYPE = "type";
+    public static final String RESOURCE_TYPES = "types";
+    public static final String RESOURCE_TYPESDESC = "typedesc";
+    public static final String RESOURCE_ENTRY = "entry";
+    public static final String RESOURCE_PARENTS = "parents";
+    public static final String RESOURCE_VERSIONS = "versions";
+    public static final String RESOURCE_ALLOWABLEACIONS = "allowableactions";
+    public static final String RESOURCE_ACL = "acl";
+    public static final String RESOURCE_POLICIES = "policies";
+    public static final String RESOURCE_RELATIONSHIPS = "relationships";
+    public static final String RESOURCE_OBJECTBYID = "id";
+    public static final String RESOURCE_OBJECTBYPATH = "path";
+    public static final String RESOURCE_QUERY = "query";
+    public static final String RESOURCE_CHECKEDOUT = "checkedout";
+    public static final String RESOURCE_UNFILED = "unfiled";
+    public static final String RESOURCE_CHANGES = "changes";
+    public static final String RESOURCE_CONTENT = "content";
+    public static final String RESOURCE_BULK_UPDATE = "update";
+
+    public static final String REPOSITORY_PLACEHOLDER = "{repositoryId}";
+
+    public static final BigInteger PAGE_SIZE = BigInteger.valueOf(100);
+
+    public static final String TYPE_AUTHOR = "unknown";
+
+    /**
+     * Compiles the base URL for links, collections and templates.
+     */
+    public UrlBuilder compileBaseUrl(HttpServletRequest request, String repositoryId) {
+        String baseUrl = (String) request.getAttribute(Dispatcher.BASE_URL_ATTRIBUTE);
+        if (baseUrl != null) {
+            int repIdPos = baseUrl.indexOf(REPOSITORY_PLACEHOLDER);
+            if (repIdPos < 0) {
+                return new UrlBuilder(baseUrl);
+            } else {
+                return new UrlBuilder(baseUrl.substring(0, repIdPos) + repositoryId
+                        + baseUrl.substring(repIdPos + REPOSITORY_PLACEHOLDER.length()));
+            }
+        }
+
+        UrlBuilder url = new UrlBuilder(request.getScheme(), request.getServerName(), request.getServerPort(), null);
+
+        url.addPath(request.getContextPath());
+        url.addPath(request.getServletPath());
+
+        if (repositoryId != null) {
+            url.addPathSegment(repositoryId);
+        }
+
+        return url;
+    }
+
+    /**
+     * Compiles a URL for links, collections and templates.
+     */
+    public String compileUrl(UrlBuilder baseUrl, String resource, String id) {
+        return compileUrlBuilder(baseUrl, resource, id).toString();
+    }
+
+    /**
+     * Compiles a URL for links, collections and templates.
+     */
+    public UrlBuilder compileUrlBuilder(UrlBuilder baseUrl, String resource, String id) {
+        UrlBuilder url = new UrlBuilder(baseUrl);
+        url.addPathSegment(resource);
+
+        if (id != null) {
+            url.addParameter("id", id);
+        }
+
+        return url;
+    }
+
+    // -------------------------------------------------------------------------
+    // --- namespaces ---
+    // -------------------------------------------------------------------------
+
+    public Map<String, String> getNamespaces(Object obj) {
+        if (obj instanceof NamespaceDefinitions) {
+            return ((NamespaceDefinitions) obj).getNamespaces();
+        }
+
+        return null;
+    }
+
+    // -------------------------------------------------------------------------
+    // --- entry builder ---
+    // -------------------------------------------------------------------------
+
+    /**
+     * Writes the a object entry.
+     */
+    public void writeObjectEntry(CmisService service, AtomEntry entry, ObjectData object,
+            List<ObjectInFolderContainer> children, String repositoryId, String pathSegment,
+            String relativePathSegment, UrlBuilder baseUrl, boolean isRoot, CmisVersion cmisVersion)
+            throws XMLStreamException {
+        if (object == null) {
+            throw new CmisRuntimeException("Object not set!");
+        }
+
+        ObjectInfo info = service.getObjectInfo(repositoryId, object.getId());
+        if (info == null) {
+            throw new CmisRuntimeException("Object Info not found for: " + object.getId());
+        }
+
+        // start
+        entry.startEntry(isRoot);
+
+        // write object
+        String contentSrc = null;
+
+        if (info.hasContent()) {
+            UrlBuilder contentSrcBuilder = compileUrlBuilder(baseUrl, RESOURCE_CONTENT, info.getId());
+            if (info.getFileName() != null) {
+                contentSrcBuilder.addPathSegment(info.getFileName());
+            }
+
+            contentSrc = contentSrcBuilder.toString();
+        }
+
+        entry.writeObject(object, info, contentSrc, info.getContentType(), pathSegment, relativePathSegment,
+                cmisVersion);
+
+        // write links
+        entry.writeServiceLink(baseUrl.toString(), repositoryId);
+
+        entry.writeSelfLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()), info.getId());
+        entry.writeEnclosureLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()));
+        entry.writeEditLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()));
+        entry.writeDescribedByLink(compileUrl(baseUrl, RESOURCE_TYPE, info.getTypeId()));
+        entry.writeAllowableActionsLink(compileUrl(baseUrl, RESOURCE_ALLOWABLEACIONS, info.getId()));
+
+        if (info.hasParent()) {
+            entry.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENTS, info.getId()), Constants.MEDIATYPE_FEED);
+        }
+
+        if (info.getBaseType() == BaseTypeId.CMIS_FOLDER) {
+            entry.writeDownLink(compileUrl(baseUrl, RESOURCE_CHILDREN, info.getId()), Constants.MEDIATYPE_FEED);
+
+            if (info.supportsDescendants()) {
+                entry.writeDownLink(compileUrl(baseUrl, RESOURCE_DESCENDANTS, info.getId()),
+                        Constants.MEDIATYPE_DESCENDANTS);
+            }
+
+            if (info.supportsFolderTree()) {
+                entry.writeFolderTreeLink(compileUrl(baseUrl, RESOURCE_FOLDERTREE, info.getId()));
+            }
+        }
+
+        if (info.getVersionSeriesId() != null) {
+            UrlBuilder vsUrl = compileUrlBuilder(baseUrl, RESOURCE_VERSIONS, info.getId());
+            vsUrl.addParameter(Constants.PARAM_VERSION_SERIES_ID, info.getVersionSeriesId());
+            entry.writeVersionHistoryLink(vsUrl.toString());
+        }
+
+        if (!info.isCurrentVersion()) {
+            UrlBuilder cvUrl = compileUrlBuilder(baseUrl, RESOURCE_ENTRY, info.getId());
+            cvUrl.addParameter(Constants.PARAM_RETURN_VERSION, ReturnVersion.LATEST);
+            entry.writeEditLink(cvUrl.toString());
+        }
+
+        if (info.getBaseType() == BaseTypeId.CMIS_DOCUMENT) {
+            entry.writeEditMediaLink(compileUrl(baseUrl, RESOURCE_CONTENT, info.getId()), info.getContentType());
+        }
+
+        if (info.getWorkingCopyId() != null) {
+            entry.writeWorkingCopyLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getWorkingCopyId()));
+        }
+
+        if (info.getWorkingCopyOriginalId() != null) {
+            entry.writeViaLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getWorkingCopyOriginalId()));
+        }
+
+        if (info.getRenditionInfos() != null) {
+            for (RenditionInfo ri : info.getRenditionInfos()) {
+                UrlBuilder rurl = compileUrlBuilder(baseUrl, RESOURCE_CONTENT, info.getId());
+                rurl.addParameter(Constants.PARAM_STREAM_ID, ri.getId());
+                entry.writeAlternateLink(rurl.toString(), ri.getContenType(), ri.getKind(), ri.getTitle(),
+                        ri.getLength());
+            }
+        }
+
+        if (info.hasAcl()) {
+            entry.writeAclLink(compileUrl(baseUrl, RESOURCE_ACL, info.getId()));
+        }
+
+        if (info.supportsPolicies()) {
+            entry.writePoliciesLink(compileUrl(baseUrl, RESOURCE_POLICIES, info.getId()));
+        }
+
+        if (info.supportsRelationships()) {
+            entry.writeRelationshipsLink(compileUrl(baseUrl, RESOURCE_RELATIONSHIPS, info.getId()));
+        }
+
+        if (info.getRelationshipSourceIds() != null) {
+            for (String id : info.getRelationshipSourceIds()) {
+                entry.writeRelationshipSourceLink(compileUrl(baseUrl, RESOURCE_ENTRY, id));
+            }
+        }
+
+        if (info.getRelationshipTargetIds() != null) {
+            for (String id : info.getRelationshipTargetIds()) {
+                entry.writeRelationshipTargetLink(compileUrl(baseUrl, RESOURCE_ENTRY, id));
+            }
+        }
+
+        // write children
+        if ((children != null) && (children.size() > 0)) {
+            writeObjectChildren(service, entry, info, children, repositoryId, baseUrl, cmisVersion);
+        }
+
+        // we are done
+        entry.endEntry();
+    }
+
+    /**
+     * Writes the a object entry in a content changes list.
+     * 
+     * Content changes objects need special treatment because some of them could
+     * have been deleted and an object info cannot be generated.
+     */
+    public void writeContentChangesObjectEntry(CmisService service, AtomEntry entry, ObjectData object,
+            List<ObjectInFolderContainer> children, String repositoryId, String pathSegment,
+            String relativePathSegment, UrlBuilder baseUrl, boolean isRoot, CmisVersion cmisVersion)
+            throws XMLStreamException {
+        if (object == null) {
+            throw new CmisRuntimeException("Object not set!");
+        }
+
+        ObjectInfo info = null;
+        try {
+            info = service.getObjectInfo(repositoryId, object.getId());
+        } catch (Exception e) {
+            // ignore all exceptions
+        }
+
+        if (info != null) {
+            writeObjectEntry(service, entry, object, children, repositoryId, pathSegment, relativePathSegment, baseUrl,
+                    isRoot, cmisVersion);
+            return;
+        }
+
+        // start delete object entry
+        entry.startEntry(isRoot);
+
+        // write object
+        entry.writeDeletedObject(object, cmisVersion);
+
+        // write links
+        entry.writeServiceLink(baseUrl.toString(), repositoryId);
+
+        // we are done
+        entry.endEntry();
+    }
+
+    /**
+     * Writes an objects entry children feed.
+     */
+    public void writeObjectChildren(CmisService service, AtomEntry entry, ObjectInfo folderInfo,
+            List<ObjectInFolderContainer> children, String repositoryId, UrlBuilder baseUrl, CmisVersion cmisVersion)
+            throws XMLStreamException {
+
+        // start
+        AtomFeed feed = new AtomFeed(entry.getWriter());
+        feed.startChildren();
+        feed.startFeed(false);
+
+        // write basic Atom feed elements
+        feed.writeFeedElements(folderInfo.getId(), folderInfo.getAtomId(), folderInfo.getCreatedBy(),
+                folderInfo.getName(), folderInfo.getLastModificationDate(), null, null);
+
+        // write links
+        feed.writeServiceLink(baseUrl.toString(), repositoryId);
+
+        feed.writeSelfLink(compileUrl(baseUrl, RESOURCE_DESCENDANTS, folderInfo.getId()), null);
+
+        feed.writeViaLink(compileUrl(baseUrl, RESOURCE_ENTRY, folderInfo.getId()));
+
+        feed.writeDownLink(compileUrl(baseUrl, RESOURCE_CHILDREN, folderInfo.getId()), Constants.MEDIATYPE_FEED);
+
+        feed.writeDownLink(compileUrl(baseUrl, RESOURCE_FOLDERTREE, folderInfo.getId()),
+                Constants.MEDIATYPE_DESCENDANTS);
+
+        feed.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENTS, folderInfo.getId()), Constants.MEDIATYPE_FEED);
+
+        for (ObjectInFolderContainer container : children) {
+            if ((container != null) && (container.getObject() != null)) {
+                writeObjectEntry(service, entry, container.getObject().getObject(), container.getChildren(),
+                        repositoryId, container.getObject().getPathSegment(), null, baseUrl, false, cmisVersion);
+            }
+        }
+
+        // we are done
+        feed.endFeed();
+        feed.endChildren();
+    }
+
+    /**
+     * Writes the a type entry.
+     */
+    public void writeTypeEntry(AtomEntry entry, TypeDefinition type, List<TypeDefinitionContainer> children,
+            String repositoryId, UrlBuilder baseUrl, boolean isRoot, CmisVersion cmisVersion) throws XMLStreamException {
+
+        // start
+        entry.startEntry(isRoot);
+
+        // write type
+        entry.writeType(type, cmisVersion);
+
+        // write links
+        entry.writeServiceLink(baseUrl.toString(), repositoryId);
+
+        entry.writeSelfLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()), type.getId());
+        entry.writeEnclosureLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()));
+        if (type.getParentTypeId() != null) {
+            entry.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getParentTypeId()), Constants.MEDIATYPE_ENTRY);
+        }
+        UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null);
+        downLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
+        entry.writeDownLink(downLink.toString(), Constants.MEDIATYPE_CHILDREN);
+        UrlBuilder downLink2 = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null);
+        downLink2.addParameter(Constants.PARAM_TYPE_ID, type.getId());
+        entry.writeDownLink(downLink2.toString(), Constants.MEDIATYPE_DESCENDANTS);
+        entry.writeDescribedByLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getBaseTypeId().value()));
+
+        // write children
+        if ((children != null) && (children.size() > 0)) {
+            writeTypeChildren(entry, type, children, repositoryId, baseUrl, cmisVersion);
+        }
+
+        // we are done
+        entry.endEntry();
+    }
+
+    /**
+     * Writes the a type entry children feed.
+     */
+    private void writeTypeChildren(AtomEntry entry, TypeDefinition type, List<TypeDefinitionContainer> children,
+            String repositoryId, UrlBuilder baseUrl, CmisVersion cmisVersion) throws XMLStreamException {
+
+        // start
+        AtomFeed feed = new AtomFeed(entry.getWriter());
+        feed.startChildren();
+        feed.startFeed(false);
+
+        // write basic Atom feed elements
+        feed.writeFeedElements(type.getId(), null, TYPE_AUTHOR, type.getDisplayName(), new GregorianCalendar(), null,
+                null);
+
+        feed.writeServiceLink(baseUrl.toString(), repositoryId);
+
+        UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null);
+        selfLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
+        feed.writeSelfLink(selfLink.toString(), type.getId());
+
+        feed.writeViaLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()));
+
+        UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null);
+        downLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
+        feed.writeDownLink(downLink.toString(), Constants.MEDIATYPE_FEED);
+
+        if (type.getParentTypeId() != null) {
+            feed.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getParentTypeId()), Constants.MEDIATYPE_ENTRY);
+        }
+
+        // write tree
+        for (TypeDefinitionContainer container : children) {
+            if ((container != null) && (container.getTypeDefinition() != null)) {
+                writeTypeEntry(entry, container.getTypeDefinition(), container.getChildren(), repositoryId, baseUrl,
+                        false, cmisVersion);
+            }
+        }
+
+        // we are done
+        feed.endFeed();
+        feed.endChildren();
+    }
+}

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AclService.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AclService.java?rev=1481420&r1=1481419&r2=1481420&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AclService.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/AclService.java Sat May 11 21:19:48 2013
@@ -18,10 +18,6 @@
  */
 package org.apache.chemistry.opencmis.server.impl.atompub;
 
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getBooleanParameter;
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getEnumParameter;
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getStringParameter;
-
 import java.io.OutputStream;
 
 import javax.servlet.http.HttpServletRequest;
@@ -46,75 +42,79 @@ import org.apache.chemistry.opencmis.com
  */
 public class AclService {
 
-    private AclService() {
+    public static abstract class AclServiceCall extends AbstractAtomPubServiceCall {
+        protected void writeAclXML(Acl acl, CmisVersion cmisVersion, OutputStream out) throws XMLStreamException {
+            XMLStreamWriter writer = XMLUtils.createWriter(out);
+            XMLUtils.startXmlDocument(writer);
+            XMLConverter.writeAcl(writer, cmisVersion, true, acl);
+            XMLUtils.endXmlDocument(writer);
+        }
     }
 
     /**
      * Get ACL.
      */
-    public static void getAcl(CallContext context, CmisService service, String repositoryId,
-            HttpServletRequest request, HttpServletResponse response) throws Exception {
-        // get parameters
-        String objectId = getStringParameter(request, Constants.PARAM_ID);
-        Boolean onlyBasicPermissions = getBooleanParameter(request, Constants.PARAM_ONLY_BASIC_PERMISSIONS);
+    public static class GetAcl extends AclServiceCall {
+        public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request,
+                HttpServletResponse response) throws Exception {
+            // get parameters
+            String objectId = getStringParameter(request, Constants.PARAM_ID);
+            Boolean onlyBasicPermissions = getBooleanParameter(request, Constants.PARAM_ONLY_BASIC_PERMISSIONS);
 
-        // execute
-        Acl acl = service.getAcl(repositoryId, objectId, onlyBasicPermissions, null);
+            // execute
+            Acl acl = service.getAcl(repositoryId, objectId, onlyBasicPermissions, null);
 
-        if (acl == null) {
-            throw new CmisRuntimeException("ACL is null!");
-        }
+            if (acl == null) {
+                throw new CmisRuntimeException("ACL is null!");
+            }
 
-        // set headers
-        response.setStatus(HttpServletResponse.SC_OK);
-        response.setContentType(Constants.MEDIATYPE_ACL);
+            // set headers
+            response.setStatus(HttpServletResponse.SC_OK);
+            response.setContentType(Constants.MEDIATYPE_ACL);
 
-        // write XML
-        writeAclXML(acl, context.getCmisVersion(), response.getOutputStream());
+            // write XML
+            writeAclXML(acl, context.getCmisVersion(), response.getOutputStream());
+        }
     }
 
     /**
      * Apply ACL.
      */
-    public static void applyAcl(CallContext context, CmisService service, String repositoryId,
-            HttpServletRequest request, HttpServletResponse response) throws Exception {
-        // get parameters
-        String objectId = getStringParameter(request, Constants.PARAM_ID);
-        AclPropagation aclPropagation = getEnumParameter(request, Constants.PARAM_ACL_PROPAGATION, AclPropagation.class);
-
-        Acl aces = null;
-        XMLStreamReader parser = null;
-        try {
-            parser = XMLUtils.createParser(request.getInputStream());
-            XMLUtils.findNextStartElemenet(parser);
-            aces = XMLConverter.convertAcl(parser);
-        } catch (XMLStreamException e) {
-            throw new CmisInvalidArgumentException("Invalid request!");
-        } finally {
-            if (parser != null) {
-                try {
-                    parser.close();
-                } catch (XMLStreamException e2) {
-                    // ignore
+    public static class ApplyAcl extends AclServiceCall {
+        public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request,
+                HttpServletResponse response) throws Exception {
+            // get parameters
+            String objectId = getStringParameter(request, Constants.PARAM_ID);
+            AclPropagation aclPropagation = getEnumParameter(request, Constants.PARAM_ACL_PROPAGATION,
+                    AclPropagation.class);
+
+            Acl aces = null;
+            XMLStreamReader parser = null;
+            try {
+                parser = XMLUtils.createParser(request.getInputStream());
+                XMLUtils.findNextStartElemenet(parser);
+                aces = XMLConverter.convertAcl(parser);
+            } catch (XMLStreamException e) {
+                throw new CmisInvalidArgumentException("Invalid request!");
+            } finally {
+                if (parser != null) {
+                    try {
+                        parser.close();
+                    } catch (XMLStreamException e2) {
+                        // ignore
+                    }
                 }
             }
-        }
-
-        // execute
-        Acl acl = service.applyAcl(repositoryId, objectId, aces, aclPropagation);
 
-        // set headers
-        response.setStatus(HttpServletResponse.SC_CREATED);
-        response.setContentType(Constants.MEDIATYPE_ACL);
+            // execute
+            Acl acl = service.applyAcl(repositoryId, objectId, aces, aclPropagation);
 
-        // write XML
-        writeAclXML(acl, context.getCmisVersion(), response.getOutputStream());
-    }
+            // set headers
+            response.setStatus(HttpServletResponse.SC_CREATED);
+            response.setContentType(Constants.MEDIATYPE_ACL);
 
-    private static void writeAclXML(Acl acl, CmisVersion cmisVersion, OutputStream out) throws XMLStreamException {
-        XMLStreamWriter writer = XMLUtils.createWriter(out);
-        XMLUtils.startXmlDocument(writer);
-        XMLConverter.writeAcl(writer, cmisVersion, true, acl);
-        XMLUtils.endXmlDocument(writer);
+            // write XML
+            writeAclXML(acl, context.getCmisVersion(), response.getOutputStream());
+        }
     }
 }

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/CmisAtomPubServlet.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/CmisAtomPubServlet.java?rev=1481420&r1=1481419&r2=1481420&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/CmisAtomPubServlet.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/CmisAtomPubServlet.java Sat May 11 21:19:48 2013
@@ -18,27 +18,27 @@
  */
 package org.apache.chemistry.opencmis.server.impl.atompub;
 
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_ACL;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_ALLOWABLEACIONS;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_BULK_UPDATE;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CHANGES;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CHECKEDOUT;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CHILDREN;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CONTENT;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_DESCENDANTS;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_ENTRY;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_FOLDERTREE;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_OBJECTBYID;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_OBJECTBYPATH;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_PARENTS;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_POLICIES;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_QUERY;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_RELATIONSHIPS;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_TYPE;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_TYPES;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_TYPESDESC;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_UNFILED;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_VERSIONS;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_ACL;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_ALLOWABLEACIONS;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_BULK_UPDATE;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_CHANGES;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_CHECKEDOUT;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_CHILDREN;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_CONTENT;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_DESCENDANTS;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_ENTRY;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_FOLDERTREE;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_OBJECTBYID;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_OBJECTBYPATH;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_PARENTS;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_POLICIES;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_QUERY;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_RELATIONSHIPS;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_TYPE;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_TYPES;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_TYPESDESC;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_UNFILED;
+import static org.apache.chemistry.opencmis.server.impl.atompub.AbstractAtomPubServiceCall.RESOURCE_VERSIONS;
 import static org.apache.chemistry.opencmis.server.shared.Dispatcher.METHOD_DELETE;
 import static org.apache.chemistry.opencmis.server.shared.Dispatcher.METHOD_GET;
 import static org.apache.chemistry.opencmis.server.shared.Dispatcher.METHOD_POST;
@@ -49,7 +49,6 @@ import java.io.PrintWriter;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -68,18 +67,15 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
 import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
 import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
-import org.apache.chemistry.opencmis.commons.impl.ClassLoaderUtil;
 import org.apache.chemistry.opencmis.commons.server.CallContext;
 import org.apache.chemistry.opencmis.commons.server.CmisService;
-import org.apache.chemistry.opencmis.commons.server.CmisServiceFactory;
-import org.apache.chemistry.opencmis.server.impl.CmisRepositoryContextListener;
 import org.apache.chemistry.opencmis.server.impl.ServerVersion;
-import org.apache.chemistry.opencmis.server.shared.CallContextHandler;
+import org.apache.chemistry.opencmis.server.shared.AbstractCmisHttpServlet;
 import org.apache.chemistry.opencmis.server.shared.Dispatcher;
 import org.apache.chemistry.opencmis.server.shared.ExceptionHelper;
 import org.apache.chemistry.opencmis.server.shared.HttpUtils;
 import org.apache.chemistry.opencmis.server.shared.QueryStringHttpServletRequestWrapper;
-import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory;
+import org.apache.chemistry.opencmis.server.shared.ServiceCall;
 import org.apache.commons.lang.StringEscapeUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -87,110 +83,76 @@ import org.slf4j.LoggerFactory;
 /**
  * CMIS AtomPub servlet.
  */
-public class CmisAtomPubServlet extends HttpServlet {
-
-    public static final String PARAM_CALL_CONTEXT_HANDLER = "callContextHandler";
-    public static final String PARAM_CMIS_VERSION = "cmisVersion";
+public class CmisAtomPubServlet extends AbstractCmisHttpServlet {
 
     private static final Logger LOG = LoggerFactory.getLogger(CmisAtomPubServlet.class.getName());
 
     private static final long serialVersionUID = 1L;
 
-    private CmisVersion cmisVersion;
-
-    private ThresholdOutputStreamFactory streamFactory;
-    private Dispatcher dispatcher;
-    private CallContextHandler callContextHandler;
+    private final Dispatcher dispatcher = new Dispatcher();
 
     @Override
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
 
-        // initialize the call context handler
-        callContextHandler = null;
-        String callContextHandlerClass = config.getInitParameter(PARAM_CALL_CONTEXT_HANDLER);
-        if (callContextHandlerClass != null) {
-            try {
-                callContextHandler = (CallContextHandler) ClassLoaderUtil.loadClass(callContextHandlerClass)
-                        .newInstance();
-            } catch (Exception e) {
-                throw new ServletException("Could not load call context handler: " + e, e);
-            }
-        }
+        // set the binding
+        setBinding(CallContext.BINDING_ATOMPUB);
 
-        // get CMIS version
+        // get and CMIS version
         String cmisVersionStr = config.getInitParameter(PARAM_CMIS_VERSION);
         if (cmisVersionStr != null) {
             try {
-                cmisVersion = CmisVersion.fromValue(cmisVersionStr);
+                setCmisVersion(CmisVersion.fromValue(cmisVersionStr));
             } catch (IllegalArgumentException e) {
                 LOG.warn("CMIS version is invalid! Setting it to CMIS 1.0.");
-                cmisVersion = CmisVersion.CMIS_1_0;
+                setCmisVersion(CmisVersion.CMIS_1_0);
             }
         } else {
             LOG.warn("CMIS version is not defined! Setting it to CMIS 1.0.");
-            cmisVersion = CmisVersion.CMIS_1_0;
+            setCmisVersion(CmisVersion.CMIS_1_0);
         }
 
-        // get memory threshold and temp directory
-        CmisServiceFactory factory = (CmisServiceFactory) config.getServletContext().getAttribute(
-                CmisRepositoryContextListener.SERVICES_FACTORY);
-
-        if (factory == null) {
-            throw new CmisRuntimeException("Service factory not available! Configuration problem?");
-        }
-
-        // set up stream factory
-        streamFactory = ThresholdOutputStreamFactory.newInstance(factory.getTempDirectory(),
-                factory.getMemoryThreshold(), factory.getMaxContentSize(), factory.encryptTempFiles());
-
-        // initialize the dispatcher
-        dispatcher = new Dispatcher();
-
-        try {
-            dispatcher.addResource(RESOURCE_TYPES, METHOD_GET, RepositoryService.class, "getTypeChildren");
-            dispatcher.addResource(RESOURCE_TYPES, METHOD_POST, RepositoryService.class, "createType");
-            dispatcher.addResource(RESOURCE_TYPESDESC, METHOD_GET, RepositoryService.class, "getTypeDescendants");
-            dispatcher.addResource(RESOURCE_TYPE, METHOD_GET, RepositoryService.class, "getTypeDefinition");
-            dispatcher.addResource(RESOURCE_TYPE, METHOD_PUT, RepositoryService.class, "updateType");
-            dispatcher.addResource(RESOURCE_TYPE, METHOD_DELETE, RepositoryService.class, "deleteType");
-            dispatcher.addResource(RESOURCE_CHILDREN, METHOD_GET, NavigationService.class, "getChildren");
-            dispatcher.addResource(RESOURCE_DESCENDANTS, METHOD_GET, NavigationService.class, "getDescendants");
-            dispatcher.addResource(RESOURCE_FOLDERTREE, METHOD_GET, NavigationService.class, "getFolderTree");
-            dispatcher.addResource(RESOURCE_PARENTS, METHOD_GET, NavigationService.class, "getObjectParents");
-            dispatcher.addResource(RESOURCE_CHECKEDOUT, METHOD_GET, NavigationService.class, "getCheckedOutDocs");
-            dispatcher.addResource(RESOURCE_ENTRY, METHOD_GET, ObjectService.class, "getObject");
-            dispatcher.addResource(RESOURCE_OBJECTBYID, METHOD_GET, ObjectService.class, "getObject");
-            dispatcher.addResource(RESOURCE_OBJECTBYPATH, METHOD_GET, ObjectService.class, "getObjectByPath");
-            dispatcher.addResource(RESOURCE_ALLOWABLEACIONS, METHOD_GET, ObjectService.class, "getAllowableActions");
-            dispatcher.addResource(RESOURCE_CONTENT, METHOD_GET, ObjectService.class, "getContentStream");
-            dispatcher.addResource(RESOURCE_CONTENT, METHOD_PUT, ObjectService.class, "setOrAppendContentStream");
-            dispatcher.addResource(RESOURCE_CONTENT, METHOD_DELETE, ObjectService.class, "deleteContentStream");
-            dispatcher.addResource(RESOURCE_CHILDREN, METHOD_POST, ObjectService.class, "create");
-            dispatcher.addResource(RESOURCE_RELATIONSHIPS, METHOD_POST, ObjectService.class, "createRelationship");
-            dispatcher.addResource(RESOURCE_ENTRY, METHOD_PUT, ObjectService.class, "updateProperties");
-            dispatcher.addResource(RESOURCE_ENTRY, METHOD_DELETE, ObjectService.class, "deleteObject");
-            dispatcher.addResource(RESOURCE_CHILDREN, METHOD_DELETE, ObjectService.class, "deleteTree"); // 1.1
-            dispatcher.addResource(RESOURCE_DESCENDANTS, METHOD_DELETE, ObjectService.class, "deleteTree");
-            dispatcher.addResource(RESOURCE_FOLDERTREE, METHOD_DELETE, ObjectService.class, "deleteTree");
-            dispatcher.addResource(RESOURCE_CHECKEDOUT, METHOD_POST, VersioningService.class, "checkOut");
-            dispatcher.addResource(RESOURCE_VERSIONS, METHOD_GET, VersioningService.class, "getAllVersions");
-            dispatcher.addResource(RESOURCE_VERSIONS, METHOD_DELETE, VersioningService.class, "deleteAllVersions");
-            dispatcher.addResource(RESOURCE_QUERY, METHOD_GET, DiscoveryService.class, "query");
-            dispatcher.addResource(RESOURCE_QUERY, METHOD_POST, DiscoveryService.class, "query");
-            dispatcher.addResource(RESOURCE_CHANGES, METHOD_GET, DiscoveryService.class, "getContentChanges");
-            dispatcher.addResource(RESOURCE_RELATIONSHIPS, METHOD_GET, RelationshipService.class,
-                    "getObjectRelationships");
-            dispatcher.addResource(RESOURCE_UNFILED, METHOD_POST, MultiFilingService.class, "removeObjectFromFolder");
-            dispatcher.addResource(RESOURCE_ACL, METHOD_GET, AclService.class, "getAcl");
-            dispatcher.addResource(RESOURCE_ACL, METHOD_PUT, AclService.class, "applyAcl");
-            dispatcher.addResource(RESOURCE_POLICIES, METHOD_GET, PolicyService.class, "getAppliedPolicies");
-            dispatcher.addResource(RESOURCE_POLICIES, METHOD_POST, PolicyService.class, "applyPolicy");
-            dispatcher.addResource(RESOURCE_POLICIES, METHOD_DELETE, PolicyService.class, "removePolicy");
-            dispatcher.addResource(RESOURCE_BULK_UPDATE, METHOD_POST, ObjectService.class, "bulkUpdateProperties");
-        } catch (NoSuchMethodException e) {
-            LOG.error("Cannot initialize dispatcher!", e);
-        }
+        // initialize resources
+        addResource("", METHOD_GET, new RepositoryService.GetRepositories());
+        addResource(RESOURCE_TYPES, METHOD_GET, new RepositoryService.GetTypeChildren());
+        addResource(RESOURCE_TYPES, METHOD_POST, new RepositoryService.CreateType());
+        addResource(RESOURCE_TYPESDESC, METHOD_GET, new RepositoryService.GetTypeDescendants());
+        addResource(RESOURCE_TYPE, METHOD_GET, new RepositoryService.GetTypeDefinition());
+        addResource(RESOURCE_TYPE, METHOD_PUT, new RepositoryService.UpdateType());
+        addResource(RESOURCE_TYPE, METHOD_DELETE, new RepositoryService.DeleteType());
+        addResource(RESOURCE_CHILDREN, METHOD_GET, new NavigationService.GetChildren());
+        addResource(RESOURCE_DESCENDANTS, METHOD_GET, new NavigationService.GetDescendants());
+        addResource(RESOURCE_FOLDERTREE, METHOD_GET, new NavigationService.GetFolderTree());
+        addResource(RESOURCE_PARENTS, METHOD_GET, new NavigationService.GetObjectParents());
+        addResource(RESOURCE_CHECKEDOUT, METHOD_GET, new NavigationService.GetCheckedOutDocs());
+        addResource(RESOURCE_ENTRY, METHOD_GET, new ObjectService.GetObject());
+        addResource(RESOURCE_OBJECTBYID, METHOD_GET, new ObjectService.GetObject());
+        addResource(RESOURCE_OBJECTBYPATH, METHOD_GET, new ObjectService.GetObjectByPath());
+        addResource(RESOURCE_ALLOWABLEACIONS, METHOD_GET, new ObjectService.GetAllowableActions());
+        addResource(RESOURCE_CONTENT, METHOD_GET, new ObjectService.GetContentStream());
+        addResource(RESOURCE_CONTENT, METHOD_PUT, new ObjectService.SetOrAppendContentStream());
+        addResource(RESOURCE_CONTENT, METHOD_DELETE, new ObjectService.DeleteContentStream());
+        addResource(RESOURCE_CHILDREN, METHOD_POST, new ObjectService.Create());
+        addResource(RESOURCE_RELATIONSHIPS, METHOD_POST, new ObjectService.CreateRelationship());
+        addResource(RESOURCE_ENTRY, METHOD_PUT, new ObjectService.UpdateProperties());
+        addResource(RESOURCE_ENTRY, METHOD_DELETE, new ObjectService.DeleteObject());
+        addResource(RESOURCE_CHILDREN, METHOD_DELETE, new ObjectService.DeleteTree()); // 1.1
+        addResource(RESOURCE_DESCENDANTS, METHOD_DELETE, new ObjectService.DeleteTree());
+        addResource(RESOURCE_FOLDERTREE, METHOD_DELETE, new ObjectService.DeleteTree());
+        addResource(RESOURCE_BULK_UPDATE, METHOD_POST, new ObjectService.BulkUpdateProperties());
+        addResource(RESOURCE_CHECKEDOUT, METHOD_POST, new VersioningService.CheckOut());
+        addResource(RESOURCE_VERSIONS, METHOD_GET, new VersioningService.GetAllVersions());
+        addResource(RESOURCE_VERSIONS, METHOD_DELETE, new VersioningService.DeleteAllVersions());
+        addResource(RESOURCE_QUERY, METHOD_GET, new DiscoveryService.Query());
+        addResource(RESOURCE_QUERY, METHOD_POST, new DiscoveryService.Query());
+        addResource(RESOURCE_CHANGES, METHOD_GET, new DiscoveryService.GetContentChanges());
+        addResource(RESOURCE_RELATIONSHIPS, METHOD_GET, new RelationshipService.GetObjectRelationships());
+        addResource(RESOURCE_UNFILED, METHOD_POST, new MultiFilingService.RemoveObjectFromFolder());
+        addResource(RESOURCE_ACL, METHOD_GET, new AclService.GetAcl());
+        addResource(RESOURCE_ACL, METHOD_PUT, new AclService.ApplyAcl());
+        addResource(RESOURCE_POLICIES, METHOD_GET, new PolicyService.GetAppliedPolicies());
+        addResource(RESOURCE_POLICIES, METHOD_POST, new PolicyService.ApplyPolicy());
+        addResource(RESOURCE_POLICIES, METHOD_DELETE, new PolicyService.RemovePolicy());
     }
 
     @Override
@@ -205,8 +167,7 @@ public class CmisAtomPubServlet extends 
         // create a context object, dispatch and handle exceptions
         CallContext context = null;
         try {
-            context = HttpUtils.createContext(qsRequest, response, getServletContext(), CallContext.BINDING_ATOMPUB,
-                    cmisVersion, callContextHandler, streamFactory);
+            context = createContext(getServletContext(), qsRequest, response);
             dispatch(context, qsRequest, response);
         } catch (Exception e) {
             if (e instanceof CmisPermissionDeniedException) {
@@ -226,6 +187,13 @@ public class CmisAtomPubServlet extends 
     }
 
     /**
+     * Registers a new resource.
+     */
+    protected void addResource(String resource, String httpMethod, ServiceCall serviceCall) {
+        dispatcher.addResource(resource, httpMethod, serviceCall);
+    }
+
+    /**
      * Dispatches to feed, entry or whatever.
      */
     private void dispatch(CallContext context, HttpServletRequest request, HttpServletResponse response)
@@ -233,23 +201,15 @@ public class CmisAtomPubServlet extends 
 
         CmisService service = null;
         try {
-            // get services factory
-            CmisServiceFactory factory = (CmisServiceFactory) getServletContext().getAttribute(
-                    CmisRepositoryContextListener.SERVICES_FACTORY);
-
-            if (factory == null) {
-                throw new CmisRuntimeException("Service factory not available! Configuration problem?");
-            }
-
             // get the service
-            service = factory.getService(context);
+            service = getServiceFactory().getService(context);
 
             // analyze the path
             String[] pathFragments = HttpUtils.splitPath(request);
 
             if (pathFragments.length < 2) {
                 // root -> service document
-                RepositoryService.getRepositories(context, service, request, response);
+                dispatcher.dispatch("", METHOD_GET, context, service, null, request, response);
                 return;
             }
 
@@ -258,12 +218,12 @@ public class CmisAtomPubServlet extends 
             String resource = pathFragments[1];
 
             // dispatch
-            boolean methodFound = dispatcher.dispatch(resource, method, context, service, repositoryId, request,
+            boolean callServiceFound = dispatcher.dispatch(resource, method, context, service, repositoryId, request,
                     response);
 
-            // if the dispatcher couldn't find a matching method, return an
-            // error message
-            if (!methodFound) {
+            // if the dispatcher couldn't find a matching service
+            // -> return an error message
+            if (!callServiceFound) {
                 response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Unknown operation");
             }
         } finally {
@@ -276,7 +236,7 @@ public class CmisAtomPubServlet extends 
     /**
      * Translates an exception in an appropriate HTTP error code.
      */
-    private static int getErrorCode(CmisBaseException ex) {
+    protected int getErrorCode(CmisBaseException ex) {
         if (ex instanceof CmisConstraintException) {
             return 409;
         } else if (ex instanceof CmisContentAlreadyExistsException) {
@@ -309,7 +269,7 @@ public class CmisAtomPubServlet extends 
     /**
      * Prints the error HTML page.
      */
-    private static void printError(Exception ex, HttpServletResponse response) {
+    protected void printError(Exception ex, HttpServletResponse response) {
         int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
         String exceptionName = "runtime";
 

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/DiscoveryService.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/DiscoveryService.java?rev=1481420&r1=1481419&r2=1481420&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/DiscoveryService.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/atompub/DiscoveryService.java Sat May 11 21:19:48 2013
@@ -18,17 +18,6 @@
  */
 package org.apache.chemistry.opencmis.server.impl.atompub;
 
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CHANGES;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_QUERY;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.compileBaseUrl;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.compileUrlBuilder;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.getNamespaces;
-import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.writeContentChangesObjectEntry;
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getBigIntegerParameter;
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getBooleanParameter;
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getEnumParameter;
-import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getStringParameter;
-
 import java.math.BigInteger;
 import java.util.GregorianCalendar;
 
@@ -52,242 +41,243 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.server.CallContext;
 import org.apache.chemistry.opencmis.commons.server.CmisService;
 import org.apache.chemistry.opencmis.commons.spi.Holder;
+import org.apache.chemistry.opencmis.server.shared.Dispatcher;
 
 /**
  * Discovery Service operations.
  */
-public final class DiscoveryService {
-
-    private static final String METHOD_GET = "GET";
-    private static final String METHOD_POST = "POST";
-
-    private DiscoveryService() {
-    }
+public class DiscoveryService {
 
     /**
      * Query.
      */
-    public static void query(CallContext context, CmisService service, String repositoryId, HttpServletRequest request,
-            HttpServletResponse response) throws Exception {
-        // get parameters
-        String statement = null;
-        Boolean searchAllVersions = null;
-        Boolean includeAllowableActions = null;
-        IncludeRelationships includeRelationships = null;
-        String renditionFilter = null;
-        BigInteger maxItems = null;
-        BigInteger skipCount = null;
-
-        int statusCode = 0;
-
-        if (METHOD_POST.equals(request.getMethod())) {
-            // POST -> read from stream
-
-            QueryTypeImpl queryType = null;
-            XMLStreamReader parser = null;
-            try {
-                parser = XMLUtils.createParser(request.getInputStream());
-                XMLUtils.findNextStartElemenet(parser);
-                queryType = XMLConverter.convertQuery(parser);
-            } catch (XMLStreamException e) {
-                throw new CmisInvalidArgumentException("Invalid query request!");
-            } finally {
-                if (parser != null) {
-                    try {
-                        parser.close();
-                    } catch (XMLStreamException e2) {
-                        // ignore
+    public static class Query extends AbstractAtomPubServiceCall {
+        public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request,
+                HttpServletResponse response) throws Exception {
+            // get parameters
+            String statement = null;
+            Boolean searchAllVersions = null;
+            Boolean includeAllowableActions = null;
+            IncludeRelationships includeRelationships = null;
+            String renditionFilter = null;
+            BigInteger maxItems = null;
+            BigInteger skipCount = null;
+
+            int statusCode = 0;
+
+            if (Dispatcher.METHOD_POST.equals(request.getMethod())) {
+                // POST -> read from stream
+
+                QueryTypeImpl queryType = null;
+                XMLStreamReader parser = null;
+                try {
+                    parser = XMLUtils.createParser(request.getInputStream());
+                    XMLUtils.findNextStartElemenet(parser);
+                    queryType = XMLConverter.convertQuery(parser);
+                } catch (XMLStreamException e) {
+                    throw new CmisInvalidArgumentException("Invalid query request!");
+                } finally {
+                    if (parser != null) {
+                        try {
+                            parser.close();
+                        } catch (XMLStreamException e2) {
+                            // ignore
+                        }
                     }
                 }
-            }
 
-            statement = queryType.getStatement();
-            searchAllVersions = queryType.getSearchAllVersions();
-            includeAllowableActions = queryType.getIncludeAllowableActions();
-            includeRelationships = queryType.getIncludeRelationships();
-            renditionFilter = queryType.getRenditionFilter();
-            maxItems = queryType.getMaxItems();
-            skipCount = queryType.getSkipCount();
-
-            statusCode = HttpServletResponse.SC_CREATED;
-        } else if (METHOD_GET.equals(request.getMethod())) {
-            // GET -> parameters
-            statement = getStringParameter(request, Constants.PARAM_Q);
-            searchAllVersions = getBooleanParameter(request, Constants.PARAM_SEARCH_ALL_VERSIONS);
-            includeAllowableActions = getBooleanParameter(request, Constants.PARAM_ALLOWABLE_ACTIONS);
-            includeRelationships = getEnumParameter(request, Constants.PARAM_RELATIONSHIPS, IncludeRelationships.class);
-            renditionFilter = null;
-            maxItems = getBigIntegerParameter(request, Constants.PARAM_MAX_ITEMS);
-            skipCount = getBigIntegerParameter(request, Constants.PARAM_SKIP_COUNT);
-
-            statusCode = HttpServletResponse.SC_OK;
-        } else {
-            throw new CmisRuntimeException("Invalid HTTP method!");
-        }
+                statement = queryType.getStatement();
+                searchAllVersions = queryType.getSearchAllVersions();
+                includeAllowableActions = queryType.getIncludeAllowableActions();
+                includeRelationships = queryType.getIncludeRelationships();
+                renditionFilter = queryType.getRenditionFilter();
+                maxItems = queryType.getMaxItems();
+                skipCount = queryType.getSkipCount();
+
+                statusCode = HttpServletResponse.SC_CREATED;
+            } else if (Dispatcher.METHOD_GET.equals(request.getMethod())) {
+                // GET -> parameters
+                statement = getStringParameter(request, Constants.PARAM_Q);
+                searchAllVersions = getBooleanParameter(request, Constants.PARAM_SEARCH_ALL_VERSIONS);
+                includeAllowableActions = getBooleanParameter(request, Constants.PARAM_ALLOWABLE_ACTIONS);
+                includeRelationships = getEnumParameter(request, Constants.PARAM_RELATIONSHIPS,
+                        IncludeRelationships.class);
+                renditionFilter = null;
+                maxItems = getBigIntegerParameter(request, Constants.PARAM_MAX_ITEMS);
+                skipCount = getBigIntegerParameter(request, Constants.PARAM_SKIP_COUNT);
+
+                statusCode = HttpServletResponse.SC_OK;
+            } else {
+                throw new CmisRuntimeException("Invalid HTTP method!");
+            }
 
-        // execute
-        ObjectList results = service.query(repositoryId, statement, searchAllVersions, includeAllowableActions,
-                includeRelationships, renditionFilter, maxItems, skipCount, null);
+            // execute
+            ObjectList results = service.query(repositoryId, statement, searchAllVersions, includeAllowableActions,
+                    includeRelationships, renditionFilter, maxItems, skipCount, null);
 
-        if (results == null) {
-            throw new CmisRuntimeException("Results are null!");
-        }
+            if (results == null) {
+                throw new CmisRuntimeException("Results are null!");
+            }
 
-        // set headers
-        UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
+            // set headers
+            UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
 
-        UrlBuilder pagingUrl = compileUrlBuilder(baseUrl, RESOURCE_QUERY, null);
-        pagingUrl.addParameter(Constants.PARAM_Q, statement);
-        pagingUrl.addParameter(Constants.PARAM_SEARCH_ALL_VERSIONS, searchAllVersions);
-        pagingUrl.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
-        pagingUrl.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
-
-        UrlBuilder location = new UrlBuilder(pagingUrl);
-        location.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
-        location.addParameter(Constants.PARAM_SKIP_COUNT, skipCount);
-
-        response.setStatus(statusCode);
-        response.setContentType(Constants.MEDIATYPE_FEED);
-
-        // The Content-Location header is optional (CMIS specification 3.7.2.1).
-        // Since it can cause problems with long query statements it is
-        // deactivated.
-        // response.setHeader("Content-Location", location.toString());
-
-        // The Location header is not optional (CMIS specification 3.7.2.1).
-        response.setHeader("Location", location.toString());
-
-        // write XML
-        AtomFeed feed = new AtomFeed();
-        feed.startDocument(response.getOutputStream(), getNamespaces(service));
-        feed.startFeed(true);
-
-        // write basic Atom feed elements
-        GregorianCalendar now = new GregorianCalendar();
-        feed.writeFeedElements("query", null, "", "Query", now, null, results.getNumItems());
-
-        // write links
-        feed.writeServiceLink(baseUrl.toString(), repositoryId);
-
-        feed.writePagingLinks(pagingUrl, maxItems, skipCount, results.getNumItems(), results.hasMoreItems(),
-                AtomPubUtils.PAGE_SIZE);
-
-        CmisVersion cmisVersion = context.getCmisVersion();
-        if (results.getObjects() != null) {
-            AtomEntry entry = new AtomEntry(feed.getWriter());
-            int idCounter = 0;
-            for (ObjectData result : results.getObjects()) {
-                if (result == null) {
-                    continue;
+            UrlBuilder pagingUrl = compileUrlBuilder(baseUrl, RESOURCE_QUERY, null);
+            pagingUrl.addParameter(Constants.PARAM_Q, statement);
+            pagingUrl.addParameter(Constants.PARAM_SEARCH_ALL_VERSIONS, searchAllVersions);
+            pagingUrl.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
+            pagingUrl.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
+
+            UrlBuilder location = new UrlBuilder(pagingUrl);
+            location.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
+            location.addParameter(Constants.PARAM_SKIP_COUNT, skipCount);
+
+            response.setStatus(statusCode);
+            response.setContentType(Constants.MEDIATYPE_FEED);
+
+            // The Content-Location header is optional (CMIS specification
+            // 3.7.2.1).
+            // Since it can cause problems with long query statements it is
+            // deactivated.
+            // response.setHeader("Content-Location", location.toString());
+
+            // The Location header is not optional (CMIS specification 3.7.2.1).
+            response.setHeader("Location", location.toString());
+
+            // write XML
+            AtomFeed feed = new AtomFeed();
+            feed.startDocument(response.getOutputStream(), getNamespaces(service));
+            feed.startFeed(true);
+
+            // write basic Atom feed elements
+            GregorianCalendar now = new GregorianCalendar();
+            feed.writeFeedElements("query", null, "", "Query", now, null, results.getNumItems());
+
+            // write links
+            feed.writeServiceLink(baseUrl.toString(), repositoryId);
+
+            feed.writePagingLinks(pagingUrl, maxItems, skipCount, results.getNumItems(), results.hasMoreItems(),
+                    PAGE_SIZE);
+
+            CmisVersion cmisVersion = context.getCmisVersion();
+            if (results.getObjects() != null) {
+                AtomEntry entry = new AtomEntry(feed.getWriter());
+                int idCounter = 0;
+                for (ObjectData result : results.getObjects()) {
+                    if (result == null) {
+                        continue;
+                    }
+                    idCounter++;
+                    writeQueryResultEntry(entry, result, "id-" + idCounter, now, cmisVersion);
                 }
-                idCounter++;
-                writeQueryResultEntry(entry, result, "id-" + idCounter, now, cmisVersion);
             }
-        }
 
-        // we are done
-        feed.endFeed();
-        feed.endDocument();
-    }
-
-    private static void writeQueryResultEntry(AtomEntry entry, ObjectData result, String id, GregorianCalendar now,
-            CmisVersion cmisVersion) throws Exception {
-        if (result == null) {
-            return;
+            // we are done
+            feed.endFeed();
+            feed.endDocument();
         }
 
-        // start
-        entry.startEntry(false);
+        private void writeQueryResultEntry(AtomEntry entry, ObjectData result, String id, GregorianCalendar now,
+                CmisVersion cmisVersion) throws Exception {
+            if (result == null) {
+                return;
+            }
+
+            // start
+            entry.startEntry(false);
 
-        // write Atom base tags
-        entry.writeAuthor("");
-        entry.writeId(entry.generateAtomId(id));
-        entry.writePublished(now);
-        entry.writeTitle("Query Result " + id);
-        entry.writeUpdated(now);
-
-        // write query result object
-        XMLConverter.writeObject(entry.getWriter(), cmisVersion, false, XMLConstants.TAG_OBJECT,
-                XMLConstants.NAMESPACE_RESTATOM, result);
+            // write Atom base tags
+            entry.writeAuthor("");
+            entry.writeId(entry.generateAtomId(id));
+            entry.writePublished(now);
+            entry.writeTitle("Query Result " + id);
+            entry.writeUpdated(now);
+
+            // write query result object
+            XMLConverter.writeObject(entry.getWriter(), cmisVersion, false, XMLConstants.TAG_OBJECT,
+                    XMLConstants.NAMESPACE_RESTATOM, result);
 
-        // we are done
-        entry.endEntry();
+            // we are done
+            entry.endEntry();
+        }
     }
 
     /**
      * Get content changes.
      */
-    public static void getContentChanges(CallContext context, CmisService service, String repositoryId,
-            HttpServletRequest request, HttpServletResponse response) throws Exception {
-        // get parameters
-        String changeLogToken = getStringParameter(request, Constants.PARAM_CHANGE_LOG_TOKEN);
-        Boolean includeProperties = getBooleanParameter(request, Constants.PARAM_PROPERTIES);
-        String filter = getStringParameter(request, Constants.PARAM_FILTER);
-        Boolean includePolicyIds = getBooleanParameter(request, Constants.PARAM_POLICY_IDS);
-        Boolean includeAcl = getBooleanParameter(request, Constants.PARAM_ACL);
-        BigInteger maxItems = getBigIntegerParameter(request, Constants.PARAM_MAX_ITEMS);
-
-        // execute
-        Holder<String> changeLogTokenHolder = new Holder<String>(changeLogToken);
-        ObjectList changes = service.getContentChanges(repositoryId, changeLogTokenHolder, includeProperties, filter,
-                includePolicyIds, includeAcl, maxItems, null);
+    public static class GetContentChanges extends AbstractAtomPubServiceCall {
+        public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request,
+                HttpServletResponse response) throws Exception {
+            // get parameters
+            String changeLogToken = getStringParameter(request, Constants.PARAM_CHANGE_LOG_TOKEN);
+            Boolean includeProperties = getBooleanParameter(request, Constants.PARAM_PROPERTIES);
+            String filter = getStringParameter(request, Constants.PARAM_FILTER);
+            Boolean includePolicyIds = getBooleanParameter(request, Constants.PARAM_POLICY_IDS);
+            Boolean includeAcl = getBooleanParameter(request, Constants.PARAM_ACL);
+            BigInteger maxItems = getBigIntegerParameter(request, Constants.PARAM_MAX_ITEMS);
+
+            // execute
+            Holder<String> changeLogTokenHolder = new Holder<String>(changeLogToken);
+            ObjectList changes = service.getContentChanges(repositoryId, changeLogTokenHolder, includeProperties,
+                    filter, includePolicyIds, includeAcl, maxItems, null);
 
-        if (changes == null) {
-            throw new CmisRuntimeException("Changes are null!");
-        }
+            if (changes == null) {
+                throw new CmisRuntimeException("Changes are null!");
+            }
 
-        // set headers
-        response.setStatus(HttpServletResponse.SC_OK);
-        response.setContentType(Constants.MEDIATYPE_FEED);
-
-        // write XML
-        AtomFeed feed = new AtomFeed();
-        feed.startDocument(response.getOutputStream(), getNamespaces(service));
-        feed.startFeed(true);
-
-        // write basic Atom feed elements
-        GregorianCalendar now = new GregorianCalendar();
-        feed.writeFeedElements("contentChanges", null, "", "Content Change", now, null, changes.getNumItems());
-
-        // write links
-        UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
-
-        feed.writeServiceLink(baseUrl.toString(), repositoryId);
-
-        UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_CHANGES, null);
-        selfLink.addParameter(Constants.PARAM_CHANGE_LOG_TOKEN, changeLogToken);
-        selfLink.addParameter(Constants.PARAM_PROPERTIES, includeProperties);
-        selfLink.addParameter(Constants.PARAM_FILTER, filter);
-        selfLink.addParameter(Constants.PARAM_POLICY_IDS, includePolicyIds);
-        selfLink.addParameter(Constants.PARAM_ACL, includeAcl);
-        selfLink.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
-        feed.writeSelfLink(selfLink.toString(), null);
-
-        if (changeLogTokenHolder.getValue() != null) {
-            UrlBuilder nextLink = compileUrlBuilder(baseUrl, RESOURCE_CHANGES, null);
-            nextLink.addParameter(Constants.PARAM_CHANGE_LOG_TOKEN, changeLogTokenHolder.getValue());
-            nextLink.addParameter(Constants.PARAM_PROPERTIES, includeProperties);
-            nextLink.addParameter(Constants.PARAM_FILTER, filter);
-            nextLink.addParameter(Constants.PARAM_POLICY_IDS, includePolicyIds);
-            nextLink.addParameter(Constants.PARAM_ACL, includeAcl);
-            nextLink.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
-            feed.writeNextLink(nextLink.toString());
-        }
+            // set headers
+            response.setStatus(HttpServletResponse.SC_OK);
+            response.setContentType(Constants.MEDIATYPE_FEED);
+
+            // write XML
+            AtomFeed feed = new AtomFeed();
+            feed.startDocument(response.getOutputStream(), getNamespaces(service));
+            feed.startFeed(true);
+
+            // write basic Atom feed elements
+            GregorianCalendar now = new GregorianCalendar();
+            feed.writeFeedElements("contentChanges", null, "", "Content Change", now, null, changes.getNumItems());
+
+            // write links
+            UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
+
+            feed.writeServiceLink(baseUrl.toString(), repositoryId);
+
+            UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_CHANGES, null);
+            selfLink.addParameter(Constants.PARAM_CHANGE_LOG_TOKEN, changeLogToken);
+            selfLink.addParameter(Constants.PARAM_PROPERTIES, includeProperties);
+            selfLink.addParameter(Constants.PARAM_FILTER, filter);
+            selfLink.addParameter(Constants.PARAM_POLICY_IDS, includePolicyIds);
+            selfLink.addParameter(Constants.PARAM_ACL, includeAcl);
+            selfLink.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
+            feed.writeSelfLink(selfLink.toString(), null);
+
+            if (changeLogTokenHolder.getValue() != null) {
+                UrlBuilder nextLink = compileUrlBuilder(baseUrl, RESOURCE_CHANGES, null);
+                nextLink.addParameter(Constants.PARAM_CHANGE_LOG_TOKEN, changeLogTokenHolder.getValue());
+                nextLink.addParameter(Constants.PARAM_PROPERTIES, includeProperties);
+                nextLink.addParameter(Constants.PARAM_FILTER, filter);
+                nextLink.addParameter(Constants.PARAM_POLICY_IDS, includePolicyIds);
+                nextLink.addParameter(Constants.PARAM_ACL, includeAcl);
+                nextLink.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
+                feed.writeNextLink(nextLink.toString());
+            }
 
-        // write entries
-        if (changes.getObjects() != null) {
-            AtomEntry entry = new AtomEntry(feed.getWriter());
-            for (ObjectData object : changes.getObjects()) {
-                if (object == null) {
-                    continue;
+            // write entries
+            if (changes.getObjects() != null) {
+                AtomEntry entry = new AtomEntry(feed.getWriter());
+                for (ObjectData object : changes.getObjects()) {
+                    if (object == null) {
+                        continue;
+                    }
+                    writeContentChangesObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl,
+                            false, context.getCmisVersion());
                 }
-                writeContentChangesObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl, false,
-                        context.getCmisVersion());
             }
-        }
 
-        // we are done
-        feed.endFeed();
-        feed.endDocument();
+            // we are done
+            feed.endFeed();
+            feed.endDocument();
+        }
     }
 }