Hi all,
I'm a Spring Framework user (beneficiary!) and of course have been
forced to find ways in which Restlet can integrate easily with Spring.
I thought I would just give out the solution we're using for Spring
integration. I haven't seen anyone post a solution quite like this yet,
and I believe this is a bit easier (possibly more correct) way of doing
Spring integration than is currently in the spring extension package of
the restlet distribution.
I guess I'm only referring to integration between Restlet's Resource
class and Spring, not anything else.
The problem, of course, is that Resource is stateful and cannot be
reused per connection (maybe something which can be fixed in a future
release) making it impossible to easily integrate with Spring (which is
essentially singleton based).
The Finder class is essentially a factory that is meant to produce these
stateful Resource instances. This was recently brought up on the
mailing list (Re: Proposal for ResourceFactory, Sean Landis). The trick
is to think of the Finder class as a Resource factory and then
delegate to a spring bean from there.
Finder thankfully happens to be a stateless bean, and therefore can be
easily added into your Spring configuration. So the trick is two-fold:
a) Create an interface which has methods similar to Resource, except
make the interface stateless by passing in the Request and Response
objects. I named this interface ResourceService.
b) Create a Finder which creates a delegating Resource class which
delegates to the above ResourceService interface. I call this finder
ResourceServiceFinder.
I'm attaching the source code to these two classes. In order to use
them, you will need to setup your spring configuration to look like this:
<!--
This is a ResourceService implementation.
Ie. SomeResourceService implements ResourceService
-->
<bean id="someResourceService" class="path.to.SomeResourceService">
<property name="someDAO" ref="someDAO" />
<property name="someOtherProperty" value="foo" />
</bean>
<!-- This defines a new Finder for the Service above -->
<bean id="someResourceServiceFinder" class="path.to.ResourceServiceFinder">
<property name="resourceService" ref="someResourceService" />
</bean>
That's it! Make sure that the "someResourceServiceFinder" Finder is
attached to your Restlet router somewhere. This too can be done via
Spring (ask me if you need direction on this).
The advantage here, as compared with the spring extension in the restlet
package, is that we're not using dynamic proxies to make it work. Ie.
In org.restlet.ext.spring.SpringFinder, the Javadoc says to use
<lookup-method> which Spring implement using CGLib to create a runtime
proxy to make this work properly. The problem is that this may cause
other problems if you are using proxies for other pieces in Spring (like
Aspects or transactions) as these runtime proxies can conflict, or if
you don't want to introduce the CGLib dependency.
Notice that the createResource method creates a new anonymous inner
Resource class and then delegates the appropriate methods to the interface.
I hope this helps someone. Jerome, please feel free to post this to the
wiki if you want.
Adam
/*
* Author: Adam Taft - [EMAIL PROTECTED]
* License: Public Domain 2007
*/
package com.adamtaft.restlet.restlets;
import java.util.List;
import org.restlet.Finder;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Representation;
import org.restlet.resource.Resource;
import org.restlet.resource.Variant;
import com.adamtaft.restlet.services.ResourceService;
public class ResourceServiceFinder extends Finder {
private ResourceService resourceService;
public void setResourceService(ResourceService resourceService) {
this.resourceService = resourceService;
}
@Override
public Resource createResource(Request request, Response response) {
return new Resource(getContext(), request, response) {
@Override
public List<Variant> getVariants() {
return resourceService.getVariants();
}
@Override
public boolean allowGet() {
return resourceService.allowGet(getRequest());
}
@Override
public boolean allowPost() {
return resourceService.allowPost(getRequest());
}
@Override
public boolean allowPut() {
return resourceService.allowPut(getRequest());
}
@Override
public boolean allowDelete() {
return
resourceService.allowDelete(getRequest());
}
@Override
public Representation getRepresentation(Variant
variant) {
return resourceService.get(getRequest(),
getResponse(), variant);
}
@Override
public void post(Representation representation) {
resourceService.post(getRequest(),
getResponse(), representation);
}
@Override
public void put(Representation representation) {
resourceService.put(getRequest(),
getResponse(), representation);
}
@Override
public void delete() {
resourceService.delete(getRequest(),
getResponse());
}
/**
* Overriden to allow a POST without a message body.
*/
@Override
public void handlePost() {
post(getRequest().getEntity());
}
};
}
}
/*
* Author: Adam Taft - [EMAIL PROTECTED]
* License: Public Domain 2007
*/
package com.adamtaft.restlet.services;
import java.util.List;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Representation;
import org.restlet.resource.Variant;
import org.springframework.transaction.annotation.Transactional;
public interface ResourceService {
List<Variant> getVariants();
boolean allowGet(Request request);
boolean allowPost(Request request);
boolean allowPut(Request request);
boolean allowDelete(Request request);
@Transactional(readOnly=true)
Representation get(Request request, Response response, Variant variant);
@Transactional(readOnly=false)
void post(Request request, Response response, Representation
representation);
@Transactional(readOnly=false)
void put(Request request, Response response, Representation
representation);
@Transactional(readOnly=false)
void delete(Request request, Response response);
}