[seam-commits] Seam SVN: r10232 - in trunk: src/resteasy/org/jboss/seam/resteasy and 1 other directory.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Mon Mar 30 11:16:52 EDT 2009


Author: jharting
Date: 2009-03-30 11:16:52 -0400 (Mon, 30 Mar 2009)
New Revision: 10232

Added:
   trunk/src/resteasy/org/jboss/seam/resteasy/AbstractResource.java
   trunk/src/resteasy/org/jboss/seam/resteasy/EntityHomeWrapper.java
   trunk/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java
   trunk/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java
Modified:
   trunk/build/resteasy.pom.xml
   trunk/build/root.pom.xml
   trunk/src/resteasy/org/jboss/seam/resteasy/Application.java
   trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java
   trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java
   trunk/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd
Log:
JBSEAM-3987

Modified: trunk/build/resteasy.pom.xml
===================================================================
--- trunk/build/resteasy.pom.xml	2009-03-30 05:19:29 UTC (rev 10231)
+++ trunk/build/resteasy.pom.xml	2009-03-30 15:16:52 UTC (rev 10232)
@@ -27,11 +27,28 @@
       </dependency>
       
       <dependency>
+         <groupId>org.jboss.resteasy</groupId>
+         <artifactId>resteasy-jaxb-provider</artifactId>
+      </dependency>
+      
+      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>servlet-api</artifactId>
          <scope>provided</scope>
       </dependency>
       
+      <dependency>
+         <groupId>javax.persistence</groupId>
+         <artifactId>persistence-api</artifactId>
+         <scope>provided</scope>
+      </dependency>
+      
+      <dependency>
+         <groupId>org.hibernate</groupId>
+         <artifactId>hibernate</artifactId>
+         <scope>provided</scope>
+      </dependency>
+      
    </dependencies>
 
 </project>
\ No newline at end of file

Modified: trunk/build/root.pom.xml
===================================================================
--- trunk/build/root.pom.xml	2009-03-30 05:19:29 UTC (rev 10231)
+++ trunk/build/root.pom.xml	2009-03-30 15:16:52 UTC (rev 10232)
@@ -348,6 +348,22 @@
       </dependency>
 
       <dependency>
+         <groupId>org.jboss.resteasy</groupId>
+         <artifactId>resteasy-jaxb-provider</artifactId>
+         <version>1.1-RC1</version>
+         <exclusions>
+            <exclusion>
+               <groupId>com.sun.xml.bind</groupId>
+               <artifactId>jaxb-impl</artifactId>
+            </exclusion>
+            <exclusion>
+               <groupId>org.codehaus.jettison</groupId>
+               <artifactId>jettison</artifactId>
+            </exclusion>
+         </exclusions>
+      </dependency>
+
+      <dependency>
         <groupId>org.hibernate</groupId>
         <artifactId>hibernate</artifactId>
         <version>3.2.4.sp1</version>

Added: trunk/src/resteasy/org/jboss/seam/resteasy/AbstractResource.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/AbstractResource.java	                        (rev 0)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/AbstractResource.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -0,0 +1,166 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+package org.jboss.seam.resteasy;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Resource class used by ResourceHome and ResourceQuery components. Contains
+ * information about path, media types and entity class the component operates
+ * on.
+ * 
+ * @author Jozef Hartinger
+ * 
+ * @param <T> entity class
+ */
+public abstract class AbstractResource<T>
+{
+
+   @Context
+   private HttpHeaders httpHeaders;
+
+   private String path = null;
+   private MediaType[] mediaTypes = null;
+   private Class entityClass = null;
+
+   public AbstractResource()
+   {
+      mediaTypes = new MediaType[] { MediaType.APPLICATION_XML_TYPE };
+   }
+
+   public MediaType[] getMediaTypes()
+   {
+      return mediaTypes;
+   }
+
+   public void setMediaTypes(String[] mediaTypes)
+   {
+      this.mediaTypes = new MediaType[mediaTypes.length];
+      for (int i = 0; i < mediaTypes.length; i++)
+      {
+         this.mediaTypes[i] = MediaType.valueOf(mediaTypes[i]);
+      }
+   }
+
+   public void setMediaTypes(MediaType[] mediaTypes)
+   {
+      this.mediaTypes = mediaTypes;
+   }
+
+   public void setEntityClass(Class entityClass)
+   {
+      this.entityClass = entityClass;
+   }
+
+   /**
+    * Retrieve entity class. If not set, type parameters of a superclass are
+    * examined.
+    * 
+    * @return entity class
+    */
+   public Class<T> getEntityClass()
+   {
+      if (entityClass == null)
+      {
+         Type superclass = this.getClass().getGenericSuperclass();
+         if (superclass instanceof ParameterizedType)
+         {
+            ParameterizedType parameterizedSuperclass = (ParameterizedType) superclass;
+            if (parameterizedSuperclass.getActualTypeArguments().length > 0)
+            {
+               return (Class) parameterizedSuperclass.getActualTypeArguments()[0];
+            }
+         }
+         throw new RuntimeException("Unable to determine entity class.");
+      }
+      else
+      {
+         return entityClass;
+      }
+   }
+
+   /**
+    * Retrieve a suffix of this resource's URI. This property does not have to
+    * be set. @Path annotation can be used insted to set the URI.
+    * 
+    * @see javax.ws.rs.Path
+    * @return path
+    */
+   public String getPath()
+   {
+      return path;
+   }
+
+   /**
+    * see {@link #getPath()}
+    * 
+    * @param path
+    */
+   public void setPath(String path)
+   {
+      this.path = path;
+   }
+
+   /**
+    * Select a media type that will be used for marshalling entity. Media type
+    * is selected from the intersection of media types supported by this
+    * resource and media types accepted by client.
+    * 
+    * @return selected media type, returns null if no suitable media type is
+    *         found
+    */
+   protected MediaType selectResponseMediaType()
+   {
+      for (MediaType acceptedMediaType : httpHeaders.getAcceptableMediaTypes())
+      {
+         for (MediaType availableMediaType : mediaTypes)
+         {
+            if (acceptedMediaType.isCompatible(availableMediaType))
+               return availableMediaType;
+         }
+      }
+      return null;
+   }
+
+   /**
+    * Check if media type passed as parameter is supported by this resource.
+    * 
+    * @param mediaType
+    * @return true if and only if the media type is accepted by this resource
+    */
+   public boolean isMediaTypeCompatible(MediaType mediaType)
+   {
+      for (MediaType availableMediaType : mediaTypes)
+      {
+         if (availableMediaType.isCompatible(mediaType))
+         {
+            return true;
+         }
+      }
+      return false;
+   }
+}


Property changes on: trunk/src/resteasy/org/jboss/seam/resteasy/AbstractResource.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/src/resteasy/org/jboss/seam/resteasy/Application.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/Application.java	2009-03-30 05:19:29 UTC (rev 10231)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/Application.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -23,9 +23,9 @@
 public class Application extends javax.ws.rs.core.Application
 {
 
-    private Map<Class<?>, Component> providerClasses = new HashMap<Class<?>, Component>();
-    private Map<Class<?>, Component> resourceClasses = new HashMap<Class<?>, Component>();
-
+   private Map<Class<?>, Set<Component>> providerClasses = new HashMap<Class<?>, Set<Component>>();
+   private Map<Class<?>, Set<Component>> resourceClasses = new HashMap<Class<?>, Set<Component>>();
+   
     private List<String> providerClassNames = new ArrayList<String>();
     private List<String> resourceClassNames = new ArrayList<String>();
 
@@ -55,35 +55,57 @@
         return resourceClasses.keySet();
     }
 
-    public void addProviderClass(Class<?> clazz, Component component)
-    {
-        providerClasses.put(clazz, component);
-    }
+   public void addProviderClass(Class<?> clazz)
+   {
+      providerClasses.put(clazz, null);
+   }
 
+   public void addProviderClass(Class<?> clazz, Component component)
+   {
+      Set<Component> components = providerClasses.get(clazz);
+      if (components == null)
+      {
+         components = new HashSet<Component>();
+         providerClasses.put(clazz, components);
+      }
+      components.add(component);
+   }
+
     public void removeProviderClass(Class<?> clazz)
     {
         providerClasses.remove(clazz);
     }
 
-    public void addResourceClass(Class<?> clazz, Component component)
-    {
-        resourceClasses.put(clazz, component);
-    }
+   public void addResourceClass(Class<?> clazz)
+   {
+      resourceClasses.put(clazz, null);
+   }
 
+   public void addResourceClass(Class<?> clazz, Set<Component> newComponents)
+   {
+      Set<Component> components = resourceClasses.get(clazz);
+      if (components == null)
+      {
+         components = new HashSet<Component>();
+         resourceClasses.put(clazz, components);
+      }
+      components.addAll(newComponents);
+   }
+
     public void removeResourceClass(Class<?> clazz)
     {
         resourceClasses.remove(clazz);
     }
 
-    public Component getProviderClassComponent(Class clazz)
-    {
-        return providerClasses.get(clazz) != null ? providerClasses.get(clazz) : null;
-    }
+   public Set<Component> getProviderClassComponent(Class clazz)
+   {
+      return providerClasses.get(clazz) != null ? providerClasses.get(clazz) : null;
+   }
 
-    public Component getResourceClassComponent(Class clazz)
-    {
-        return resourceClasses.get(clazz) != null ? resourceClasses.get(clazz) : null;
-    }
+   public Set<Component> getResourceClassComponent(Class clazz)
+   {
+      return resourceClasses.get(clazz) != null ? resourceClasses.get(clazz) : null;
+   }
 
     public Map<String, MediaType> getMediaTypeMappings()
     {

Added: trunk/src/resteasy/org/jboss/seam/resteasy/EntityHomeWrapper.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/EntityHomeWrapper.java	                        (rev 0)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/EntityHomeWrapper.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -0,0 +1,166 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+package org.jboss.seam.resteasy;
+
+import org.jboss.seam.framework.EntityHome;
+import org.jboss.seam.framework.HibernateEntityHome;
+import org.jboss.seam.framework.Home;
+
+/**
+ * This class provides an unified interface for accessing EntityHome and
+ * HibernateEntityHome instances.
+ * 
+ * @author Jozef Hartinger
+ * 
+ * @see org.jboss.seam.framework.EntityHome
+ * @see org.jboss.seam.framework.HibernateEntityHome
+ * 
+ */
+public class EntityHomeWrapper<T>
+{
+   private Home<?, T> home = null;
+   private boolean hibernate;
+
+   /**
+    * EntityHome or HibernateEntityHome instance is expected.
+    */
+   public EntityHomeWrapper(Home<?, T> entityHome)
+   {
+      if (entityHome instanceof EntityHome)
+      {
+         hibernate = false;
+      }
+      else if (entityHome instanceof HibernateEntityHome)
+      {
+         hibernate = true;
+      }
+      else
+      {
+         throw new IllegalArgumentException("You must use either EntityHome or HibernateEntityHome instance.");
+      }
+      this.home = entityHome;
+   }
+
+   public Object getId()
+   {
+      return home.getId();
+   }
+
+   public void setId(Object id)
+   {
+      home.setId(id);
+   }
+
+   public T getInstance()
+   {
+      return home.getInstance();
+   }
+
+   public void setInstance(T instance)
+   {
+      home.setInstance(instance);
+   }
+
+   public T find()
+   {
+      if (hibernate)
+      {
+         return getHibernateEntityHome().find();
+      }
+      else
+      {
+         return getEntityHome().find();
+      }
+   }
+
+   public void persist()
+   {
+      if (hibernate)
+      {
+         getHibernateEntityHome().persist();
+      }
+      else
+      {
+         getEntityHome().persist();
+      }
+   }
+
+   public void remove()
+   {
+      if (hibernate)
+      {
+         getHibernateEntityHome().remove();
+      }
+      else
+      {
+         getEntityHome().remove();
+      }
+   }
+
+   /**
+    * Merge the state of the given entity with the current persistence context.
+    */
+   public void merge(T object)
+   {
+      if (hibernate)
+      {
+         getHibernateEntityHome().getSession().merge(object);
+         getHibernateEntityHome().update();
+      }
+      else
+      {
+         getEntityHome().getEntityManager().merge(object);
+         getEntityHome().update();
+      }
+   }
+
+   public Class<T> getEntityClass()
+   {
+      return home.getEntityClass();
+   }
+
+   private EntityHome<T> getEntityHome()
+   {
+      return (EntityHome<T>) home;
+   }
+
+   private HibernateEntityHome<T> getHibernateEntityHome()
+   {
+      return (HibernateEntityHome<T>) home;
+   }
+
+   /**
+    * Return the underlying EntityHome or HibernateEntityHome instance.
+    */
+   public Home<?, T> unwrap()
+   {
+      if (hibernate)
+      {
+         return getHibernateEntityHome();
+      }
+      else
+      {
+         return getEntityHome();
+      }
+   }
+
+}


Property changes on: trunk/src/resteasy/org/jboss/seam/resteasy/EntityHomeWrapper.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java	                        (rev 0)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -0,0 +1,371 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+package org.jboss.seam.resteasy;
+
+import java.io.InputStream;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.URI;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.MessageBodyReader;
+
+import org.jboss.resteasy.core.Headers;
+import org.jboss.resteasy.core.StringParameterInjector;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.jboss.seam.Entity;
+import org.jboss.seam.annotations.Create;
+import org.jboss.seam.framework.Home;
+
+import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE;
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+/**
+ * This component exposes EntityHome and HibernateEntityHome components as a
+ * REST resource.
+ * 
+ * @author Jozef Hartinger
+ * 
+ * @param <T> Entity class
+ * @param <T2> Entity id class
+ */
+ at Path("")
+public class ResourceHome<T, T2> extends AbstractResource<T>
+{
+   private EntityHomeWrapper<T> entityHome = null;
+
+   @Context
+   private UriInfo uriInfo;
+   @Context
+   private Headers headers;
+   @HeaderParam("Content-Type")
+   private MediaType requestContentType;
+
+   private Class entityIdClass = null;
+   private boolean readonly;
+
+   /**
+    * Called at component instantiation. EntityHome component must be set in
+    * order for component to be created.
+    */
+   @Create
+   public void create()
+   {
+      setEntityHome(getEntityHome());
+      if (entityHome == null)
+      {
+         throw new IllegalStateException("entityHome is not set");
+      }
+   }
+
+   /**
+    * Called by RESTEasy when HTTP GET request is received. String form of
+    * entity identifier is passed as a parameter. Returns a response containing
+    * database entity.
+    * 
+    * @see #getEntity
+    * @param rawId String form of entity identifier
+    * @return response
+    */
+   @Path("/{id}")
+   @GET
+   public Response getResource(@PathParam("id") String rawId)
+   {
+      MediaType selectedMediaType = selectResponseMediaType();
+      if (selectedMediaType == null)
+      {
+         return Response.status(UNSUPPORTED_MEDIA_TYPE).build();
+      }
+
+      T2 id = unmarshallId(rawId);
+      T entity = getEntity(id);
+
+      return Response.ok(new GenericEntity(entity, getEntityClass())
+      {
+      }, selectedMediaType).build();
+   }
+
+   /**
+    * Retrieve an entity identified by id parameter.
+    * 
+    * @param id entity identifier
+    * @return entity database entity
+    */
+   public T getEntity(T2 id)
+   {
+      entityHome.setId(id);
+      return entityHome.find();
+
+   }
+
+   /**
+    * Called by RESTEasy when HTTP POST request is received. Persists received
+    * entity and returns 201 HTTP status code with location header set to new
+    * URI if operation succeeds.
+    * 
+    * @see #createEntity
+    * @param messageBody HTTP request body
+    * @return response
+    */
+   @POST
+   public Response createResource(InputStream messageBody)
+   {
+      if (readonly)
+      {
+         return Response.status(405).build();
+      }
+
+      // check if we accept this content type
+      if (!isMediaTypeCompatible(requestContentType))
+      {
+         return Response.status(UNSUPPORTED_MEDIA_TYPE).build();
+      }
+
+      T entity = unmarshallEntity(messageBody);
+
+      T2 id = createEntity(entity);
+
+      URI uri = uriInfo.getAbsolutePathBuilder().path(id.toString()).build();
+      return Response.created(uri).build();
+   }
+
+   /**
+    * Store entity passed as a parameter in the database.
+    * 
+    * @param entity Object to be persisted
+    * @return id identifier assigned to the entity
+    */
+   public T2 createEntity(T entity)
+   {
+      entityHome.setInstance(entity);
+      entityHome.persist();
+      return (T2) entityHome.getId();
+   }
+
+   /**
+    * Called by RESTEasy when HTTP PUT request is received. Merges the state of
+    * the database entity with the received representation.
+    * 
+    * @see #updateEntity
+    * @param rawId String form of entity identifier
+    * @param messageBody HTTP request body
+    * @return response
+    */
+   @Path("/{id}")
+   @PUT
+   public Response updateResource(@PathParam("id") String rawId, InputStream messageBody)
+   {
+      if (readonly)
+      {
+         return Response.status(405).build();
+      }
+
+      // check if we accept this content type
+      if (!isMediaTypeCompatible(requestContentType))
+      {
+         return Response.status(UNSUPPORTED_MEDIA_TYPE).build();
+      }
+
+      T entity = unmarshallEntity(messageBody);
+      T2 id = unmarshallId(rawId);
+
+      // check representation id - we don't allow renaming
+      Object storedId = Entity.forBean(entity).getIdentifier(entity);
+      if (!id.equals(storedId))
+      {
+         return Response.status(BAD_REQUEST).build();
+      }
+
+      updateEntity(entity);
+      return Response.noContent().build();
+   }
+
+   /**
+    * Merge the state of the database entity with the entity passed as a
+    * parameter.
+    * 
+    * @param entity
+    */
+   public void updateEntity(T entity)
+   {
+      entityHome.merge(entity);
+   }
+
+   /**
+    * Called by RESTEasy when HTTP DELETE request is received. Deletes a
+    * database entity.
+    * 
+    * @see #deleteEntity
+    * @param rawId String form of entity identifier
+    * @return response
+    */
+   @Path("/{id}")
+   @DELETE
+   public Response deleteResource(@PathParam("id") String rawId)
+   {
+      if (readonly)
+      {
+         return Response.status(405).build();
+      }
+
+      T2 id = unmarshallId(rawId);
+      deleteEntity(id);
+      return Response.noContent().build();
+   }
+
+   /**
+    * Delete database entity.
+    * 
+    * @param id entity identifier
+    */
+   public void deleteEntity(T2 id)
+   {
+      getEntity(id);
+      entityHome.remove();
+   }
+
+   /**
+    * Convert HTTP request body into entity class instance.
+    * 
+    * @param is HTTP request body
+    * @return entity
+    */
+   private T unmarshallEntity(InputStream is)
+   {
+      Class<T> entityClass = getEntityClass();
+      MessageBodyReader<T> reader = ResteasyProviderFactory.getInstance().getMessageBodyReader(entityClass, entityClass, entityClass.getAnnotations(), requestContentType);
+      if (reader == null)
+      {
+         throw new RuntimeException("Unable to find MessageBodyReader for content type " + requestContentType);
+      }
+      T entity;
+      try
+      {
+         entity = reader.readFrom(entityClass, entityClass, entityClass.getAnnotations(), requestContentType, headers, is);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("Unable to unmarshall request body");
+      }
+      return entity;
+   }
+
+   /**
+    * Converts String form of entity identifier to it's natural type.
+    * 
+    * @param id String form of entity identifier
+    * @return entity identifier
+    */
+   private T2 unmarshallId(String id)
+   {
+      StringParameterInjector injector = new StringParameterInjector(getEntityIdClass(), getEntityIdClass(), "id", PathParam.class, null, null, ResteasyProviderFactory.getInstance());
+      return (T2) injector.extractValue(id);
+   }
+
+   /**
+    * EntityHome component getter. Override this method to set the EntityHome
+    * this resource will operate on. You can use either EntityHome or
+    * HibernateEntityHome instance.
+    * 
+    * @return entity home
+    */
+   public Home<?, T> getEntityHome()
+   {
+      return (entityHome == null) ? null : entityHome.unwrap();
+   }
+
+   /**
+    * EntityHome component setter
+    * 
+    * @param entityHome
+    */
+   public void setEntityHome(Home<?, T> entityHome)
+   {
+      this.entityHome = new EntityHomeWrapper<T>(entityHome);
+   }
+
+   @Override
+   public Class<T> getEntityClass()
+   {
+      return entityHome.getEntityClass();
+   }
+
+   public boolean isReadonly()
+   {
+      return readonly;
+   }
+
+   /**
+    * If set to read-only mode, this resource will only response to GET
+    * requests. HTTP 415 status code (method not allowed) will returned in all
+    * other cases.
+    * 
+    * @param readonly
+    */
+   public void setReadonly(boolean readonly)
+   {
+      this.readonly = readonly;
+   }
+
+   /**
+    * Retrieve entity identifier's class. If not set, type parameters of a
+    * superclass are examined.
+    * 
+    * @return class of entity identifier
+    */
+   public Class getEntityIdClass()
+   {
+      if (entityIdClass == null)
+      {
+         Type superclass = this.getClass().getGenericSuperclass();
+         if (superclass instanceof ParameterizedType)
+         {
+            ParameterizedType parameterizedSuperclass = (ParameterizedType) superclass;
+            if (parameterizedSuperclass.getActualTypeArguments().length == 2)
+            {
+               return (Class) parameterizedSuperclass.getActualTypeArguments()[1];
+            }
+         }
+         throw new RuntimeException("Unable to determine entity id class.");
+      }
+      else
+      {
+         return entityIdClass;
+      }
+   }
+
+   public void setEntityIdClass(Class entityIdClass)
+   {
+      this.entityIdClass = entityIdClass;
+   }
+}


Property changes on: trunk/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java	                        (rev 0)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -0,0 +1,159 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */ 
+package org.jboss.seam.resteasy;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
+import org.jboss.seam.annotations.Create;
+import org.jboss.seam.framework.EntityQuery;
+import org.jboss.seam.framework.Query;
+import static javax.ws.rs.core.Response.Status.UNSUPPORTED_MEDIA_TYPE;
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+/**
+ * This component exposes EntityQuery component as a REST resource responding to
+ * HTTP GET request.
+ * 
+ * @author Jozef Hartinger
+ * 
+ * @param <T> entity type
+ */
+ at Path("")
+public class ResourceQuery<T> extends AbstractResource<T>
+{
+
+   private Query<?, T> entityQuery = null;
+
+   /**
+    * Called at component instantiation.
+    */
+   @Create
+   public void create()
+   {
+      this.entityQuery = getEntityQuery();
+      if (entityQuery == null)
+      {
+         this.entityQuery = createEntityQuery();
+      }
+   }
+
+   /**
+    * Called by RESTEasy to respond for an HTTP GET request. Retrieves a list of
+    * entities matching criteria set by query parameters from database and
+    * returns it wrapped in Response instance.
+    * 
+    * @see #getEntityList
+    * @param start first entity in the list
+    * @param show maximum size of the list
+    * @return representation of a list of database entries
+    */
+   @GET
+   @Wrapped
+   public Response getResourceList(@QueryParam("start") @DefaultValue("0") int start, @QueryParam("show") @DefaultValue("25") int show)
+   {
+      MediaType selectedMediaType = selectResponseMediaType();
+      if (selectedMediaType == null)
+      {
+         return Response.status(UNSUPPORTED_MEDIA_TYPE).build();
+      }
+
+      if ((start < 0) || (show < 0))
+      {
+         return Response.status(BAD_REQUEST).build();
+      }
+
+      final List<T> result = getEntityList(start, show);
+      // create a proper response type
+      Type responseType = new ParameterizedType()
+      {
+
+         public Type getRawType()
+         {
+            return result.getClass();
+         }
+
+         public Type getOwnerType()
+         {
+            return null;
+         }
+
+         public Type[] getActualTypeArguments()
+         {
+            Type[] types = { getEntityClass() };
+            return types;
+         }
+      };
+      return Response.ok(new GenericEntity(result, responseType)
+      {
+      }, selectedMediaType).build();
+   }
+
+   /**
+    * Retrieve a list of database entities.
+    * 
+    * @param start first entity in the list
+    * @param show maximum size of the list, 0 for unlimited
+    * @return list of database entries
+    */
+   public List<T> getEntityList(int start, int show)
+   {
+      entityQuery.setFirstResult(start);
+      if (show > 0) // set 0 for unlimited
+      {
+         entityQuery.setMaxResults(show);
+      }
+      return entityQuery.getResultList();
+   }
+
+   /**
+    * EntityQuery getter
+    * 
+    * @return EntityQuery instance
+    */
+   public Query<?, T> getEntityQuery()
+   {
+      return entityQuery;
+   }
+
+   public void setEntityQuery(Query<?, T> query)
+   {
+      this.entityQuery = query;
+   }
+
+   public Query<?, T> createEntityQuery()
+   {
+      Query<?, T> entityQuery = new EntityQuery<T>();
+      entityQuery.setEjbql("select entity from " + getEntityClass().getName() + " entity");
+      return entityQuery;
+   }
+}


Property changes on: trunk/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java	2009-03-30 05:19:29 UTC (rev 10231)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -13,6 +13,7 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Set;
 
 /**
  * Scans annoated JAX-RS resources and providers, optionally registers them as Seam components.
@@ -53,7 +54,7 @@
         }
 
         log.debug("finding all Seam component classes");
-        Map<Class, Component> seamComponents = new HashMap<Class, Component>();
+        Map<Class, Set<Component>> seamComponents = new HashMap<Class, Set<Component>>();
         String[] applicationContextNames = Contexts.getApplicationContext().getNames();
         for (String applicationContextName : applicationContextNames)
         {
@@ -62,7 +63,12 @@
                 Component seamComponent =
                         (Component) Component.getInstance(applicationContextName, ScopeType.APPLICATION);
                 // TODO: This should consider EJB components/annotations on interfaces somehow?
-                seamComponents.put(seamComponent.getBeanClass(), seamComponent);
+                Class beanClass = seamComponent.getBeanClass();
+                if (!seamComponents.containsKey(beanClass))
+                {
+                   seamComponents.put(beanClass, new HashSet<Component>());
+                }
+                seamComponents.get(beanClass).add(seamComponent);
             }
         }
 
@@ -71,7 +77,7 @@
     }
 
     // Load all provider classes, either scanned or through explicit configuration
-    protected void registerProviders(Map<Class, Component> seamComponents, Collection annotatedProviderClasses)
+    protected void registerProviders(Map<Class, Set<Component>> seamComponents, Collection annotatedProviderClasses)
     {
         Collection<Class> providerClasses = new HashSet<Class>();
         try
@@ -92,30 +98,37 @@
             // Ignore built-in providers, we register them manually later
             if (providerClass.getName().startsWith("org.jboss.resteasy.plugins.providers")) continue;
 
-            Component seamComponent = null;
             // Check if this is also a Seam component bean class
             if (seamComponents.containsKey(providerClass))
             {
-                seamComponent = seamComponents.get(providerClass);
-                // Needs to be APPLICATION or STATELESS
-                if (!seamComponent.getScope().equals(ScopeType.APPLICATION) &&
+               for (Component seamComponent : seamComponents.get(providerClass))
+               {
+                  // Needs to be APPLICATION or STATELESS
+                  if (!seamComponent.getScope().equals(ScopeType.APPLICATION) &&
                         !seamComponent.getScope().equals(ScopeType.STATELESS))
-                {
-                    log.warn("can't add provider Seam component, not APPLICATION or STATELESS scope: " + seamComponent.getName());
-                    log.warn("this provider class will be registered without Seam injection or lifecycle!");
-                    seamComponent = null;
-                }
+                  {
+                     log.warn("can't add provider Seam component, not APPLICATION or STATELESS scope: " + seamComponent.getName());
+                     log.warn("this provider class will be registered without Seam injection or lifecycle!");
+                     seamComponent = null;
+                  }
+                  if (seamComponent != null)
+                  {
+                     log.debug("adding provider Seam component: " + seamComponent.getName());
+                     application.addProviderClass(providerClass, seamComponent);
+                  }
+                  else
+                  {
+                     log.debug("adding provider class: " + providerClass.getName());
+                     application.addProviderClass(providerClass);
+                  }
+               }
             }
-            if (seamComponent != null)
-            {
-                log.debug("adding provider Seam component: " + seamComponent.getName());
-            }
             else
             {
-                log.debug("adding provider class: " + providerClass.getName());
+               log.debug("adding provider class: " + providerClass.getName());
+               application.addProviderClass(providerClass);
             }
-            application.addProviderClass(providerClass, seamComponent);
-        }
+         }
         if (application.getProviderClasses().size() == 0 &&
                 !application.isUseBuiltinProviders())
         {
@@ -124,7 +137,7 @@
     }
 
     // Load all resource classes, either scanned or through explicit configuration
-    protected void registerResources(Map<Class, Component> seamComponents, Collection annotatedResourceClasses)
+    protected void registerResources(Map<Class, Set<Component>> seamComponents, Collection annotatedResourceClasses)
     {
         Collection<Class> resourceClasses = new HashSet<Class>();
         try
@@ -142,19 +155,18 @@
         }
         for (Class<Object> resourceClass : resourceClasses)
         {
-
-            Component seamComponent = null;
             // Check if this is also a Seam component bean class
             if (seamComponents.containsKey(resourceClass))
             {
-                seamComponent = seamComponents.get(resourceClass);
-                log.debug("adding resource Seam component: " + seamComponent.getName());
+                Set<Component> components = seamComponents.get(resourceClass);
+                log.debug("adding resource Seam components {0} for class {1}", components, resourceClass);
+                application.addResourceClass(resourceClass, components);
             }
             else
             {
                 log.debug("adding resource class with JAX-RS default lifecycle: " + resourceClass.getName());
+                application.addResourceClass(resourceClass);
             }
-            application.addResourceClass(resourceClass, seamComponent);
         }
         if (application.getClasses().size() == 0)
             log.info("no JAX-RS resource classes registered");

Modified: trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java	2009-03-30 05:19:29 UTC (rev 10231)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java	2009-03-30 15:16:52 UTC (rev 10232)
@@ -1,5 +1,7 @@
 package org.jboss.seam.resteasy;
 
+import java.util.Set;
+
 import org.jboss.seam.Component;
 import org.jboss.seam.ScopeType;
 import org.jboss.seam.contexts.Contexts;
@@ -60,38 +62,40 @@
         }
         for (Class providerClass : application.getProviderClasses())
         {
-            log.debug("registering JAX RS provider class: " + providerClass.getName());
-            Component seamComponent = application.getProviderClassComponent(providerClass);
-            if (seamComponent != null)
+            Set<Component> seamComponents = application.getProviderClassComponent(providerClass);
+            if (seamComponents != null)
             {
-                if (ScopeType.STATELESS.equals(seamComponent.getScope()))
+                for (Component seamComponent : seamComponents)
                 {
-                    throw new RuntimeException(
-                            "Registration of STATELESS Seam components as RESTEasy providers not implemented!"
-                    );
-                }
-                else if (ScopeType.APPLICATION.equals(seamComponent.getScope()))
-                {
-                    Object providerInstance = Component.getInstance(seamComponent.getName());
-                    boolean isStringConverter = false;
-                    for (Class componentIface : seamComponent.getBusinessInterfaces())
-                    {
-                        if (StringConverter.class.isAssignableFrom(componentIface))
-                        {
+                   if (ScopeType.STATELESS.equals(seamComponent.getScope()))
+                   {
+                      throw new RuntimeException(
+                               "Registration of STATELESS Seam components as RESTEasy providers not implemented!"
+                      );
+                   }
+                   else if (ScopeType.APPLICATION.equals(seamComponent.getScope()))
+                   {
+                      Object providerInstance = Component.getInstance(seamComponent.getName());
+                      boolean isStringConverter = false;
+                      for (Class componentIface : seamComponent.getBusinessInterfaces())
+                      {
+                         if (StringConverter.class.isAssignableFrom(componentIface))
+                         {
                             isStringConverter = true;
                             break;
-                        }
-                    }
-                    if (isStringConverter)
-                    {
-                        log.error("can't register Seam component as RESTEasy StringConverter, see: https://jira.jboss.org/jira/browse/JBSEAM-4020");
-                        //log.debug("registering Seam component as custom RESTEasy string converter provider: " + seamComponent.getName());
-                        //providerFactory.addStringConverter((StringConverter)providerInstance);
-                    }
-                    else
-                    {
-                        providerFactory.registerProviderInstance(providerInstance);
-                    }
+                         }
+                      }
+                      if (isStringConverter)
+                      {
+                         log.error("can't register Seam component as RESTEasy StringConverter, see: https://jira.jboss.org/jira/browse/JBSEAM-4020");
+                         //log.debug("registering Seam component as custom RESTEasy string converter provider: " + seamComponent.getName());
+                         //providerFactory.addStringConverter((StringConverter)providerInstance);
+                      }
+                      else
+                      {
+                         providerFactory.registerProviderInstance(providerInstance);
+                      }
+                   }
                 }
             }
             else
@@ -114,13 +118,15 @@
         for (final Class resourceClass : application.getClasses())
         {
             log.debug("registering JAX RS resource class: " + resourceClass);
-            final Component seamComponent = application.getResourceClassComponent(resourceClass);
-            if (seamComponent != null)
+            Set<Component> components = application.getResourceClassComponent(resourceClass);
+            if (components != null)
             {
-                // Seam component lookup when call is dispatched to resource
-                log.debug("registering as Seam component resource factory: " + resourceClass);
-                registry.addResourceFactory(
-                        new ResourceFactory()
+                log.debug("registering all {0} components of {1}", components.size(), resourceClass);
+                // Register every component
+                for (final Component seamComponent : components)
+                {
+                    // Seam component lookup when call is dispatched to resource
+                    ResourceFactory factory = new ResourceFactory()
                         {
 
                             public Class<?> getScannableClass()
@@ -159,12 +165,39 @@
                             public void unregistered()
                             {
                             }
-                        }
-                );
+                        };
+                    // Register on specific path if component has it's path property set to not-null value
+                    if (AbstractResource.class.isAssignableFrom(seamComponent.getBeanClass()))
+                    {
+                       // TODO get the path some other way - it may not be possible to create a instance at this time
+                       AbstractResource instance = (AbstractResource) seamComponent.newInstance();
+                       String path = instance.getPath();
+                       if (instance.getPath() != null)
+                       {
+                          log.debug("registering resource {0} on path {1}", seamComponent.getName(), path);
+                          registry.addResourceFactory(factory, path);
+                       }
+                       else
+                       {
+                          log.debug("registering resource {0}", seamComponent.getName());
+                          registry.addResourceFactory(factory);
+                       }
+                    }
+                    else
+                    {
+                       log.debug("registering resource {0}", seamComponent.getName());
+                       registry.addResourceFactory(factory);
+                    }
+                }
             }
             else
             {
+               // ResourceHome and ResourceQuery won't be registered if not declared as a component
+               if (ResourceHome.class.equals(resourceClass) || ResourceQuery.class.equals(resourceClass))
+                  continue;
+               
                 // JAX-RS default lifecycle
+                log.info("registering resource {0} with default JAX-RS lifecycle", resourceClass);
                 registry.addResourceFactory(new POJOResourceFactory(resourceClass));
             }
         }

Modified: trunk/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd
===================================================================
--- trunk/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd	2009-03-30 05:19:29 UTC (rev 10231)
+++ trunk/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd	2009-03-30 15:16:52 UTC (rev 10232)
@@ -51,6 +51,34 @@
        </xs:complexType>
    </xs:element>
 
+	<xs:element name="resource-home">
+		<xs:annotation>
+			<xs:documentation>
+				Allows you to expose an entity home component as a
+				REST resource.
+			</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:attributeGroup ref="components:attlist.component" />
+			<xs:attributeGroup ref="resteasy:attlist.resourceHome" />
+			<xs:attributeGroup ref="resteasy:attlist.abstractResource" />
+		</xs:complexType>
+	</xs:element>
+
+	<xs:element name="resource-query">
+		<xs:annotation>
+			<xs:documentation>
+				Allows you to expose an entity query component as a
+				REST resource.
+			</xs:documentation>
+		</xs:annotation>
+		<xs:complexType>
+			<xs:attributeGroup ref="components:attlist.component" />
+			<xs:attributeGroup ref="resteasy:attlist.resourceQuery" />
+			<xs:attributeGroup ref="resteasy:attlist.abstractResource" />
+		</xs:complexType>
+	</xs:element>
+
    <xs:attributeGroup name="attlist.application">
        <xs:attribute name="scan-providers" type="components:boolean">
            <xs:annotation>
@@ -92,5 +120,67 @@
           </xs:annotation>
       </xs:attribute>
    </xs:attributeGroup>
+   
+   	<xs:attributeGroup name="attlist.abstractResource">
+		<xs:attribute name="path" type="xs:string" use="required" >
+			<xs:annotation>
+				<xs:documentation>
+					Location of the resource. For example /user
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+		<xs:attribute name="media-types" type="xs:string">
+			<xs:annotation>
+				<xs:documentation>
+					Media type this resource will operate on. Defaults to application/xml.
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+	</xs:attributeGroup>
 
+	<xs:attributeGroup name="attlist.resourceHome">
+		<xs:attribute name="entity-id-class" type="xs:string"
+			use="required">
+			<xs:annotation>
+				<xs:documentation>
+					Entity identifier class.
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+		<xs:attribute name="entity-home" type="components:elType"
+			use="required">
+			<xs:annotation>
+				<xs:documentation>
+					EntityHome component that will be used for operating database.
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+		<xs:attribute name="readonly" type="xs:boolean" default="false">
+			<xs:annotation>
+				<xs:documentation>
+					Disable "write" operations on this resource. Resource will only allow GET
+					method.
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+	</xs:attributeGroup>
+
+	<xs:attributeGroup name="attlist.resourceQuery">
+		<xs:attribute name="entity-class" type="xs:string" use="required">
+			<xs:annotation>
+				<xs:documentation>
+					Entity class.
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+		<xs:attribute name="entity-query" type="components:elType">
+			<xs:annotation>
+				<xs:documentation>
+					EntityQuery component that will be used for
+					listing operation (GET on path). If not set, it will be created
+					automatically.
+				</xs:documentation>
+			</xs:annotation>
+		</xs:attribute>
+	</xs:attributeGroup>
 </xs:schema>




More information about the seam-commits mailing list