Jersey #Path annotation mandatory at class level - java

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

Related

Quarkus does not use JaxRs #Path when declared in interface in an external jar

I have a Java interface which declares all things related to JaxRs like this:
#Path("/notes-service")
public interface NotesApi {
#POST
#Path("/notes")
#Consumes({ "application/json" })
#Produces({ "application/json" })
Response createNote(
#HeaderParam("Accept-Language") #DefaultValue("en") String acceptLanguage,
#Valid CreateNoteDTO createNoteDTO);
}
The interface is shipped as an external dependency as a jar.
And an implementation in my service:
#ApplicationScoped
#Transactional
public class NotesService implements NotesApi {
#Override
public Response createNote(String range, CreateNoteDTO createNoteDTO) {
// ...
}
}
Such a setup like above doesn't work. It returnes 404 for an endpoint /notes-service/notes
However when I copy the #Path part into the implenentation, everything works just fine:
#ApplicationScoped
#Transactional
#Path("/notes-service")
public class NotesService implements NotesApi {
#Override
public Response createNote(String range, CreateNoteDTO createNoteDTO) {
// ...
}
}
Also another workaaround: When I copy-paste the interface into my service then it also works without #Path declaration in an implementation.
This means that #Path annotation on interface level works just fine (I have checked it with a minimal service). But it doesn't work when the interface comes as a dependency.
Is it a bug in Quarkus or an expected behavior?
You need to add an empty beans.xml in your external project in src/main/resources/META-INF, so that Quarkus scans your external jar file.

Annotations for feature flipping REST end points

I have spring controller with several (REST) endpoints. I want to bring up multiple instances of this controller where each instance would have few endpoints selectively enabled/disabled.
Based on my reading so far, togglz provides feature flipping, but it doesnt enable/disable the REST endpoints (togglz provides API so that caller code can check if a feature is enabled); ff4j seems to be another alternative, but it was not very obvious from the documentation if it can enable/disable REST end points
I read the thread Feature Toggling Java Annotations but it is a longer implementation. Is there any package that I can use to specify the endpoints that need to be enabled/disabled in a configuration file and use annotation on REST endpoints to disable/enable them (this way the logic in my method stays untouched and minimizes the testing)
A class with the #Bean or #Component will be loaded by spring on startup through the bean visitor mechanism. To exclude this bean from the Spring context at startup you can create a BeanPostProcessor(here) and check for dedicated annotation BUT as far as I understand, you cannot put the bean back to the context at runtime.
As a consequence, you must make this bean 'intelligent' to perform the correct operation/mock (or send 503 HTTP code) when requests come in.
FF4j can indeed help you implementing this behaviour but not with a single annotation on top of your REST Controller. What you could do :
Create an interface, annotate the interface with the dedicated FF4J annotation
Create 2 implementations of the interface, each time with a different name
Use FF4J to choose an implementation or another at runtime.
Here some code snippet to get the idea :
public interface GreetingService {
#Flip(name = "theFeatureIDToToggle", alterBean = "greeting.french")
String sayHello(String name);
}
#Component("greeting.french")
public class GreetingServiceFrenchImpl implements GreetingService {
public String sayHello(String name) {return "Bonjour " + name;
}
#Component("greeting.english")
public class GreetingServiceEnglishImpl implements GreetingService {
public String sayHello(String name) {return "Hello " + name;
}
//... import
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:applicationContext-ff4j-aop-test.xml")
public class FeatureAdvisorTest {
#Autowired
private FF4j ff4j;
#Autowired
#Qualifier("greeting.english")
private GreetingService greeting
#Test
public void testAnnotatedFlipping_with_alterBean() {
ff4j.disable("theFeatureIDToToggle");
Assert.assertTrue(greeting.sayHello("CLU").startsWith("Hello"));
ff4j.enable("theFeatureIDToToggle");
Assert.assertTrue(greeting.sayHello("CLU").startsWith("Bonjour"));
}
}
You can toggle a single method or the whole class, as you wish all samples are available here.

Restlet with #RequestMapping annotation does not work

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)

Is it possible to define a jax-rs service interface separated from its implementation (with eclipse and jersey)?

I don't know if the title is confusing, but let's say I have this interface:
#Produces(MediaType.APPLICATION_JSON)
#Path("/user")
public interface UserService {
#GET
#Path("/{userId}")
public Response getUser(#PathParam("userId") Long userId);
}
Why when I try to implement a version Eclipse rewrites annotation for the overridden method but not for the class?
class UserServiceImpl implements UserService {
#Override
#GET
#Path("/{userId}")
public Response getUser(#PathParam("userId") Long userId) {
// TODO Auto-generated method stub
return null;
}
}
I was trying to create a standard definition for the restful web service and then having different implementations. Is something like this possible with standard jax-rs? Am I using wrong annotations by any chance?
You can use annotation inheritance only if you don't use any jax-rs annotation on the implementing class: it is stated on section 3.6 of JSR-339.
You redefine #Path and #Produces for the method but not for the class.
So the Path annotation in your code should be on the concrete class:
public interface UserService {
#GET
#Path("/{userId}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUser(#PathParam("userId") Long userId);
}
#Path("/user")
class UserServiceImpl implements UserService {
#Override
#GET
#Path("/{userId}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUser(#PathParam("userId") Long userId) {
// TODO Auto-generated method stub
return null;
}
}
BTW, the specification encourages us to replicate the annotations on the concrete classes:
For consistency with other Java EE specifications, it is recommended to always repeat annotations instead
of relying on annotation inheritance.
I had a similar issue working with interface automatically generated using the OpenAPI generator. The problem here was that I couldn't easily remove the #Path annotation from the interface and additionally adding it to the class caused ambiguity issues.
Problems with #Path-annotated interfaces however only occurs for resources that are automatically discovered on registered packages. For registered packages, all classes (and unfortunately interfaces as well) that are annotated with #Path will be instantiated.
To prevent that, you can just register your resources manually with your ResourceConfig as in the following example:
new ResourceConfig()
.registerClasses(UserServiceImpl.class);
Just make sure, your interfaces are not in a package that are registered with your ResourceConfig. Using this approach, you can use the implementation from your example and also the #Path annotation added to the interface will be interpreted correctly.
Disclaimer: There probably should not be any #Path path annotation on the interface in the first place, but unfortunately this is what the OpenAPI generator creates. If you find yourself in a similar situation, I hope this helps. Otherwise, you should refer to the accepted answer.

Spring-MVC Problem using #Controller on controller implementing an interface

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)

Categories

Resources