I have some ServerResources in my application which are identified by the #Service(name) annotation. The methods are annotated with the #Get and #Post restlet annotations.
Everything worked fine until recently I wanted to add another ServerResource which has to serve a URL pattern with different parameters and request methods, thus I tried to use #RequestMapping annotation on the methods like:
#Service
public class MyResource extends ServerResource {
#RequestMapping(value="/pathToMyResource/{parameter1}", method=RequestMethod.GET)
public Representation getResponseForGetRequest(Representation entity) {
...
}
//and for the other method:
#RequestMapping(value="/pathToMyResource/{parameter1}/{parameter2}", method=RequestMethod.POST)
public Representation getResponseForPostRequest(Representation entity) {
...
}
...
}
However my resource is not properly registered with org.restlet.ext.spring.SpringBeanRouter as it is not found when this URL is requested.
The only way I figured out multiple paths working for one resource is via XML configuration:
<bean name="/pathToMyResource/{parameter1}"
id="myGetResource"
class="com.mycompany.resource.MyResource"
autowire="byName" scope="prototype">
</bean>
<bean name="/pathToMyResource/{parameter1}/{parameter2}"
id="myPostResource"
class="com.mycompany.resource.MyResource"
autowire="byName" scope="prototype">
</bean>
and using Restlet annotation for methods:
public class MyResource extends ServerResource {
#Get
public Representation getResponseForGetRequest(Representation entity) {
...
}
//and for the other method:
#Post
public Representation getResponseForPostRequest(Representation entity) {
...
}
...
}
Do you know how the #RequestMapping annotation works with Restlet? I would like to avoid XML configuration completely and trying to find a way to get it working with annotations...
As I mentioned I have no problem with resources mapped only to one path like:
#Service("/pathToMyResource/{parameter1}")
public class MyResource extends ServerResource {
...
}
This is working fine... only multiple paths mapping causes problems.
Thanks for any help!
from what I see, we don't support annotations taken from Spring framework.
If you want to remove any xml configuration, you can follow two ways:
use classic Restlet code: define an Application, implement the createInboundRoot methods in order to define the routing aspects, use annotated ServerResource. (see this page for a simple example http://restlet.com/learn/guide/2.2/editions/jse/, or this one for a complete code http://restlet.com/learn/guide/2.2/introduction/first-steps/first-application,
use the JaxRs extension in order to rely on JaxRs annotations (see http://restlet.com/learn/guide/2.2/extensions/jaxrs)
Related
I have a class like:
public class TestService {
#Path("/v1/test1/list")
public Response getTest1() {
}
#Path("/v1/test2/list")
public Response getTest2() {
}
}
If I do not give #Path annotation at Class level, then this class is not recognized as a REST resource, but I cannot give "/v1" Path for this class since there is already another class with #Path("/v1").
What are possible workaround, to make this class to be recognized as a Rest Resource
Resource classes
A #Path annotation is required to define a resource class. Quoting the Jersey documentation:
Root resource classes are POJOs (Plain Old Java Objects) that are annotated with #Path, have at least one method annotated with #Path or a resource method designator annotation such as #GET, #PUT, #POST, #DELETE.
One possible solution
As already mentioned by Justas, one possible solution is to add the #Path("") annotation to the TestService class. However, it doesn't smell good:
#Path("")
public class TestService {
#GET
#Path("/v1/test1/list")
public Response getTest1() {
...
}
#GET
#Path("/v1/test2/list")
public Response getTest2() {
...
}
}
A better solution
I don't know what your project looks like, but instead of having a single class, I would have two classes, designed as following:
#Path("/v1/test1")
public class TestService1 {
#GET
#Path("/list")
public Response getTest1() {
...
}
}
#Path("/v1/test2")
public class TestService2 {
#GET
#Path("/list")
public Response getTest2() {
...
}
}
You can add empty path #Path("") or #Path("/"). However, this problem may show that you should design your code differently.
The #Path annotation is used to specify the URI through which a resource and an API can be accessed. Resource in this case is the REST Web service itself. Thus this annotation is present at the class level as well as the method level. It is mandatory to annotate a REST Web resource class with the #Path annotation. Thus if a user wants to access the 'Countries' through the resource 'HelloWorld' context, then:
Resource at the class level would have #Path("/"). This is the default annotation at the class level.
Resource at the API level would have #Path("Countries") annotation.
As a best practice, the class-level path annotation should be a noun that qualifies the Web service, and the method-level annotation can be a noun or an action (for example, user and add-user, respectively).
https://www.juniper.net/documentation/en_US/junos-space-sdk/13.1/apiref/com.juniper.junos_space.sdk.help/html/reference/spaceannoREST.html
I am trying to do some validation on requests coming into my service using the ContainerRequestFilter. Everything is working fine, however there is one problem - every single request gets pass through the filters, even though some of the filters will never apply to them (one filter only validates on ResourceOne, another only on ResourceTwo etc.)
Is there a way to set a filter only to be invoked on a request under certain conditions?
While it is not a blocker or hindrance, it would be nice to be able to stop this kind of behaviour :)
I assume that You are using Jersey 2.x (implementation for JAX-RS 2.0 API).
You have two ways to achieve Your goal.
1. Use Name bindings:
1.1 Create custom annotation annotated with #NameBinding:
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AnnotationForResourceOne {}
1.2. Create filter with Your annotation:
#Provider
#AnnotationForResourceOne
public class ResourceOneFilter implements ContainerRequestFilter {
...
}
1.3. And bind created filter with selected resource method:
#Path("/resources")
public class Resources {
#GET
#Path("/resourceOne")
#AnnotationForResourceOne
public String getResourceOne() {...}
}
2. Use DynamicFeature:
2.1. Create filter:
public class ResourceOneFilter implements ContainerRequestFilter {
...
}
2.2. Implement javax.ws.rs.container.DynamicFeature interface:
#Provider
public class MaxAgeFeature implements DynamicFeature {
public void configure(ResourceInfo ri, FeatureContext ctx) {
if(resourceShouldBeFiltered(ri)){
ResourceOneFilter filter = new ResourceOneFilter();
ctx.register(filter);
}
}
}
In this scenario:
filter is not annotated with #Provider annotation;
configure(...) method is invoked for every resource method;
ctx.register(filter) binds filter with resource method;
When we use #NameBinding we need to remove #PreMatching annotation from the Filter. #PreMatching causes all the requests go through the filter.
#PreMatching does not work together with #NameBinding, because the resource class/method is not yet known in pre-matching phase.
I solved this issue by removing #PreMatching from the filter and using binding priority. See ResourceConfig.register(Object component, int bindingPriority).
Filters to be executed before the resource simply get a higher priority.
The rest application in build using spring and jersy.
I need a rest application that can read the value of #path annotation from the properties file.
PFB the code sample:-
#Controller
#Path("report")
public class CommonService extends BaseService
{
#Path("specificGet")
#GET
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response getReport ()
{
-----------
-----------
}
I want the value that is now "specificGet" to be readable from a .properties file.
For example if i have the below entry in properties file:-
path.getwithoutparam=thispathforGet
I want to refer to this property for #path annotation
Please comments/suggestions will be very helpful.
I might get some downvotes :)) but I'm pretty sure there is no out-of-the-box support for property placeholders for #Path annotations.
I guess you would have to build your own annotation processor in this case, that plugs into Spring property handling system.
I just started working with spring and set up a simple spring project in NetBeans. By default it doesn't seem to use annotations.
In one of my controllers I'd like to handle different clicks on the form. I have looked at some solutions and it seems like adding a parameter check could work:
#RequestMapping(params = "someAction")
public ModelAndView doSomeAction() {
However it looks like all of the requests are going to:
public class SetupController implements Controller {
#Override
public org.springframework.web.servlet.ModelAndView handleRequest(
HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception {
Is there a way I can achieve the same effect without RequestMapping annotation?
It seams that you mixed the old (implements Controller) style with the new one annotation based style.
You can't (or at least should not) mix them, at least not for one class.
If you use Spring 3.x then I strongly recommend to use the Annotation based style.
#Controller
#RequestMapping("/whatever")
public class DemoController() {
#RequestMapping(params="x");
public ModelAndView doX() {
}
#RequestMapping(params="y");
public ModelAndView doY() {
}
}
To enable the Annotation Based style, you need at least this configuration settings:
<!-- Turns on support for mapping requests to Spring MVC #Controller methods
Also registers default Formatters and Validators for use across all
#Controllers -->
<mvc:annotation-driven conversion-service="applicationConversionService" />
I'm using spring 2.5 and annotations to configure my spring-mvc web context. Unfortunately, I am unable to get the following to work. I'm not sure if this is a bug (seems like it) or if there is a basic misunderstanding on how the annotations and interface implementation subclassing works.
For example,
#Controller
#RequestMapping("url-mapping-here")
public class Foo {
#RequestMapping(method=RequestMethod.GET)
public void showForm() {
...
}
#RequestMapping(method=RequestMethod.POST)
public String processForm() {
...
}
}
works fine. When the context starts up, the urls this handler deals with are discovered, and everything works great.
This however does not:
#Controller
#RequestMapping("url-mapping-here")
public class Foo implements Bar {
#RequestMapping(method=RequestMethod.GET)
public void showForm() {
...
}
#RequestMapping(method=RequestMethod.POST)
public String processForm() {
...
}
}
When I try to pull up the url, I get the following nasty stack trace:
javax.servlet.ServletException: No adapter for handler [com.shaneleopard.web.controller.RegistrationController#e973e3]: Does your handler implement a supported interface like Controller?
org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1091)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:874)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
javax.servlet.http.HttpServlet.service(HttpServlet.java:627)
However, if I change Bar to be an abstract superclass and have Foo extend it, then it works again.
#Controller
#RequestMapping("url-mapping-here")
public class Foo extends Bar {
#RequestMapping(method=RequestMethod.GET)
public void showForm() {
...
}
#RequestMapping(method=RequestMethod.POST)
public String processForm() {
...
}
}
This seems like a bug. The #Controller annotation should be sufficient to mark this as a controller, and I should be able to implement one or more interfaces in my controller without having to do anything else. Any ideas?
What I needed to do was replace
<tx:annotation-driven/>
with
<tx:annotation-driven proxy-target-class="true"/>
This forces aspectj to use CGLIB for doing aspects instead of dynamic proxies - CGLIB doesn't lose the annotation since it extends the class, whereas dynamic proxies just expose the implemented interface.
Ed is right, adding
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
works fine
If you wish to use interfaces for your Spring MVC controllers then you need to move the annotations around a bit, as mentioned in the Spring docs: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping
Using #RequestMapping On Interface Methods A common pitfall when
working with annotated controller classes happens when applying
functionality that requires creating a proxy for the controller object
(e.g. #Transactional methods). Usually you will introduce an interface
for the controller in order to use JDK dynamic proxies. To make this
work you must move the #RequestMapping annotations to the interface as
well as the mapping mechanism can only "see" the interface exposed by
the proxy. Alternatively, you could activate proxy-target-class="true"
in the configuration for the functionality applied to the controller
(in our transaction scenario in ). Doing so
indicates that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 8.6, “Proxying mechanisms”.
Unfortunately it doesn't give a concrete example of this. I have found a setup like this works:
#Controller
#RequestMapping(value = "/secure/exhibitor")
public interface ExhibitorController {
#RequestMapping(value = "/{id}")
void exhibitor(#PathVariable("id") Long id);
}
#Controller
public class ExhibitorControllerImpl implements ExhibitorController {
#Secured({"ROLE_EXHIBITOR"})
#Transactional(readOnly = true)
#Override
public void exhibitor(final Long id) {
}
}
So what you have here is an interface that declares the #Controller, #PathVariable and #RequestMapping annotations (the Spring MVC annotations) and then you can either put your #Transactional or #Secured annotations for instance on the concrete class. It is only the #Controller type annotations that you need to put on the interface because of the way Spring does its mappings.
Note that you only need to do this if you use an interface. You don't necessarily need to do it if you are happy with CGLib proxies, but if for some reason you want to use JDK dynamic proxies, this might be the way to go.
There's no doubt that annotations and inheritance can get a little tricky, but I think that should work. Try explicitly adding the AnnotationMethodHandlerAdapter to your servlet context.
http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup
If that doesn't work, a little more information would be helpful. Specifically, are the two annotated controller methods from the interface? Is Foo supposed to be RegistrationController?
I know it is too late but i'm writing this for anyone have this problem
if you are using annotation based configuration... the solution might be like this:
#Configuration
#ComponentScan("org.foo.controller.*")
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}
The true reason you need to use 'proxy-target-class="true"' is in DefaultAnnotationHandlerMapping#determineUrlsForHandler() method: though it uses ListableBeanFactory#findAnnotationOnBean for looking up a #RequestMapping annotation (and this takes care about any proxy issues), the additional lookup for #Controller annotation is done using AnnotationUtils#findAnnotation (which does not handles proxy issues)