public void serve(
        CallContext context,
        CmisService service,
        String repositoryId,
        HttpServletRequest request,
        HttpServletResponse response)
        throws Exception {
      assert context != null;
      assert service != null;
      assert repositoryId != null;
      assert request != null;
      assert response != null;

      // get parameters
      String 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
      if (stopBeforeService(service)) {
        return;
      }

      Holder<String> changeLogTokenHolder = new Holder<String>(changeLogToken);
      ObjectList changes =
          service.getContentChanges(
              repositoryId,
              changeLogTokenHolder,
              includeProperties,
              filter,
              includePolicyIds,
              includeAcl,
              maxItems,
              null);

      if (stopAfterService(service)) {
        return;
      }

      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) {
        if (Boolean.TRUE.equals(changes.hasMoreItems())) {
          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());
        }

        // The CMIS spec says that the AtomPub binding doesn't provide
        // the change log token. We are doing it anyway.
        XMLStreamWriter writer = feed.getWriter();
        writer.writeStartElement(
            XMLConstants.PREFIX_APACHE_CHEMISTY,
            "changeLogToken",
            XMLConstants.NAMESPACE_APACHE_CHEMISTRY);
        writer.writeNamespace(
            XMLConstants.PREFIX_APACHE_CHEMISTY, XMLConstants.NAMESPACE_APACHE_CHEMISTRY);
        writer.writeCharacters(changeLogTokenHolder.getValue());
        writer.writeEndElement();
      }

      // 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());
        }
      }

      // write extensions
      feed.writeExtensions(changes);

      // we are done
      feed.endFeed();
      feed.endDocument();
    }