You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@abdera.apache.org by jm...@apache.org on 2007/10/02 23:45:36 UTC

svn commit: r581397 - in /incubator/abdera/java/trunk: server/ server/src/main/java/org/apache/abdera/protocol/server/content/ server/src/main/java/org/apache/abdera/protocol/server/impl/ server/src/test/java/org/apache/abdera/server/ server/src/test/j...

Author: jmsnell
Date: Tue Oct  2 14:45:34 2007
New Revision: 581397

URL: http://svn.apache.org/viewvc?rev=581397&view=rev
Log:
Dan's server module updates. This is still a work in progress

Added:
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractCollectionProvider.java
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractWorkspaceProvider.java
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/CollectionProvider.java
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/SimpleWorkspaceInfo.java
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceInfo.java
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceProvider.java
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/ProviderSupport.java
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/ContentProviderTest.java
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/Customer.java
    incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/CustomerContentProvider.java
Modified:
    incubator/abdera/java/trunk/server/pom.xml
    incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/AbstractProvider.java
    incubator/abdera/java/trunk/spring/src/main/resources/META-INF/schemas/abdera-spring.xsd

Modified: incubator/abdera/java/trunk/server/pom.xml
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/pom.xml?rev=581397&r1=581396&r2=581397&view=diff
==============================================================================
--- incubator/abdera/java/trunk/server/pom.xml (original)
+++ incubator/abdera/java/trunk/server/pom.xml Tue Oct  2 14:45:34 2007
@@ -69,5 +69,17 @@
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.mortbay.jetty</groupId>
+      <artifactId>jetty</artifactId>
+      <version>6.1.5</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-client</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>   
   </dependencies>
 </project>

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractCollectionProvider.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractCollectionProvider.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractCollectionProvider.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractCollectionProvider.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,397 @@
+package org.apache.abdera.protocol.server.content;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+
+import javax.activation.MimeType;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Content;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.parser.ParseException;
+import org.apache.abdera.parser.Parser;
+import org.apache.abdera.protocol.server.RequestContext;
+import org.apache.abdera.protocol.server.ResponseContext;
+import org.apache.abdera.protocol.server.impl.AbstractResponseContext;
+import org.apache.abdera.protocol.server.impl.BaseResponseContext;
+import org.apache.abdera.protocol.server.impl.EmptyResponseContext;
+import org.apache.abdera.protocol.server.impl.MediaResponseContext;
+import org.apache.abdera.protocol.server.impl.ProviderSupport;
+import org.apache.abdera.util.EntityTag;
+import org.apache.abdera.util.MimeTypeHelper;
+
+public abstract class AbstractCollectionProvider<T> extends ProviderSupport
+  implements CollectionProvider<T> {
+
+  private String baseMediaIri = "media/";
+  
+  public ResponseContext createEntry(RequestContext request) {
+    try {
+      MimeType contentType = request.getContentType();
+      if (isMediaType(contentType))
+        return createMediaEntry(request);
+      else
+        return createNonMediaEntry(request);
+    } catch (ParseException pe) {
+      return new EmptyResponseContext(415);
+    } catch (ClassCastException cce) {
+      return new EmptyResponseContext(415);
+    } catch (IOException e) {
+      e.printStackTrace();
+      return new EmptyResponseContext(500);
+    }
+  }
+
+  protected boolean isMediaType(MimeType contentType) {
+    String ctype = (contentType != null) ? contentType.toString() : null;
+    return ctype != null && !MimeTypeHelper.isAtom(ctype) /*&& !MimeTypeHelper.isXml(ctype)*/;
+  }
+  
+  public abstract T createEntry(String title, String summary, Content content);
+  
+  public T createMediaEntry(MimeType mimeType, String slug, InputStream inputStream) {
+    throw new UnsupportedOperationException();
+  }
+  
+  public ResponseContext deleteEntry(RequestContext request) {
+    String id = getEntryID(request);
+    if (id != null) {
+
+      deleteEntry(id);
+
+      return new EmptyResponseContext(204);
+    } else {
+      // TODO: is this right?
+      return new EmptyResponseContext(404);
+    }
+  }
+
+  public abstract void deleteEntry(String resourceName);
+
+  public abstract String getAuthor();
+
+  public String getBaseMediaIri() {
+    return baseMediaIri;
+  }
+  
+  public abstract Object getContent(T entry);
+  
+  // GET, POST, PUT, DELETE
+  
+  public String getContentType(T entry) {
+    throw new UnsupportedOperationException();
+  }
+  
+  public abstract Iterable<T> getEntries();
+  
+  public ResponseContext getEntry(RequestContext request, IRI entryBaseIri) {
+    Entry entry = getEntryFromCollectionProvider(entryBaseIri,
+                                                 request);
+    if (entry != null) {
+      Feed feed = createDocumentsFeed(request.getAbdera());
+      entry.setSource(feed.getAsSource());
+      Document<Entry> entry_doc = entry.getDocument();
+      AbstractResponseContext rc = new BaseResponseContext<Document<Entry>>(entry_doc);
+      rc.setEntityTag(calculateEntityTag(entry));
+      return rc;
+    } else {
+      return new EmptyResponseContext(404);
+    }
+  }
+  
+  public abstract T getEntry(String resourceName);
+  
+  public abstract T getEntryFromId(String id);
+
+
+  public ResponseContext getFeed(RequestContext request) {
+    Abdera abdera = request.getAbdera();
+    Feed feed = createDocumentsFeed(abdera);
+    
+    return getFeed(request, feed);
+  }
+  
+  public ResponseContext getFeed(RequestContext request, Feed feed) {
+    feed.setUpdated(new Date());
+
+    for (T entryObj : getEntries()) {
+      Entry e = feed.addEntry();
+
+      addEntryDetails(request, e, resolveBase(request), entryObj);
+      addContent(e, entryObj);
+    }
+
+    Document<Feed> document = feed.getDocument();
+    AbstractResponseContext rc = new BaseResponseContext<Document<Feed>>(document);
+    rc.setEntityTag(calculateEntityTag(document.getRoot()));
+    return rc;
+  }
+
+  public abstract String getId();
+  /**
+   * Gets the UUID for the specified entry.
+   * @param entry
+   * @return
+   */
+  public abstract String getId(T entry);
+
+  public ResponseContext getMedia(RequestContext request) {
+    try {
+      String id = getEntryID(request);
+      T entryObj = getEntry(id);
+
+      if (entryObj == null) {
+        return new EmptyResponseContext(404);
+      }
+
+      MediaResponseContext ctx = new MediaResponseContext(getMediaStream(entryObj), 
+                                                          getUpdated(entryObj), 
+                                                          200);
+      ctx.setContentType(getContentType(entryObj));
+
+      return ctx;
+    } catch (ParseException pe) {
+      return new EmptyResponseContext(415);
+    } catch (ClassCastException cce) {
+      return new EmptyResponseContext(415);
+    } catch (Exception e) {
+      return new EmptyResponseContext(400);
+    }
+  }
+  public String getMediaName(T entry) {
+    throw new UnsupportedOperationException();
+  }
+
+  public InputStream getMediaStream(T gdoc) {
+    throw new UnsupportedOperationException();
+  }
+  
+  public abstract String getName(T entry);
+
+  public abstract String getTitle();
+
+  public abstract String getTitle(T entry);
+
+  public abstract Date getUpdated(T entry);
+
+  public boolean isMediaEntry(T entry) {
+    return false;
+  }
+
+  public void setBaseMediaIri(String baseMediaIri) {
+    this.baseMediaIri = baseMediaIri;
+  }
+
+  public ResponseContext updateEntry(RequestContext request, IRI feedUri) {
+    Abdera abdera = request.getAbdera();
+    Entry orig_entry = getEntryFromCollectionProvider(feedUri, request);
+    if (orig_entry != null) {
+      try {
+        MimeType contentType = request.getContentType();
+        if (contentType != null && !MimeTypeHelper.isAtom(contentType.toString()))
+          return new EmptyResponseContext(415);
+
+        Entry entry = getEntryFromRequest(request);
+        if (entry != null) {
+          if (!entry.getId().equals(orig_entry.getId()))
+            return new EmptyResponseContext(409);
+
+          if (!isValidEntry(entry))
+            return new EmptyResponseContext(400);
+
+          entry.setUpdated(new Date());
+          // TODO: is this really necessary?
+          // entry.getIdElement().setValue(factory.newUuidUri());
+          entry.addLink(feedUri.resolve(entry.getId().toString()).toString(), "edit");
+
+          // entryObjProvider.updateEntry(entry)
+
+          Feed feed = createDocumentsFeed(abdera);
+          feed.insertEntry(entry);
+          feed.setUpdated(new Date());
+          return new EmptyResponseContext(204);
+        } else {
+          return new EmptyResponseContext(400);
+        }
+      } catch (ParseException pe) {
+        return new EmptyResponseContext(415);
+      } catch (ClassCastException cce) {
+        return new EmptyResponseContext(415);
+      } catch (Exception e) {
+        return new EmptyResponseContext(400);
+      }
+    } else {
+      return new EmptyResponseContext(404);
+    }
+  }
+
+  public abstract T updateEntry(T entry, Content content);
+
+  protected void addContent(Entry e, T doc) {
+    Object content = getContent(doc);
+
+    if (content instanceof Content) {
+      e.setContentElement((Content)content);
+    } else if (content instanceof String) {
+      e.setContent((String)content);
+    }
+  }
+
+  protected void addEntryDetails(RequestContext request, Entry e, 
+                               IRI entryBaseIri, T entryObj) {
+    IRI entryIri = entryBaseIri.resolve(getName(entryObj));
+    e.addLink(entryIri.toString(), "edit");
+    e.setId(getId(entryObj));
+    e.setTitle(getTitle(entryObj));
+    e.setUpdated(getUpdated(entryObj));
+  }
+
+  protected void addMediaContent(IRI entryBaseIri, Entry entry, T doc) {
+    String name = getMediaName(doc);
+    IRI mediaIri = getMediaIRI(entryBaseIri, name);
+    mediaIri = entryBaseIri.resolve(mediaIri);
+
+    entry.setContent(mediaIri, getContentType(doc));
+    entry.addLink(mediaIri.toString(), "edit-media");
+  }
+
+  protected EntityTag calculateEntityTag(Base base) {
+    String id = null;
+    String modified = null;
+    if (base instanceof Entry) {
+      id = ((Entry)base).getId().toString();
+      modified = ((Entry)base).getUpdatedElement().getText();
+    } else if (base instanceof Feed) {
+      id = ((Feed)base).getId().toString();
+      modified = ((Feed)base).getUpdatedElement().getText();
+    }
+    return EntityTag.generate(id, modified);
+  }
+  protected Feed createDocumentsFeed(Abdera abdera) {
+    Factory factory = abdera.getFactory();
+    Feed feed = factory.newFeed();
+    try {
+      feed.setId(getId());
+      feed.setTitle(getTitle());
+      feed.addLink("");
+      feed.addLink("", "self");
+      feed.addAuthor(getAuthor());
+    } catch (Exception e) {
+    }
+    return feed;
+  }
+
+  protected ResponseContext createMediaEntry(RequestContext request) {
+    try {
+      T doc = createMediaEntry(request.getContentType(), request.getSlug(), request.getInputStream());
+
+      IRI baseIri = resolveBase(request);
+      IRI entryIri = getEntryBaseFromFeedIRI(baseIri);
+
+      Entry entry = request.getAbdera().getFactory().newEntry();
+
+      addEntryDetails(request, entry, entryIri, doc);
+
+      addMediaContent(entryIri, entry, doc);
+
+      BaseResponseContext<Entry> rc = new BaseResponseContext<Entry>(entry);
+      rc.setLocation(entryIri.resolve(entry.getEditLinkResolvedHref()).toString());
+      rc.setContentLocation(rc.getLocation().toString());
+      rc.setEntityTag(calculateEntityTag(entry));
+      rc.setStatus(201);
+      return rc;
+    } catch (IOException e) {
+      return new EmptyResponseContext(500);
+    }
+  }
+
+  protected ResponseContext createNonMediaEntry(RequestContext request)
+    throws IOException {
+    Entry entry = getEntryFromRequest(request);
+    if (entry != null) {
+      if (!isValidEntry(entry))
+        return new EmptyResponseContext(400);
+
+      entry.setUpdated(new Date());
+      
+      T entryObj = createEntry(entry.getTitle(), entry.getSummary(), 
+                                        entry.getContentElement());
+      entry.getIdElement().setValue(getId(entryObj));
+      
+      IRI entryBaseUri = getEntryBaseFromFeedIRI(resolveBase(request));
+      
+      IRI entryIri = entryBaseUri.resolve(getName(entryObj));
+      entry.addLink(entryIri.toString(), "edit");
+
+      BaseResponseContext<Entry> rc = new BaseResponseContext<Entry>(entry);
+      rc.setLocation(entryIri.resolve(entry.getEditLinkResolvedHref()).toString());
+      rc.setContentLocation(rc.getLocation().toString());
+      rc.setEntityTag(calculateEntityTag(entry));
+      rc.setStatus(201);
+      return rc;
+    } else {
+      return new EmptyResponseContext(400);
+    }
+  }
+
+  protected IRI getEntryBaseFromFeedIRI(IRI baseIri) {
+    return new IRI(baseIri + "/");
+  }
+
+  protected Entry getEntryFromCollectionProvider(IRI feedIri, RequestContext request) {
+
+    String id = getEntryID(request);
+    T entryObj = getEntry(id);
+
+    if (entryObj == null) {
+      return null;
+    }
+
+    Abdera abdera = request.getAbdera();
+    Factory factory = abdera.getFactory();
+    Entry entry = factory.newEntry();
+
+    addEntryDetails(request, entry, feedIri, entryObj);
+
+    if (isMediaEntry(entryObj)) {
+      addMediaContent(feedIri, entry, entryObj);
+    } else {
+      addContent(entry, entryObj);
+    }
+
+    return entry;
+  }
+
+  @SuppressWarnings("unchecked")
+  protected Entry getEntryFromRequest(RequestContext request) throws IOException {
+    Abdera abdera = request.getAbdera();
+    Parser parser = abdera.getParser();
+
+    Document<Entry> entry_doc = (Document<Entry>)request.getDocument(parser).clone();
+    if (entry_doc == null) {
+      return null;
+    }
+    return entry_doc.getRoot();
+  }
+
+  protected String getEntryID(RequestContext request) {
+    String path = request.getUri().toString();
+    String[] segments = path.split("/");
+    return segments[segments.length - 1];
+  }
+
+  protected IRI getMediaIRI(IRI entryBaseIri, String name) {
+    return entryBaseIri.resolve(getBaseMediaIri()).resolve(name);
+  }
+
+  @Override
+  protected IRI resolveBase(RequestContext request) {
+    return request.getBaseUri().resolve(request.getUri());
+  }
+}

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractWorkspaceProvider.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractWorkspaceProvider.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractWorkspaceProvider.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/AbstractWorkspaceProvider.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,134 @@
+package org.apache.abdera.protocol.server.content;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Collection;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Service;
+import org.apache.abdera.model.Workspace;
+import org.apache.abdera.protocol.server.RequestContext;
+import org.apache.abdera.protocol.server.ResponseContext;
+import org.apache.abdera.protocol.server.impl.AbstractProvider;
+import org.apache.abdera.protocol.server.impl.AbstractResponseContext;
+import org.apache.abdera.protocol.server.impl.BaseResponseContext;
+import org.apache.abdera.util.EntityTag;
+
+public abstract class AbstractWorkspaceProvider extends AbstractProvider {
+
+    private EntityTag service_etag = new EntityTag("simple");
+    
+    protected AbstractWorkspaceProvider(int count) {
+      super(count);
+    }
+
+    public ResponseContext getService(RequestContext request) {
+      Abdera abdera = request.getAbdera();
+      Document<Service> service = getServicesDocument(abdera);
+      AbstractResponseContext rc = new BaseResponseContext<Document<Service>>(service);
+      rc.setEntityTag(service_etag);
+      return rc;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Document<Service> getServicesDocument(Abdera abdera) {
+      Factory factory = abdera.getFactory();
+      Service service = factory.newService();
+      for (WorkspaceInfo wp : getWorkspaces()) {
+        Workspace workspace = service.addWorkspace(wp.getName());
+        Set<Map.Entry<String, CollectionProvider>> entrySet = 
+          (Set<Map.Entry<String, CollectionProvider>>) (wp.getCollectionProviders().entrySet());
+        for (Map.Entry<String, CollectionProvider> entry : entrySet) {
+          CollectionProvider<?> cp = entry.getValue();
+          Collection collection = workspace.addCollection(cp.getTitle(), 
+                                                          wp.getId() + "/" + entry.getKey());
+          collection.setAccept("entry");
+          // collection.addCategories().setFixed(false);
+        }
+      }
+      return service.getDocument();
+    }
+
+    public abstract java.util.Collection<WorkspaceInfo> getWorkspaces();
+
+    public ResponseContext getFeed(RequestContext request) {
+      CollectionProvider provider = getCollectionProvider(resolveBase(request), request);
+      
+      return provider.getFeed(request);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> CollectionProvider<T> getCollectionProvider(IRI resolveBase, 
+                                                      RequestContext request) {
+      String path = resolveBase.getPath();
+      String[] paths = path.split("/");
+      if (paths.length < 1) {
+        // TODO:
+        throw new RuntimeException();
+      } else if (paths.length == 1) {
+        WorkspaceInfo wp = getWorkspaceInfo("");
+        if (wp == null) {
+          // TODO: 404
+          throw new RuntimeException();
+        }
+        return wp.getCollectionProvider(paths[0]);
+      } else {    
+        WorkspaceInfo wp = getWorkspaceInfo(paths[paths.length - 2]);
+        if (wp == null) {
+          // TODO: 404
+          throw new RuntimeException();
+        }
+        return wp.getCollectionProvider(paths[paths.length - 1]);
+      }
+    }
+
+    protected abstract WorkspaceInfo<?> getWorkspaceInfo(String string);
+
+    public ResponseContext createEntry(RequestContext request) {
+      CollectionProvider provider = getCollectionProvider(request.getUri(), request);
+      
+      return provider.createEntry(request);
+    }
+
+    @Override
+    public ResponseContext getMedia(RequestContext request) {
+      IRI entryBaseIri = resolveBase(request).resolve("../");
+      CollectionProvider provider = getCollectionProvider(entryBaseIri, request);
+
+      return provider.getMedia(request);
+    }
+
+
+    @Override
+    public ResponseContext updateMedia(RequestContext request) {
+      // TODO Auto-generated method stub
+      return super.updateMedia(request);
+    }
+
+    public ResponseContext deleteEntry(RequestContext request) {
+      CollectionProvider<?> provider = getCollectionProvider(resolveBase(request).resolve("./"), request);
+
+      return provider.deleteEntry(request);
+    }
+
+    
+    public ResponseContext getEntry(RequestContext request) {
+      IRI entryBaseIri = resolveBase(request).resolve("./");
+      CollectionProvider provider = getCollectionProvider(entryBaseIri, request);
+
+      return provider.getEntry(request, entryBaseIri);
+    }
+
+    @SuppressWarnings("unchecked")
+    public ResponseContext updateEntry(RequestContext request) {
+      IRI entryBaseIri = resolveBase(request).resolve("./");
+      CollectionProvider provider = getCollectionProvider(entryBaseIri, request);
+
+      return provider.updateEntry(request, entryBaseIri);
+    }
+
+
+}

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/CollectionProvider.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/CollectionProvider.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/CollectionProvider.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/CollectionProvider.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,32 @@
+package org.apache.abdera.protocol.server.content;
+
+import java.io.InputStream;
+import java.util.Date;
+
+import javax.activation.MimeType;
+
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Content;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.protocol.server.RequestContext;
+import org.apache.abdera.protocol.server.ResponseContext;
+
+public interface CollectionProvider<T> {
+    
+    String getTitle();
+    
+    ResponseContext getFeed(RequestContext request, Feed feed);
+
+    ResponseContext createEntry(RequestContext request);
+
+    ResponseContext getMedia(RequestContext request);
+    
+    ResponseContext deleteEntry(RequestContext request);
+
+    ResponseContext getEntry(RequestContext request, IRI entryBaseIri);
+
+    ResponseContext getFeed(RequestContext request);
+
+    ResponseContext updateEntry(RequestContext request, IRI entryBaseIri);
+    
+}

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/SimpleWorkspaceInfo.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/SimpleWorkspaceInfo.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/SimpleWorkspaceInfo.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/SimpleWorkspaceInfo.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,42 @@
+package org.apache.abdera.protocol.server.content;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class SimpleWorkspaceInfo implements WorkspaceInfo {
+    private Map<String, CollectionProvider> collectionProviders;
+    private String name;
+    private String id;
+    
+    /* (non-Javadoc)
+     * @see org.apache.abdera.protocol.server.content.WorkspaceInfo#getId()
+     */
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public CollectionProvider getCollectionProvider(String id) {
+      return collectionProviders.get(id);
+    }
+
+    public Map<String, CollectionProvider> getCollectionProviders() {
+        return collectionProviders;
+    }
+
+    public void setCollectionProviders(Map<String, CollectionProvider> contentProviders) {
+        this.collectionProviders = contentProviders;
+    }
+
+    
+}

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceInfo.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceInfo.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceInfo.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceInfo.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,15 @@
+package org.apache.abdera.protocol.server.content;
+
+import java.util.Map;
+
+public interface WorkspaceInfo<T> {
+
+  String getId();
+
+  String getName();
+
+  Map<String, CollectionProvider<T>> getCollectionProviders();
+
+  CollectionProvider<T> getCollectionProvider(String id);
+
+}

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceProvider.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceProvider.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceProvider.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/content/WorkspaceProvider.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,30 @@
+package org.apache.abdera.protocol.server.content;
+
+import java.util.Collection;
+
+public class WorkspaceProvider extends AbstractWorkspaceProvider {
+
+  private Collection<WorkspaceInfo> workspaces;
+  
+  public WorkspaceProvider() {
+    super(10);
+  }
+  
+  public WorkspaceInfo<?> getWorkspaceInfo(String id) {
+    for (WorkspaceInfo wp : workspaces) {
+      if (wp.getId().equals(id)) {
+        return wp;
+      }
+    }
+    return null;
+  }
+
+  public Collection<WorkspaceInfo> getWorkspaces() {
+    return workspaces;
+  }
+
+  public void setWorkspaces(Collection<WorkspaceInfo> workspaces) {
+    this.workspaces = workspaces;
+  }
+
+}

Modified: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/AbstractProvider.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/AbstractProvider.java?rev=581397&r1=581396&r2=581397&view=diff
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/AbstractProvider.java (original)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/AbstractProvider.java Tue Oct  2 14:45:34 2007
@@ -17,33 +17,20 @@
 */
 package org.apache.abdera.protocol.server.impl;
 
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.List;
 
-import javax.xml.namespace.QName;
 
-import org.apache.abdera.Abdera;
-import org.apache.abdera.model.Base;
 import org.apache.abdera.model.Categories;
-import org.apache.abdera.model.Content;
-import org.apache.abdera.model.Document;
-import org.apache.abdera.model.Element;
-import org.apache.abdera.model.Entry;
-import org.apache.abdera.model.ExtensibleElement;
-import org.apache.abdera.protocol.error.Error;
 import org.apache.abdera.protocol.server.Provider;
 import org.apache.abdera.protocol.server.RequestContext;
 import org.apache.abdera.protocol.server.ResponseContext;
 import org.apache.abdera.protocol.server.TargetType;
-import org.apache.abdera.protocol.util.EncodingUtil;
 import org.apache.abdera.util.Messages;
-import org.apache.abdera.util.MimeTypeHelper;
-import org.apache.abdera.i18n.iri.IRI;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 public abstract class AbstractProvider 
+  extends ProviderSupport
   implements Provider {
 
   private final static Log log = LogFactory.getLog(AbstractProvider.class);
@@ -56,353 +43,6 @@
     this.defaultpagesize = defaultpagesize;
   }
   
-  protected Document<Error> createErrorDocument(
-    Abdera abdera, 
-    int code, 
-    String message, 
-    Throwable e) {
-      Error error = Error.create(abdera,code,message);
-      return error.getDocument();
-  }
-
-  /**
-   * Return a server error
-   */
-  protected ResponseContext servererror(
-    Abdera abdera,
-    RequestContext request,
-    String reason,
-    Throwable t) {
-      log.debug(Messages.get("SERVER_ERROR"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 500, 
-          reason, t), 
-        500, null);
-  }
-     
-  /**
-   * Return an unauthorized error
-   */
-  protected ResponseContext unauthorized(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("UNAUTHORIZED"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 401, 
-          reason, null), 
-        401, null);
-  }
-  
-  /**
-   * Return an unauthorized error
-   */
-  protected ResponseContext forbidden(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("FORBIDDEN"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 403, 
-          reason, null), 
-        403, null);
-  }
-  
-  /**
-   * Return a 404 not found error
-   */
-  protected ResponseContext unknown(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-    log.debug(Messages.get("UNKNOWN"));
-    return returnBase(
-      createErrorDocument(
-        abdera, 404, 
-        reason, null), 
-      404, null);
-  }
-  
-  /**
-   * Return a 405 method not allowed error
-   */
-  protected ResponseContext notallowed(
-    Abdera abdera, 
-    RequestContext request,
-    String reason,
-    String... methods) {
-      log.debug(Messages.get("NOT.ALLOWED")); 
-      BaseResponseContext resp = 
-        (BaseResponseContext)returnBase(
-          createErrorDocument(
-            abdera, 405, 
-            reason, null), 
-          405, null);
-        resp.setAllow(methods);
-        return resp;
-  }
-  
-  /**
-   * Return a 400 bad request error
-   */
-  protected ResponseContext badrequest(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("BAD.REQUEST"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 400, 
-          reason, null), 
-        400, null);
-  }
-  
-  /**
-   * Return a 409 conflict error
-   */
-  protected ResponseContext conflict(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-    log.debug(Messages.get("CONFLICT"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 409, 
-          reason, null), 
-        409, null);
-  }
-  
-  /**
-   * Return a service unavailable error
-   */
-  protected ResponseContext unavailable(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("UNAVAILABLE"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 503, 
-          reason, null), 
-        503, null);
-  }
-  
-  protected ResponseContext notmodified(
-    Abdera abdera, 
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("NOT.MODIFIED"));
-      EmptyResponseContext rc = new EmptyResponseContext(304);
-      rc.setStatusText(reason);
-      return rc;
-  }
-
-  protected ResponseContext preconditionfailed(
-    Abdera abdera, 
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("PRECONDITION.FAILED"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 412, 
-          reason, null), 
-        412, null);
-  }
-  
-  /**
-   * Return a 415 media type not-supported error
-   */
-  protected ResponseContext notsupported(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("NOT.SUPPORTED"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 415, 
-          reason, null),
-        415,null);
-  }
-
-  /**
-   * Return a 423 locked error
-   */
-  protected ResponseContext locked(
-    Abdera abdera,
-    RequestContext request,
-    String reason) {
-      log.debug(Messages.get("LOCKED"));
-      return returnBase(
-        createErrorDocument(
-          abdera, 423,
-          reason, null),
-        423,null);
-    }
-
-  /**
-   * Return a document
-   */
-  @SuppressWarnings("unchecked")
-  protected ResponseContext returnBase(
-    Base base, 
-    int status,
-    Date lastModified) {
-      log.debug(Messages.get("RETURNING.DOCUMENT"));
-      BaseResponseContext response = new BaseResponseContext(base);
-      response.setStatus(status);
-      if (lastModified != null) response.setLastModified(lastModified);
-      response.setContentType(MimeTypeHelper.getMimeType(base));
-      Document doc = base instanceof Document ? (Document)base : ((Element)base).getDocument();
-      if (doc.getEntityTag() != null) {
-        response.setEntityTag(doc.getEntityTag());
-      } else if (doc.getLastModified() != null) {
-        response.setLastModified(doc.getLastModified());
-      }
-      return response;
-  }
-
-  /**
-   * Sanitize the value of a Slug header. Any non alphanumeric characters in
-   * the slug are replaced with an underscore
-   */
-  protected String sanitizeSlug(String slug) {
-    if (slug == null) throw new IllegalArgumentException(Messages.get("SLUG.NOT.NULL"));
-    String sanitized = EncodingUtil.sanitize(slug);
-    log.debug(Messages.format("SLUG.SANITIZED", slug, sanitized));
-    return sanitized;
-  }
-
-  protected int getDefaultPageSize() {
-    log.debug(Messages.format("DEFAULT.PAGE.SIZE",defaultpagesize));
-    return defaultpagesize;
-  }
-  
-
-  protected int getPageSize(
-    RequestContext request, 
-    String pagesizeparam) {
-      int max = getDefaultPageSize();
-      int size = max;
-      try {
-        String _ps = request.getParameter(pagesizeparam);
-        size = (_ps != null) ? 
-          Math.min(Math.max(Integer.parseInt(_ps),0),max) : max;
-      } catch (Exception e) {}
-      log.debug(Messages.format("PAGE.SIZE",size));
-      return size;
-  }
-  
-  protected int getOffset(
-    RequestContext request, 
-    String pageparam, 
-    int pageSize) {
-      int offset = 0;
-      try {
-        String _page = request.getParameter(pageparam);
-        int page =(_page != null) ? Integer.parseInt(_page) : 1;
-        page = Math.max(page, 1) - 1;
-        offset = pageSize * page;
-      } catch (Exception e) {}
-      log.debug(Messages.format("OFFSET",offset));
-      return offset;
-  }
-  
-  /**
-   * Check to see if the entry is minimally valid according to RFC4287.
-   * This is not a complete check.  It just verifies that the appropriate
-   * elements are present and that their values can be accessed.
-   */
-  protected static boolean isValidEntry(Entry entry) {
-    try {
-      IRI id = entry.getId();
-      if (id == null || 
-          id.toString().trim().length() == 0 ||
-          !id.isAbsolute()) return false;
-      if (entry.getTitle() == null) return false;
-      if (entry.getAuthor() == null) return false;
-      if (entry.getUpdated() == null) return false;
-      Content content = entry.getContentElement();
-      if (content == null) {
-        if (entry.getAlternateLink() == null) return false;
-      } else {
-        if ((content.getSrc() != null ||
-            content.getContentType() == Content.Type.MEDIA || 
-            content.getContentType() == Content.Type.XML) &&
-            entry.getSummary() == null) {
-          log.debug(Messages.format("CHECKING.VALID.ENTRY",false));
-          return false;
-        }
-      }
-    } catch (Exception e) {
-      log.debug(Messages.format("CHECKING.VALID.ENTRY",false));
-      return false;
-    }
-    log.debug(Messages.format("CHECKING.VALID.ENTRY",true));
-    return true;
-  }
-  
-
-  /**
-   * Implementations should override this method to add additional namespaces
-   * to the ignore list.
-   */
-  protected void checkEntryAddAdditionalNamespaces(List ignore) {}
-  
-  /**
-   * Checks the entry for unknown extension elements.  Returns false if the 
-   * entry contains any extension elements that are not supported
-   */
-  protected boolean checkEntryNamespaces(
-    RequestContext request, 
-    Entry entry) {
-      List<String> ignore = new ArrayList<String>();
-      ignore.add(org.apache.abdera.util.Constants.APP_NS);
-      ignore.add(org.apache.abdera.util.Constants.XHTML_NS);
-      ignore.add(org.apache.abdera.util.Constants.XML_NS);
-      checkEntryAddAdditionalNamespaces(ignore);
-      boolean answer = checkElement(entry,ignore);
-      log.debug(Messages.format("CHECKING.ENTRY.NAMESPACES",answer));
-      return answer;
-  }
-  
-  /**
-   * Return false if the element contains any extension elements that are not 
-   * supported
-   */
-  private boolean checkElement(
-    Element element, 
-    List ignore) {
-      List<QName> attrs = element.getExtensionAttributes();
-      for (QName qname : attrs) {
-        String ns = qname.getNamespaceURI();
-        if (!ignore.contains(ns)) return false; 
-      }
-      if (element instanceof ExtensibleElement) {
-        ExtensibleElement ext = (ExtensibleElement) element;
-        List<Element> extensions = ext.getExtensions();
-        for (Element el : extensions) {
-          QName qname = el.getQName();
-          String ns = qname.getNamespaceURI();
-          if (!ignore.contains(ns)) return false; 
-          if (!checkElement(el, ignore)) return false;
-        }
-      }
-      return true;
-  }
-  
-  protected static boolean beforeOrEqual(Date d1, Date d2) {
-    long l1 = d1.getTime() / 1000; // drop milliseconds
-    long l2 = d2.getTime() / 1000; // drop milliseconds
-    return l1 <= l2;
-  }
-  
-  protected IRI resolveBase(RequestContext request) {
-    return request.getBaseUri().resolve(request.getUri());
-  }
-
   public ResponseContext request(RequestContext request) {
     TargetType type = request.getTarget().getType();
     String method = request.getMethod();

Added: incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/ProviderSupport.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/ProviderSupport.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/ProviderSupport.java (added)
+++ incubator/abdera/java/trunk/server/src/main/java/org/apache/abdera/protocol/server/impl/ProviderSupport.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,395 @@
+package org.apache.abdera.protocol.server.impl;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Content;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.ExtensibleElement;
+import org.apache.abdera.protocol.error.Error;
+import org.apache.abdera.protocol.server.Provider;
+import org.apache.abdera.protocol.server.RequestContext;
+import org.apache.abdera.protocol.server.ResponseContext;
+import org.apache.abdera.protocol.server.content.CollectionProvider;
+import org.apache.abdera.protocol.util.EncodingUtil;
+import org.apache.abdera.util.Messages;
+import org.apache.abdera.util.MimeTypeHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Provides support methods for {@link Provider} and {@link CollectionProvider}.
+ */
+public class ProviderSupport {
+  private final static Log log = LogFactory.getLog(ProviderSupport.class);
+
+  protected int defaultpagesize = 10;
+  
+  protected ProviderSupport() {}
+  
+  protected ProviderSupport(int defaultpagesize) {
+    this.defaultpagesize = defaultpagesize;
+  }
+  
+  public int getDefaultpagesize() {
+    return defaultpagesize;
+  }
+
+  public void setDefaultpagesize(int defaultpagesize) {
+    this.defaultpagesize = defaultpagesize;
+  }
+
+  protected Document<Error> createErrorDocument(
+    Abdera abdera, 
+    int code, 
+    String message, 
+    Throwable e) {
+      Error error = Error.create(abdera,code,message);
+      return error.getDocument();
+  }
+
+  /**
+   * Return a server error
+   */
+  protected ResponseContext servererror(
+    Abdera abdera,
+    RequestContext request,
+    String reason,
+    Throwable t) {
+      log.debug(Messages.get("SERVER_ERROR"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 500, 
+          reason, t), 
+        500, null);
+  }
+
+  /**
+   * Return an unauthorized error
+   */
+  protected ResponseContext unauthorized(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("UNAUTHORIZED"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 401, 
+          reason, null), 
+        401, null);
+  }
+
+  /**
+   * Return an unauthorized error
+   */
+  protected ResponseContext forbidden(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("FORBIDDEN"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 403, 
+          reason, null), 
+        403, null);
+  }
+
+  /**
+   * Return a 404 not found error
+   */
+  protected ResponseContext unknown(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+    log.debug(Messages.get("UNKNOWN"));
+    return returnBase(
+      createErrorDocument(
+        abdera, 404, 
+        reason, null), 
+      404, null);
+  }
+
+  /**
+   * Return a 405 method not allowed error
+   */
+  protected ResponseContext notallowed(
+    Abdera abdera, 
+    RequestContext request,
+    String reason,
+    String... methods) {
+      log.debug(Messages.get("NOT.ALLOWED")); 
+      BaseResponseContext resp = 
+        (BaseResponseContext)returnBase(
+          createErrorDocument(
+            abdera, 405, 
+            reason, null), 
+          405, null);
+        resp.setAllow(methods);
+        return resp;
+  }
+
+  /**
+   * Return a 400 bad request error
+   */
+  protected ResponseContext badrequest(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("BAD.REQUEST"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 400, 
+          reason, null), 
+        400, null);
+  }
+
+  /**
+   * Return a 409 conflict error
+   */
+  protected ResponseContext conflict(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+    log.debug(Messages.get("CONFLICT"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 409, 
+          reason, null), 
+        409, null);
+  }
+
+  /**
+   * Return a service unavailable error
+   */
+  protected ResponseContext unavailable(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("UNAVAILABLE"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 503, 
+          reason, null), 
+        503, null);
+  }
+
+  protected ResponseContext notmodified(
+    Abdera abdera, 
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("NOT.MODIFIED"));
+      EmptyResponseContext rc = new EmptyResponseContext(304);
+      rc.setStatusText(reason);
+      return rc;
+  }
+
+  protected ResponseContext preconditionfailed(
+    Abdera abdera, 
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("PRECONDITION.FAILED"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 412, 
+          reason, null), 
+        412, null);
+  }
+
+  /**
+   * Return a 415 media type not-supported error
+   */
+  protected ResponseContext notsupported(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("NOT.SUPPORTED"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 415, 
+          reason, null),
+        415,null);
+  }
+
+  /**
+   * Return a 423 locked error
+   */
+  protected ResponseContext locked(
+    Abdera abdera,
+    RequestContext request,
+    String reason) {
+      log.debug(Messages.get("LOCKED"));
+      return returnBase(
+        createErrorDocument(
+          abdera, 423,
+          reason, null),
+        423,null);
+    }
+
+  /**
+   * Return a document
+   */
+  @SuppressWarnings("unchecked")
+  protected ResponseContext returnBase(
+    Base base, 
+    int status,
+    Date lastModified) {
+      log.debug(Messages.get("RETURNING.DOCUMENT"));
+      BaseResponseContext response = new BaseResponseContext(base);
+      response.setStatus(status);
+      if (lastModified != null) response.setLastModified(lastModified);
+      response.setContentType(MimeTypeHelper.getMimeType(base));
+      Document doc = base instanceof Document ? (Document)base : ((Element)base).getDocument();
+      if (doc.getEntityTag() != null) {
+        response.setEntityTag(doc.getEntityTag());
+      } else if (doc.getLastModified() != null) {
+        response.setLastModified(doc.getLastModified());
+      }
+      return response;
+  }
+
+  /**
+   * Sanitize the value of a Slug header. Any non alphanumeric characters in
+   * the slug are replaced with an underscore
+   */
+  protected String sanitizeSlug(String slug) {
+    if (slug == null) throw new IllegalArgumentException(Messages.get("SLUG.NOT.NULL"));
+    String sanitized = EncodingUtil.sanitize(slug);
+    log.debug(Messages.format("SLUG.SANITIZED", slug, sanitized));
+    return sanitized;
+  }
+
+  protected int getDefaultPageSize() {
+    log.debug(Messages.format("DEFAULT.PAGE.SIZE",defaultpagesize));
+    return defaultpagesize;
+  }
+
+  protected int getPageSize(
+    RequestContext request, 
+    String pagesizeparam) {
+      int max = getDefaultPageSize();
+      int size = max;
+      try {
+        String _ps = request.getParameter(pagesizeparam);
+        size = (_ps != null) ? 
+          Math.min(Math.max(Integer.parseInt(_ps),0),max) : max;
+      } catch (Exception e) {}
+      log.debug(Messages.format("PAGE.SIZE",size));
+      return size;
+  }
+
+  protected int getOffset(
+    RequestContext request, 
+    String pageparam, 
+    int pageSize) {
+      int offset = 0;
+      try {
+        String _page = request.getParameter(pageparam);
+        int page =(_page != null) ? Integer.parseInt(_page) : 1;
+        page = Math.max(page, 1) - 1;
+        offset = pageSize * page;
+      } catch (Exception e) {}
+      log.debug(Messages.format("OFFSET",offset));
+      return offset;
+  }
+
+  /**
+   * Check to see if the entry is minimally valid according to RFC4287.
+   * This is not a complete check.  It just verifies that the appropriate
+   * elements are present and that their values can be accessed.
+   */
+  protected static boolean isValidEntry(Entry entry) {
+    try {
+      IRI id = entry.getId();
+      if (id == null || 
+          id.toString().trim().length() == 0 ||
+          !id.isAbsolute()) return false;
+      if (entry.getTitle() == null) return false;
+      if (entry.getAuthor() == null) return false;
+      if (entry.getUpdated() == null) return false;
+      Content content = entry.getContentElement();
+      if (content == null) {
+        if (entry.getAlternateLink() == null) return false;
+      } else {
+        if ((content.getSrc() != null ||
+            content.getContentType() == Content.Type.MEDIA || 
+            content.getContentType() == Content.Type.XML) &&
+            entry.getSummary() == null) {
+          log.debug(Messages.format("CHECKING.VALID.ENTRY",false));
+          return false;
+        }
+      }
+    } catch (Exception e) {
+      log.debug(Messages.format("CHECKING.VALID.ENTRY",false));
+      return false;
+    }
+    log.debug(Messages.format("CHECKING.VALID.ENTRY",true));
+    return true;
+  }
+
+  /**
+   * Implementations should override this method to add additional namespaces
+   * to the ignore list.
+   */
+  protected void checkEntryAddAdditionalNamespaces(List ignore) {}
+
+  /**
+   * Checks the entry for unknown extension elements.  Returns false if the 
+   * entry contains any extension elements that are not supported
+   */
+  protected boolean checkEntryNamespaces(
+    RequestContext request, 
+    Entry entry) {
+      List<String> ignore = new ArrayList<String>();
+      ignore.add(org.apache.abdera.util.Constants.APP_NS);
+      ignore.add(org.apache.abdera.util.Constants.XHTML_NS);
+      ignore.add(org.apache.abdera.util.Constants.XML_NS);
+      checkEntryAddAdditionalNamespaces(ignore);
+      boolean answer = checkElement(entry,ignore);
+      log.debug(Messages.format("CHECKING.ENTRY.NAMESPACES",answer));
+      return answer;
+  }
+
+  /**
+   * Return false if the element contains any extension elements that are not 
+   * supported
+   */
+  private boolean checkElement(
+    Element element, 
+    List ignore) {
+      List<QName> attrs = element.getExtensionAttributes();
+      for (QName qname : attrs) {
+        String ns = qname.getNamespaceURI();
+        if (!ignore.contains(ns)) return false; 
+      }
+      if (element instanceof ExtensibleElement) {
+        ExtensibleElement ext = (ExtensibleElement) element;
+        List<Element> extensions = ext.getExtensions();
+        for (Element el : extensions) {
+          QName qname = el.getQName();
+          String ns = qname.getNamespaceURI();
+          if (!ignore.contains(ns)) return false; 
+          if (!checkElement(el, ignore)) return false;
+        }
+      }
+      return true;
+  }
+
+  protected static boolean beforeOrEqual(Date d1, Date d2) {
+    long l1 = d1.getTime() / 1000; // drop milliseconds
+    long l2 = d2.getTime() / 1000; // drop milliseconds
+    return l1 <= l2;
+  }
+
+  protected IRI resolveBase(RequestContext request) {
+    return request.getBaseUri().resolve(request.getUri());
+  }
+
+}

Added: incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/ContentProviderTest.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/ContentProviderTest.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/ContentProviderTest.java (added)
+++ incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/ContentProviderTest.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,143 @@
+package org.apache.abdera.server.protocol.server.content;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+import junit.framework.TestCase;
+import org.apache.abdera.Abdera;
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.protocol.client.AbderaClient;
+import org.apache.abdera.protocol.client.ClientResponse;
+import org.apache.abdera.protocol.client.RequestOptions;
+import org.apache.abdera.protocol.server.ServiceContext;
+import org.apache.abdera.protocol.server.TargetType;
+import org.apache.abdera.protocol.server.content.CollectionProvider;
+import org.apache.abdera.protocol.server.content.SimpleWorkspaceInfo;
+import org.apache.abdera.protocol.server.content.WorkspaceInfo;
+import org.apache.abdera.protocol.server.content.WorkspaceProvider;
+import org.apache.abdera.protocol.server.impl.DefaultServiceContext;
+import org.apache.abdera.protocol.server.impl.RegexTargetResolver;
+import org.apache.abdera.protocol.server.impl.SingletonProviderManager;
+import org.apache.abdera.protocol.server.servlet.AbderaServlet;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.servlet.Context;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+public class ContentProviderTest extends TestCase {
+
+  private Server server;
+  private DefaultServiceContext abderaServiceContext;
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    
+    abderaServiceContext = new DefaultServiceContext();
+
+    RegexTargetResolver resolver = new RegexTargetResolver();
+    resolver.setPattern("/acme(\\?[^#]*)?", TargetType.TYPE_SERVICE);
+    resolver.setPattern("/acme/customers(\\?[^#]*)?", TargetType.TYPE_COLLECTION);
+    resolver.setPattern("/acme/customers/([^/#?]+)(\\?[^#]*)?", TargetType.TYPE_ENTRY);
+    abderaServiceContext.setTargetResolver(resolver);
+    
+    SingletonProviderManager pm = new SingletonProviderManager();
+    abderaServiceContext.setProviderManager(pm);
+
+    WorkspaceProvider wp = new WorkspaceProvider();
+
+    SimpleWorkspaceInfo wi = new SimpleWorkspaceInfo();
+    wi.setId("acme");
+    CustomerContentProvider cp = new CustomerContentProvider();
+    Map<String, CollectionProvider> contentProviders = new HashMap<String, CollectionProvider>();
+    contentProviders.put("customers", cp);
+    
+    wi.setCollectionProviders(contentProviders);
+
+    List<WorkspaceInfo> workspaces = new ArrayList<WorkspaceInfo>();
+    workspaces.add(wi);
+    wp.setWorkspaces(workspaces);
+    pm.setProvider(wp);
+
+    initializeJetty();
+  }
+
+  public void testCustomerProvider() throws Exception {
+
+    Abdera abdera = new Abdera();
+    Factory factory = abdera.getFactory();
+
+    AbderaClient client = new AbderaClient(abdera);
+
+    String base = "http://localhost:9002/";
+
+    // Testing of entry creation
+    IRI colUri = new IRI(base).resolve("acme/customers"); // base +
+                                                          // docCollection.getHref().toString();
+    Entry entry = factory.newEntry();
+    entry.setTitle("Hmmm this is ignored right now");
+    entry.setUpdated(new Date());
+    entry.addAuthor("Acme Industries");
+    entry.setId(factory.newUuidUri());
+    entry.setSummary("Customer document");
+    
+    Element customerEl = factory.newElement(new QName("customer"));
+    customerEl.setAttributeValue(new QName("name"), "Dan Diephouse");
+    entry.setContent(customerEl);
+
+    RequestOptions opts = new RequestOptions();
+    opts.setContentType("application/atom+xml;type=entry");
+    ClientResponse res = client.post(colUri.toString(), entry, opts);
+    assertEquals(201, res.getStatus());
+
+    prettyPrint(abdera, res.getDocument());
+
+    IRI location = res.getLocation();
+    assertEquals(colUri + "/1001-Dan_Diephouse", 
+                 location.toString());
+
+    // GET the entry
+    res = client.get(location.toString());
+    assertEquals(200, res.getStatus());
+
+    prettyPrint(abdera, res.getDocument());
+    org.apache.abdera.model.Document<Entry> entry_doc = res.getDocument();
+    entry = entry_doc.getRoot();
+
+  }
+
+  private void prettyPrint(Abdera abdera, Base doc) throws IOException {
+    abdera.getWriterFactory().getWriter("prettyxml").writeTo(doc, System.out);
+    System.out.println();
+  }
+
+  private void initializeJetty() throws Exception {
+
+    server = new Server(9002);
+    Context root = new Context(server, "/", Context.NO_SESSIONS);
+    root.addServlet(new ServletHolder(new AbderaServlet() {
+
+      @Override
+      protected ServiceContext createServiceContext() {
+        abderaServiceContext.init(getAbdera(), getProperties(getServletConfig()));
+        return abderaServiceContext;
+      }
+    }), "/*");
+    server.start();
+  }
+
+  @Override
+  protected void tearDown() throws Exception {
+    if (server != null) server.stop();
+  }
+
+}

Added: incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/Customer.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/Customer.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/Customer.java (added)
+++ incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/Customer.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,22 @@
+package org.apache.abdera.server.protocol.server.content;
+
+import java.util.Date;
+
+public class Customer {
+  private int id;
+  private String name;
+  
+  public int getId() {
+    return id;
+  }
+  public void setId(int id) {
+    this.id = id;
+  }
+  public String getName() {
+    return name;
+  }
+  public void setName(String name) {
+    this.name = name;
+  }
+  
+}

Added: incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/CustomerContentProvider.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/CustomerContentProvider.java?rev=581397&view=auto
==============================================================================
--- incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/CustomerContentProvider.java (added)
+++ incubator/abdera/java/trunk/server/src/test/java/org/apache/abdera/server/protocol/server/content/CustomerContentProvider.java Tue Oct  2 14:45:34 2007
@@ -0,0 +1,115 @@
+package org.apache.abdera.server.protocol.server.content;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.xml.namespace.QName;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.model.Content;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.protocol.server.content.AbstractCollectionProvider;
+import sun.security.pkcs.ContentInfo;
+
+public class CustomerContentProvider extends AbstractCollectionProvider<Customer> {
+  private static final String ID_PREFIX = "urn:acme:customer:";
+  
+  private final static AtomicInteger nextId = new AtomicInteger(1000);
+  private Map<Integer, Customer> customers = new HashMap<Integer, Customer>();
+  private Factory factory = new Abdera().getFactory();
+  
+  public String getId() {
+    return "tag:example.org,2006:feed";
+  }
+
+  public Customer createEntry(String title, String summary, Content content) {
+    Customer customer = contentToCustomer(content);
+    customers.put(customer.getId(), customer);
+    
+    return customer;
+  }
+
+  private Customer contentToCustomer(Content content) {
+    Customer customer = new Customer();
+    
+    return contentToCustomer(content, customer);
+  }
+
+  private Customer contentToCustomer(Content content, Customer customer) {
+    Element firstChild = content.getFirstChild();
+    customer.setName(firstChild.getAttributeValue("name"));
+    customer.setId(nextId.incrementAndGet());
+    return customer;
+  }
+
+  public void deleteEntry(String resourceName) {
+    Integer id = getIdFromResourceName(resourceName);
+    customers.remove(id);
+  }
+
+  public String getAuthor() {
+    return "Acme Industries";
+  }
+
+  public Object getContent(Customer entry) {
+    Content content = factory.newContent();
+    Element customerEl = factory.newElement(new QName("customer"));
+    customerEl.setAttributeValue(new QName("name"), entry.getName());
+   
+    content.setValueElement(customerEl);
+    return content;
+  }
+
+  public Iterable<Customer> getEntries() {
+    return customers.values();
+  }
+
+  public Customer getEntry(String resourceName) {
+    Integer id = getIdFromResourceName(resourceName);
+    return customers.get(id);
+  }
+
+  private Integer getIdFromResourceName(String resourceName) {
+    int idx = resourceName.indexOf("-");
+    if (idx == -1) {
+      // TODO: exception handling for 404 Not found
+    }
+    Integer id = new Integer(resourceName.substring(0, idx));
+    return id;
+  }
+
+  public Customer getEntryFromId(String id) {
+    return customers.get(new Integer(id));
+  }
+
+  public String getId(Customer entry) {
+    // TODO: is this valid?
+    return ID_PREFIX + entry.getId();
+  }
+
+  public String getName(Customer entry) {
+    return entry.getId() + "-" + entry.getName().replaceAll(" ", "_");
+  }
+
+  public String getTitle() {
+    return "Acme Customer Database";
+  }
+
+  public String getTitle(Customer entry) {
+    return entry.getName();
+  }
+
+  public Date getUpdated(Customer entry) {
+    return new Date();
+  }
+
+  public Customer updateEntry(Customer entry, Content content) {
+    contentToCustomer(content, entry);
+    
+    return entry;
+  }
+
+}

Modified: incubator/abdera/java/trunk/spring/src/main/resources/META-INF/schemas/abdera-spring.xsd
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/spring/src/main/resources/META-INF/schemas/abdera-spring.xsd?rev=581397&r1=581396&r2=581397&view=diff
==============================================================================
--- incubator/abdera/java/trunk/spring/src/main/resources/META-INF/schemas/abdera-spring.xsd (original)
+++ incubator/abdera/java/trunk/spring/src/main/resources/META-INF/schemas/abdera-spring.xsd Tue Oct  2 14:45:34 2007
@@ -48,13 +48,13 @@
     <xsd:complexType>
       <xsd:complexContent>
         <xsd:extension base="beans:identifiedType">
-          <xsd:sequence>
+          <xsd:choice maxOccurs="unbounded">
             <xsd:element name="category" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
             <xsd:element name="collection" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
             <xsd:element name="entry" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
             <xsd:element name="media" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
             <xsd:element name="service" type="xsd:anyType" minOccurs="0" maxOccurs="unbounded"/>
-          </xsd:sequence>
+          </xsd:choice>
         </xsd:extension>
       </xsd:complexContent>
     </xsd:complexType>