I need more ability to configure and/or define restlet classes
(Component, Route, Router, Application, Filter, Resource and Restlet)
with spring. I think I have a spring framework addiction :-)
There is something of an architectural mismatch between the restlets
framework and the spring framework. With spring the way it works best
to let the spring container create and configure object instances.
The context for object instance is injected by spring. With restlets
the context is passed throw the applications object instances with a
chain of constructors.
To get around this I'm not using spring to create the restlets class
instances directly. Instead I use spring to create factories that
make the instances. For example here's a Resource factory:
public abstract class ResourceFactory extends Finder {
public Resource createResource(Request request, Response response) {
Resource result = build();
result.init(getContext(), request, response);
return result;
}
public abstract Resource build();
}
public class PlayRestResourceFactory extends ResourceFactory
implements RouteCallback, UriPatternProvider{
@Override
public Resource build() {
return new Resource(){
private String testname;
@Override
public void init(Context context, Request request,
Response response) {
super.init(context, request, response);
testname = (String)
request.getAttributes().get("testname");
this.getVariants().add(new
Variant(MediaType.TEXT_PLAIN));
}
@Override
public Representation getRepresentation(Variant
variant) {
return new StringRepresentation(testname+"
"+playBean.getMessage());
}
};
}
private PlayBean playBean;
public void setPlayBean(PlayBean playBean) {
this.playBean = playBean;
}
public void routeCallback(Route r) {
System.out.println("PlayRestResourceFactory::routeCallback");
}
public String getUriPattern() {
return "/test3/tom_{testname}";
}
}
I have factory objects for Application, Filter, Resource, Restlet and Router.
Where I could not control the creation of objects with factories I've
added callback objects to at least give access to configure. Here's
the callback a Component:
public class PlayComponentCallback implements ComponentCallback {
public void componentCallback(Component c) {
c.setLogService(new LogService(false));
}
}
I have callbacks for Component, Route and Router.
I have extended ServerServlet use these new classes:
public class SpringRestletServlet extends ServerServlet {
private static final long serialVersionUID = 2974584650224678065L;
// init param name of spring bean name for component callback
private static final String COMPONENT_CALLBACK_INIT_PARAM =
"lazyeye.restlet.spring.servlet.SpringRestletServlet.componentCallback";
// init param name of spring bean name for application factory
private static final String APPLICATION_FACTORY_INIT_PARAM =
"lazyeye.restlet.spring.servlet.SpringRestletServlet.applicationFactory";
// default value of spring bean name for component callback
private static final String COMPONENT_CALLBACK_DEFAULT =
"restletComponentCallback";
// default value of spring bean name for application factory
private static final String APPLICATION_FACTORY_DEFAULT =
"restletApplicationFactory";
private String componentCallbackId;
private String applicationFactoryId;
/*
* reads the init parameters for component callback and application
factory
* then does parent init() and then send the the component to the
component
* callback.
*
* @see com.noelios.restlet.ext.servlet.ServerServlet#init()
*/
public void init() throws ServletException {
componentCallbackId =
this.getInitParameter(COMPONENT_CALLBACK_INIT_PARAM,COMPONENT_CALLBACK_DEFAULT);
applicationFactoryId =
this.getInitParameter(APPLICATION_FACTORY_INIT_PARAM,APPLICATION_FACTORY_DEFAULT);
super.init();
// Look to see if there is a ComponentCallback object defined
for this
// SpringRestletServlet instance and call it if it's there.
if ((getSpringContext() != null)
&&
(getSpringContext().containsBean(componentCallbackId))) {
ComponentCallback componentCallback =
(ComponentCallback) getSpringContext()
.getBean(componentCallbackId);
componentCallback.componentCallback(this.getComponent());
}
}
private ApplicationContext springContext;
/**
* @return spring application context
*/
public ApplicationContext getSpringContext() {
if (springContext == null) {
springContext = WebApplicationContextUtils
.getWebApplicationContext(this.getServletContext());
}
return springContext;
}
/*
* This is an overwrite of the parent method. Now the Application is
created using
* a an ApplicationFactory object obtained from the Spring application
context.
*
* @see
com.noelios.restlet.ext.servlet.ServerServlet#createApplication(org.restlet.Context)
*/
public Application createApplication(Context context) {
Application result = null;
if ((getSpringContext() != null)
&&
(getSpringContext().containsBean(applicationFactoryId))) {
ApplicationFactory applicationFactory =
(ApplicationFactory)
getSpringContext()
.getBean(applicationFactoryId);
result = applicationFactory.build(context);
}
if (result == null) {
result = super.createApplication(context);
}
return result;
}
}
None of this has been fully tested yet but I want to share what I've
done so far with the hope that I might get your opinions.
package lazyeye.restlet.spring.callback;
import org.restlet.Component;
public interface ComponentCallback {
public abstract void componentCallback(Component c);
}
package lazyeye.restlet.spring.callback;
import org.restlet.Route;
public interface RouteCallback {
public abstract void routeCallback(Route r);
}
package lazyeye.restlet.spring.callback;
import org.restlet.Router;
public interface RouterCallback {
public abstract void routerCallback(Router r);
}
package lazyeye.restlet.spring.factory;
import org.restlet.Application;
import org.restlet.Context;
/**
* The goal of the restlet-spring project is to be able define and/or
* configure all the restlet objects (Application, Filter, Resource, Restlet & ) form within a spring application
* context.
*
* @author TMcGee
*
*/
public interface ApplicationFactory {
public abstract Application build(Context context);
}
package lazyeye.restlet.spring.factory;
import java.util.List;
import java.util.Map;
import lazyeye.restlet.spring.callback.RouterCallback;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Filter;
import org.restlet.Restlet;
import org.restlet.Router;
import org.restlet.service.StatusService;
public class ApplicationFactoryImp implements ApplicationFactory {
public Application build(final Context context) {
return new Application(context) {
@Override
public Restlet createRoot() {
Restlet result = null;
Router router = routerFactory.build(context);
if(routerCallback != null){
routerCallback.routerCallback(router);
}
if(filterFactory != null){
Filter f = filterFactory.build(context);
f.setNext(router);
result = f;
}else{
result = router;
}
return result;
}
@Override
public StatusService getStatusService() {
if (statusService != null) {
return statusService;
} else {
return super.getStatusService();
}
}
};
}
private StatusService statusService;
public void setStatusService(StatusService ss) {
this.statusService = ss;
}
private FilterFactory filterFactory;
public void setFilterFactory(FilterFactory ff){
this.filterFactory = ff;
}
private RouterFactoryImp routerFactory = new RouterFactoryImp();
public void setAttachMappings(Map<String, Object> attachMappings) {
routerFactory.setAttachMappings(attachMappings);
}
public void setAttachList(List<UriPatternProvider> l) {
routerFactory.setAttachList(l);
}
private RouterCallback routerCallback;
public void setRouterCallback(RouterCallback rb){
routerCallback = rb;
}
}
package lazyeye.restlet.spring.factory;
import org.restlet.Context;
import org.restlet.Filter;
public interface FilterFactory {
public abstract Filter build(Context context);
}
package lazyeye.restlet.spring.factory;
import org.restlet.Finder;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Resource;
public abstract class ResourceFactory extends Finder {
public Resource createResource(Request request, Response response) {
Resource result = build();
result.init(getContext(), request, response);
return result;
}
public abstract Resource build();
}
package lazyeye.restlet.spring.factory;
import org.restlet.Context;
import org.restlet.Restlet;
public interface RestletFactory {
public abstract Restlet build(Context context);
}
package lazyeye.restlet.spring.factory;
import org.restlet.Context;
import org.restlet.Router;
public interface RouterFactory {
public abstract Router build(Context c);
}
package lazyeye.restlet.spring.factory;
import java.util.List;
import java.util.Map;
import lazyeye.restlet.spring.callback.RouteCallback;
import org.restlet.Context;
import org.restlet.Route;
import org.restlet.Router;
import org.restlet.resource.Resource;
public class RouterFactoryImp implements RouterFactory {
public Router build(Context context) {
Router router = new Router(context);
if (attachMappings != null) {
for (Object key : attachMappings.keySet()) {
Object value = attachMappings.get(key);
addToRouter(context, router, (String) key, value);
}
}
if (attachList != null) {
for (UriPatternProvider value : attachList) {
addToRouter(context, router, value.getUriPattern(), value);
}
}
return router;
}
private void addToRouter(Context context, Router router, String uriPattern,
Object value) {
Route route = null;
if (value instanceof Resource) {
route = router.attach(uriPattern, new InnerResourceFactory(
(Resource) value));
} else if (value instanceof RestletFactory) {
route = router.attach(uriPattern, ((RestletFactory) value)
.build(context));
} else if (value instanceof ResourceFactory) {
route = router.attach(uriPattern, (ResourceFactory) value);
}
if ((route != null) && (value instanceof RouteCallback)) {
((RouteCallback) value).routeCallback(route);
}
}
private class InnerResourceFactory extends ResourceFactory {
Resource resource;
private InnerResourceFactory(Resource r) {
resource = r;
}
@Override
public Resource build() {
return resource;
}
}
private Map<String, Object> attachMappings;
public void setAttachMappings(Map<String, Object> am) {
if (am != null) {
for (Object key : am.keySet()) {
Object value = am.get(key);
if (!((value instanceof Resource) || (value instanceof RestletFactory) || (value instanceof ResourceFactory))) {
throw new IllegalArgumentException(
"Value object must be Resource, RestletFactory or ResourceFactory.");
}
}
}
this.attachMappings = am;
}
private List<UriPatternProvider> attachList;
public void setAttachList(List<UriPatternProvider> l) {
if (l != null) {
for (Object value : l) {
if (!((value instanceof RestletFactory) || (value instanceof ResourceFactory))) {
throw new IllegalArgumentException(
"Value object must be RestletFactory or ResourceFactory.");
}
}
}
attachList = l;
}
}
package lazyeye.restlet.spring.factory;
public interface UriPatternProvider {
public abstract String getUriPattern();
}
package lazyeye.restlet.spring.servlet;
import javax.servlet.ServletException;
import lazyeye.restlet.spring.callback.ComponentCallback;
import lazyeye.restlet.spring.factory.ApplicationFactory;
import org.restlet.Application;
import org.restlet.Context;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.noelios.restlet.ext.servlet.ServerServlet;
/**
*
* @author TMcGee
*
*/
public class SpringRestletServlet extends ServerServlet {
private static final long serialVersionUID = 2974584650224678065L;
// init param name of spring bean name for component callback
private static final String COMPONENT_CALLBACK_INIT_PARAM = "lazyeye.restlet.spring.servlet.SpringRestletServlet.componentCallback";
// init param name of spring bean name for application factory
private static final String APPLICATION_FACTORY_INIT_PARAM = "lazyeye.restlet.spring.servlet.SpringRestletServlet.applicationFactory";
// default value of spring bean name for component callback
private static final String COMPONENT_CALLBACK_DEFAULT = "restletComponentCallback";
// default value of spring bean name for application factory
private static final String APPLICATION_FACTORY_DEFAULT = "restletApplicationFactory";
private String componentCallbackId;
private String applicationFactoryId;
/*
* reads the init parameters for component callback and application factory
* then does parent init() and then send the the component to the component
* callback.
*
* @see com.noelios.restlet.ext.servlet.ServerServlet#init()
*/
public void init() throws ServletException {
componentCallbackId = this.getInitParameter(COMPONENT_CALLBACK_INIT_PARAM,COMPONENT_CALLBACK_DEFAULT);
applicationFactoryId = this.getInitParameter(APPLICATION_FACTORY_INIT_PARAM,APPLICATION_FACTORY_DEFAULT);
super.init();
// Look to see if there is a ComponentCallback object defined for this
// SpringRestletServlet instance and call it if it's there.
if ((getSpringContext() != null)
&& (getSpringContext().containsBean(componentCallbackId))) {
ComponentCallback componentCallback = (ComponentCallback) getSpringContext()
.getBean(componentCallbackId);
componentCallback.componentCallback(this.getComponent());
}
}
private ApplicationContext springContext;
/**
* @return spring application context
*/
public ApplicationContext getSpringContext() {
if (springContext == null) {
springContext = WebApplicationContextUtils
.getWebApplicationContext(this.getServletContext());
}
return springContext;
}
/*
* This is an overwrite of the parent method. Now the Application is created using
* a an ApplicationFactory object obtained from the Spring application context.
*
* @see com.noelios.restlet.ext.servlet.ServerServlet#createApplication(org.restlet.Context)
*/
public Application createApplication(Context context) {
Application result = null;
if ((getSpringContext() != null)
&& (getSpringContext().containsBean(applicationFactoryId))) {
ApplicationFactory applicationFactory = (ApplicationFactory) getSpringContext()
.getBean(applicationFactoryId);
result = applicationFactory.build(context);
}
if (result == null) {
result = super.createApplication(context);
}
return result;
}
}