[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