Jersey 2 and Spring Boot - Not able to inject using #Context on Provider - jax-rs

Using Jersey 2.3 on Spring Boot 2.4. I have 2 JAX-RS providers. One of them implements ContainerRequestFilter(PreMatching) and another one extends JacksonJaxbJsonProvider(from jackson-jaxrs-json-provider).
I am setting a property in ContainerRequestFilter onto ContainerRequestContext. Then I am trying to inject ContainerRequestContext onto another JAX-RS Provider using #Context. But this injection is always coming null.
If I inject same object onto a JAX-RS resource using #Context, Jersey does inject it. Not sure what I am missing here. Any help is greatly appretiated.
#PreMatching
#Provider
public class MyJaxRSContextProvider implements ContainerRequestFilter {
#Context
Providers providers;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setProperty("myProperty", property);
}
}
#Provider
#Consumes(MediaType.WILDCARD)
#Produces(MediaType.WILDCARD)
public class MyJsonJaxRSProvider extends JacksonJaxbJsonProvider {
#Context
ContainerRequestContext requestContext;
#Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
//requestcontext is always null
requestContext.getProperty("myProperty");
}
}

Things to consider:
In some cases, if you register the provider as an instance, then injection may not occur. Best thing to do is to register the provider as a class or just use scanning provided by Jersey.
Some injectables are not proxiable, which will prevent smaller scoped services to be injected into larger scoped serviced (example: request scoped object into a a singleton). In this case, you should wrap the injection in javax.inject.Provider
#Inject
private javax.inject.Provider<ContainerRequest> requestProvider;
...
ContainerRequest request = requestProvider.get();

Related

After overriding the Application.getClasses() by a custom MessageBodyReader, methods on resource classes cannot be invoked

In a RESTEasy project running on Wildfly server, there is a resource class:
#Path("/company")
public class CompanyResource {
#Inject
CompanyService companyService;
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public void update(Company company) {
companyService.update(company);
}
}
Initially the REST API configuration class just extends Application without any extra #override on the existing methods of Application class. An http request, http://localhost:8080/workcontext/company, with PUT as the http request method could work, meaning the CompanyResource.update() can be invoked successfully when receiving the aforementioned http request.
However, I then tried to add a custom MessageBodyReader<Company>:
public class CompanyReader implements MessageBodyReader<Company> {
#Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
#Override
public Company readFrom(Class<Company> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
try(JsonReader reader = Json.createReader(entityStream)) {
JsonObject companyJson = reader.readObject();
Company company = new Company();
company.setCompanyCode(companyJson.getString("companyCode"));
company.setName(companyJson.getString("name"));
company.setHeadquarter(Region.valueOf(companyJson.getString("headquarter")));
return company;
}
}
}
In order to make this custom MessageBodyReader<Company> work, I registered this class by overriding the Application.getClasses():
public class JaxRsConfiguration extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(CompanyReader.class);
return classes;
}
}
I expected that this MessageBodyReader<Company> could be invoked when sending the same http PUT request, but on the opposite the response is: RESTEASY003210: Could not find resource for full path: http://localhost:8080/workcontext/company
Question: How to make this custom MessageBodyReader work?
You should annotate you're CompanyReader with #Provider. In your application if you return any classes in Application.getClasses() or Application.getSingletons() then, per the spec, those are the only classes allowed to be used in your application.
If either getClasses or getSingletons returns a non-empty collection then only those classes or singletons returned MUST be included in the published JAX-RS application.

jaxrs queryparam not loaded for interceptor

I have a REST service of the form:
#GET
#NeedsInterception
public void getSomething(#QueryParam("xxx") #MyAnnotation String thing) {
//Stuff
}
I then have an interceptor for #NeedsInterception.
In it, I perform some logic on the element annotated with #MyAnnotation.
However, when the interceptor is called, the MethodInvocation object has not yet been resolved with the value of the QueryParam, instead it is always "";
Is there a way for me to make the interception happen after the QueryParam is resolved?
Don't know which kind of interceptor you are using but a jax-rs ReaderInterceptor is intended to wrap calls to MessageBodyReader.readFrom. As you don't send a request body with a #GET request this kind of interceptor won't be used.
A ContainerRequestFilter should help:
#Provider
public class SomeFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
MultivaluedMap<String,String> queryParameters = requestContext.getUriInfo().getQueryParameters();
}
}

How to get target method of a given JAX-RS request?

Is there a way to obtain the java.lang.reflect.Method of the method (which is annotated with #Path) which will be called for a given HttpServletRequest?
Here is my use case : I'm in a Java EE Filter and want to know if the method which will be invoked later is annotated with another specific annotations.
(I'm using RESTEasy 3.0.7)
It's easy if you can use a ContainerRequestFilter instead of a normal Servlet Filter.
#Provider
public class SomeFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Method method = resourceInfo.getResourceMethod();
}
}

ejb in session context is allways null

i have created sigleton session bean which keeps one connection to my mongo database. It works well in jax-rs class when using #EJB annotation - after controller is contructed and bean is injected it calls init method anotated with #PostConstruct.
Then i created similar class, which is implementing SecurityContext. I used same pattern as in controller, but it is not working properly. init() method is never called and EJB instance is always null.
So is there a way to inject EJB to my SecurityContext implemetation ? it works well unless i try to inject and use MongoConnection
my singleton session bean I use to connect mongo database:
#Singleton
#Startup
public class MongoConnection {
#PostConstruct
public void init() {
// initialize properties
}
I use it in JAX-RS controller. it works here, also in classes inherited from EntityController.
Produces(MediaType.APPLICATION_JSON)
public class EntityController extends Application {
#Context
private UriInfo context;
**#EJB
protected MongoConnection connection;**
public EntityController() {
#PostConstruct
void init() {
...
connection.getMongo();
connection.getDatabaseName();
...
}
}
I implemented my own security context, which is looking for loged user roles in mongo database.
public class MongoSecurityContext implements SecurityContext {
**#EJB
private MongoConnection connection;**
public MongoSecurityContext() {
}
#PostConstruct
void init() {
...
connection.getMongo();
connection.getDatabaseName();
...
}
public MongoSecurityContext(ContainerRequestContext requestContext) {
token = requestContext.getHeaderString("token");
}
#Override
public boolean isUserInRole(String roleName) {
//**connection is allways null**, so it returns false;
if (connection == null)
return false;
}
}
EDIT:
I forget, i also have this warning in glassfish 4 console:
A provider extremeteacher.mongo.connection.MongoConnectionEjb registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider extremeteacher.mongo.connection.MongoConnectionEjb will be ignored
EDIT2:
#Provider
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
requestContext.setSecurityContext(new MongoSecurityContext(requestContext)) ;
}
}
Injection does not work for objects created with new because the container is never given control to perform the injection. I recommend moving the #EJB to the filter and passing it to the MongoSecurityContext constructor.

EJB 3.1 : Singleton bean not getting injected inside another stateless bean though both beans are getting registered

Here is my bean that is trying to inject a singleton bean InformationService :
#Path("/information/{name}")
#Stateless (name="InformationResource")
public class InformationResource {
#EJB
private InformationService appService;
#GET
#Produces(MediaType.APPLICATION_XML)
public Information getInfo(#PathParam("name") String name){
return appService.getMap().get(name);
}
#PUT
#POST
#Consumes(MediaType.APPLICATION_XML)
public Information putInfo(#PathParam("name") String name, Information info){
return appService.getMap().put(name,info);
}
#DELETE
public void deleteInfo(#PathParam("name") String name){
appService.getMap().remove(name);
}
}
This is the InformationService class
#Singleton
public class InformationService {
private Map<String,Information> map;
#PostConstruct
public void init(){
map = new HashMap<String,Information>();
map.put("daud", new Information("B.Tech","Lucknow"));
map.put("anuragh", new Information("M.Sc","Delhi"));
}
public Map<String,Information> getMap(){
return map;
}
}
Its part of a very simple JAX-RS implementation and I am deploying as war in JBoss 6.1 Final. The problem is that InformationService throwing a NullPointerException when I make the proper get request. If I initialize appService explicitly, everything works fine. Why is #EJB annotation not working ?
Are you using Jersey as REST implementation? If so, EJB injection is not supported out of the box.
This link provides more information on this and also a solution.
Check that your #Singleton is javax.ejb.Singleton.
Any other exceptions before NPE ?