JAX-RS: How to extend Application class to scan packages? - java

Currently, I do something similar to
import javax.annotation.Nonnull;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
#ApplicationPath("oauth")
public class OAuthApplication extends Application {
final Set<Class<?>> classes = new HashSet<>();
#Nonnull
#Override
public Set<Class<?>> getClasses() {
classes.add(RegisterResource.class);
return Collections.unmodifiableSet(classes);
}
}
No if I add ten new Resources on the ApplicationPath, I need to do
classes.add(<ClassName>.class);
ten times, it is tedious and sometimes forgetful as well.
Does JAX-RS or RESTEasy provide the way so that I can mention the package name and classes are scanned under it?
I know Jersey has something as
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
}
}
Reference
Any thoughts/ideas?
UPDATE
Seems we can do following in web.xml
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
Is there a specific Java equivalent?

"Is there a specific Java equivalent?"
Simply leave the class empty, meaning do not override getClasses() or getSingletons(). Per the spec - 2.3.2:
[...]
In either of the latter two cases, if both Application.getClasses and Application.getSingletons return an empty list then all root resource classes and providers packaged in the web application MUST be included in the published JAX-RS application. If either getClasses or getSingletons return a non-empty list then only those classes or singletons returned MUST be included in the published JAX-RS application.
So you can simply do
#ApplicationPath("oauth")
public class OAuthApplication extends Application {}
and your classpath will get scanned for #Path and #Provider classes. Override either method (returning a non-empty set), and only those classes will be added.
It should also be noted that public Map<String, Object> getProperties() can be safely overridden. You can use this to add arbitrary properties (even to register classes/features), as seen here, or if you need to configure a provider, you can do so in a Feature or DynamicFeature as seen here

Is a combination of all Application class, ResourceClass and #ApplicationPath
in agreement with the docs and the class ResourceConfig implements by jersey the way to do is:
#ApplicationPath(value = "/resource")
public class ApplicationREST extends ResourceConfig {
public ApplicationREST() {
//add single resources
register(SimpleResource1.class);
register(SimpleResource2.class);
...
//enable multipar feature
register(MultiPartFeature.class);
...
//enable scan package and recursive
packages(true, "com.example.rest");
}
}
I hope this helps you

It is very simple:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/webapi")
public class MyApp extends Application {
}
This will scan all the classes annotated with #Provider and #Path.

Related

Unused Bean Get Called in Spring App

I have some strange behavior in my Spring App, here is my Java Spring Boot application structure:
in package com.somethingsomething.packageA, I have 2 files
First is ParentA.java
package com.somethingsomething.packageA;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ParentA {
#Autowired
private ChildA childA;
public ChildA getChildA() {
return childA;
}
#PostConstruct
public void ParentAPostConstruct() {
System.out.println("ParentA PostConstruct were called");
}
}
Second is ChildA.java
package com.somethingsomething.packageA;
import org.springframework.stereotype.Component;
#Component
public class ChildA {
public ChildA() {
System.out.println("ChildA were called");
}
}
and then under package com.somethingsomething.packageB, I also have two similar files.
First is ParentB.java
package com.somethingsomething.packageB;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ParentB {
#Autowired
private ChildB childB;
public ChildB getChildB() {
return childB;
}
#PostConstruct
public void ParentBPostConstruct() {
System.out.println("ParentB PostConstruct were called");
}
}
Second is ChildB.java
package com.somethingsomething.packageB;
import org.springframework.stereotype.Component;
#Component
public class ChildB {
public ChildB() {
System.out.println("ChildB were called");
}
}
Both of packageA and packageB have similar structure. Then under com.somethingsomething I have two main function for both package:
ForPackageA.java
package com.somethingsomething;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.somethingsomething.packageA.ParentA;
#SpringBootApplication
public class ForPackageA {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(ForPackageA.class, args);
ParentA parentA =
applicationContext.getBean(ParentA.class);
System.out.println(parentA);
System.out.println(parentA.getChildA());
}
}
and ForPackageB.java
package com.somethingsomething;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.somethingsomething.packageB.ParentB;
#SpringBootApplication
public class ForPackageB {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(ForPackageB.class, args);
ParentB parentB =
applicationContext.getBean(ParentB.class);
System.out.println(parentB);
System.out.println(parentB.getChildB());
}
}
When i run ForPackageA.java, this will appear in log:
ChildA were called
ParentA PostConstruct were called
ChildB were called
ParentB PostConstruct were called
com.somethingsomething.packageA.ParentA#88d6f9b
com.somethingsomething.packageA.ChildA#47d93e0d
The ChildB were called and ParentB PostConstruct were called were not suppose to be there, since ForPackageA.java doesn't depend on those beans. Why is this happening?
The same thing also happening when i run ForPackageB.java, which will log following:
ChildA were called
ParentA PostConstruct were called
ChildB were called
ParentB PostConstruct were called
com.somethingsomething.packageB.ParentB#610db97e
com.somethingsomething.packageB.ChildB#6f0628de
But in this case ChildA were called and ParentA PostConstruct were called were not suppose to be logged.
So, why is this peculiar behavior is happening? Is this default behavior of Spring?
Edit:
If let say i add following line
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) to ParentB.java
(after #Component)
It will not make ParentB PostConstruct were called appear when i run
ForPackageA.java.
and also, if i add those line to ChildB.java it will also not
make ChildB were called appear when i run ForPackageA.java
Why are the beans act differently in prototype mode, i.e. they are not getting called like when they are in singleton mode?
Yes, this is default behaviour of Spring. At application start all Beans with a #Component annotation get created, no matter if you use them or not.
The call applicationContext.getBean(ParentB.class) then simply returns the already created Bean.
To answer your edit:
Spring Beans are by default Singletons, so theres always only one instance of the bean per applicationContext. This is Inversion of Control, meaning that Spring handles object instantiation, not you.
Beans with the Prototype scope can have multiple object instances and can, in a way, be instantiated by you. (By calling applicationContext.getBean(ParentA.class)). This is similar to doing something like ParentA a = new ParentA().
I suggest you read this to get a deeper understanding of scopes.
The bean object creation doesn't depends on which object you are trying to get. Whenever spring application gets launched, its all components object are created automatically and you are getting already created component object using applicationContext.getBean(componentClass) method. Hope this will help you to understand why you are getting logs for every object.
Why is this happening?
When you start your Spring app the ApplicationContext is initialised by component scanning your application and registering all Spring annotated beans in the context. This allows them to be injected as required.
Is this default behaviour of Spring?
Yes. You can change this behaviour by configuring the component scanning to only look at specified packages if you wish (although the use cases for this are few and far between).

#RolesAllowed etc. without web.xml

I am currently working on a quite modular application where we have many jars that will be packaged and glued together in a war file.
Some of these jar files have REST resources that want to be secured. Common way is the #RolesAllowed annotation etc.
From my current knowledge this implies an existing web.xml in the WAR. This way we would have to implement jar-specific information (e.g. context roots) inside the WAR, not in the place where it belongs.
Like the most things nowadays - is there a way to programmatically set up security contexts etc. without a web.xml?
You can restrict access to your REST resources by registering RolesAllowedDynamicFeature in your REST configuration class that extends from ResourceConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
super(ApplicationConfig.class);
register(RolesAllowedDynamicFeature.class);
}
}
So you can use your application roles on your resources methods like this
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
#Path("secured")
#PermitAll
public class SecuredResource {
#GET
#Path("user")
#RolesAllowed("user")
public String user(#Context SecurityContext sc) {
boolean test = sc.isUserInRole("user");
return (test) ? "true": "false";
}
#GET
#Path("admin")
#RolesAllowed("admin")
public String admin(#Context SecurityContext sc) {
boolean test = sc.isUserInRole("admin");
return (test) ? "true": "false";
}
}
Jersey documentation has more details on securing REST resources using annotations here
https://jersey.github.io/documentation/latest/security.html#d0e12428
I've not worked with JAX-RS for a while, but the last time I checked, when using annotation-based security, web.xml is not optional.
See my answer for details.
https://stackoverflow.com/a/20023018/839733

Cannot get simple RS webservice to work with Eclipse Mars

I followed a few tutorials for settings up a very simple Jersey Webservice which were more than unclear to me.
Sometimes the tutorial was talking about changing the web.xml, others were saying that it is only necessary to have certain annotations in your service class.
So I ended up with the following conclusion:
Using Jersey 2.x you won't have to do anything specific in your web.xml, just have the jersey-container-servlet.jar in your classpath and create your service class as follows:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#ApplicationPath("rest")
public class RestService extends Application {
#GET
#Path("/sayhello")
#Produces(MediaType.TEXT_PLAIN)
public Response sayHello() {
return Response.ok("Hello World").build();
}
}
This should allow me to access the API using http://localhost:8080/EETest/rest/sayhello
I double checked that the project has been deployed with no error and the Tomcat7 server is running. All the jersey jars and dependencies are in my lib folder and have been added to the project libraries. The modified index.html is showing fine when calling http://localhost:8080/EETest
But the web service is not responding (404 page is showing instead).
I know it must be something very basic I'm doing wrong here ... I'm running out of options.
EDIT: This is my web.xml for what it's worth
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>EETest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
Not sure where you learned to do this
#ApplicationPath("rest")
public class RestService extends Application {
#GET
#Path("/sayhello")
#Produces(MediaType.TEXT_PLAIN)
public Response sayHello() {
return Response.ok("Hello World").build();
}
}
But it's wrong. In JAX-RS, we have resource classes. The resource class should be annotated with #Path, not #ApplicationPath. The Latter is for the application configuration class. So you should have something like
#ApplicationPath("/rest")
public class AppConfig extends Application {}
#Path("/")
public class RestService {
#GET
#Path("/sayhello")
#Produces(MediaType.TEXT_PLAIN)
public Response sayHello() {
return Response.ok("Hello World").build();
}
}
What the empty class with the #ApplicationPath does is trigger classpath scanning. So the classpath will be scanned for classes that are annotated with #Path and #Provider, and those classes will be registered.
In the example, I used #Path("/") so that you can still use the same URL /rest/sayHello. But generally, the resource class will be have a path mapped to a collection URL, like /rest/animals, so you would use #Path("animals") on the class, and you can add sub-resources with methods in that class also annotated with #Path. Any method not annotated with #Path, but has a method like #GET, will be mapped to the root resource path /rest/animals.
A couple other things. Remember I mentioned the classpath scanning being triggered with the empty Application class annotated with #ApplicationPath. Well this is kind of discouraged. You could register classes explicitly instead
#ApplicationPath("/rest")
public class AppConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(RestService.class);
return classes;
}
}
But when we're using Jersey, it's preferred to use the Jersey specific classes (unless you have a requirement to keep it portable between JAX-RS implementations). With Jersey, you would use it's ResourceConfig class (which is a subclass of Application). You could register individual classes with the register method, of you can trigger package scanning (which is not the same as classpath scanning) using the packages method
#ApplicationPath("/rest")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("the.packages.to.scan");
register(RestService.class);
}
}

in jersey 2.x am getting the error like java.util.concurrent.ExecutionException

i have used the jersey implementation of a jaxrs but iam unable to the programme following is case where i am getting problem any idea help me
in following programme i used the jersy 2.x implementaion of jaxrs
i implemented the programme using jersey implemetation of jax-rs(restfull)
2 classes i have written instead of web.xml i used the class
MyResource.java
package com.rest.application;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import com.rest.webservice.SampleService;
#ApplicationPath("rest")
public class MyResource {
private Set s;
public MyResource() {
s=new HashSet();
s.add(new SampleService());
}
public Set getSingletons() {
return s;
}
}
SampleService.java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
#Path("sample")
public class SampleService {
#GET
#Produces("text/html")
#Path("{username}")
public String sayHello(#PathParam("username")String s) {
return "<font color='blue' size=8>Hello:" +s+ "</font>";
}
#GET
#Produces("text/plain")
public String sayBye() {
return "Bye";
}
}
i added the all jars needed to this programm
still i am getting following error
java.util.concurrent.ExecutionException:
org.apache.catalina.LifecycleException:
Failed to start component [StandardEngine[Catalina].
StandardHost[localhost].StandardContext[/RestApp2]]
Caused by: org.apache.catalina.LifecycleException:
Failed to start component [StandardEngine[Catalina].
StandardHost[localhost].StandardContext[/RestApp2]]
like this same error is displaying everywhere
when i changed the server tomcat 7 to 6
it is working but not displaying the output
will anybody have any idea thanking you in advance
This says, #ApplicationPath("rest") may be applied only to the subclass of Application.
Can you share more on what are you trying to do and what is the complete stack trace. Are you using web.xml ?
As #MSD mentioned, your use of #ApplicationPath is incorrect. See the Jersey documentation on Application Deployment to see all the different deployment options, and how they work in different environments.
Basically, the easiest way put the #ApplicationPath on an empty Application class
#ApplicationPath("/rest")
public class MyApplication extends Application {}
This will scan the entire classpath for #Provider and #Path annotated classes, to register with the application. Though this may seem easier, the more common approach, when working with Jersey is to use it's ResourceConfig class, which is a subclass of Application. You can register packages, which will scan the packages and subpackages
#ApplicationPath("/rest")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.my.packages");
}
}
One benefit is that sometimes there will be third party dependencies that are annotated, but you don't want registered. To register individual classes just use register(...class) in the ResourceConfig.
Now the reason for the error in Tomcat 7 and not 6, is most likely because Tomcat 6 (servlet 2.5) does not have the sevlet pluggability mechanism, which uses the ServletContainerInitializer. The Jersey implementation of this initializer loads the application, looking for the #ApplicationPath on the Application subclass. If you're not in a 3.0 environment, this functionality will not work.
Note the Jersey initializer is included in the jersey-container-servlet jar. You can read more about it here

JAX-RS Resource not found in GlassFish Server

I've been trying to create a simple Restful WebService, using NetBeans Ide.
My Java EE Version is: Java EE 7 Web.
I created a new Java Web Application, setting this ContexPath: /DukesAgeService.
Now, running my application, browser display my Index.html page at:
http://localhost:8080/DukesAgeService/
so, everything works fine.
Then, I tried to create a simple restful resource using the RESTful Web Service Wizard.
So, I created this class:
package firstcup.webservice;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
/**
* REST Web Service
*
* #author nolanof
*/
#Path("dukesAge")
public class DukesAgeResource {
#Context
private UriInfo context;
/**
* Creates a new instance of DukesAgeResource
*/
public DukesAgeResource() {
}
/**
* Retrieves representation of an instance of firstcup.webservice.DukesAgeResource
* #return an instance of java.lang.String
*/
#GET
#Produces("text/plain")
public String getText() {
return "hello world";
}
}
But running my application, at url:
http://localhost:8080/DukesAgeService/dukesAge
I get a 404-not found page.
I exptected that any incoming get request that has the url of "/dukesAge" was handled by DukesAgeResource class getText method. Whats' wrong?
Thanks
You're probably missing the JAX-RS application servlet. You can either define it in the web.xml or if you want to go xml-less, you can use an Application subclass. The easiest way IMO is just to use the Application subclass annotated with #ApplicationPath. A servlet will be created and the servlet path will be set to the value in the annotation. Something like
#ApplicationPath("/rest")
public class RestApplication extends Application {
// All request scoped resources and providers
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(DukesAgeResource.class);
return classes;
}
// all singleton resources and providers
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
return singletons;
}
}
Then the resource should be accessed via
http://localhost:8080/DukesAgeService/rest/dukesAge.
There are other ways, but this is the portable way. Glassfish uses Jersey, but creating a Java EE web application from scratch in Netbeans will only import compile time Java EE standard classes (no Jersey dependencies). So the above is really your best bet to start off with.
You can see other deployment options at the Jersey Documentation. For some of the options, you may need to add some Jersey compile-time dependencies. That's why I just mentioned the above. No other jars needed.
Another thing that would cause a 404, is if you specify the JAX-RS servlet path as /*. This will conflict with the default servlet that serves the static resources like your html pages. That's why I set it to /rest.
UPDATE
It is also stated in the JAX-RS spec that if there are empty sets returned in the getClasses() and getSingletons(), implicit classpath scanning should occur. (provider) Classes annotated withe #Provider will by default be added as singletons and resource classes annotated with #Path will be per-request objects (meaning a new object is created each request). So you could alternatively just have
#ApplicationPath("/rest")
public class RestApplication extends Application {
// Left empty
}
and it should work just the same.
You may have initialized some path in your web.xml, probably that is why you are getting a 404 error while you call the service. Do check your web.xml and in case it is set to anything rather then * then please append that to your service call to get it working.

Categories

Resources