I'm trying to build a REST API using JAX-RS, specifically the Jersey implementation. I tried to implement Authentication like in this StackOverflow question.
Basically I'm using a ContainerRequestFilter to filter the HTTP Requests, based on the Authorization header. This class defines a property, annotated with the #Inject attribute (I'm using Kotlin, not Java):
#Inject
var authenticationService: IAuthenticationService? = null
I've registered this authentication service in my AbstractBinder:
class MyApplicationBinder : AbstractBinder() {
override fun configure() {
bind(AuthenticationService::class.java) to IAuthenticationService::class.java
}
}
and registering this application binder to my ResourceConfig:
class MyResourceConfig : ResourceConfig() {
init {
// ...
register(MyApplicationBinder())
}
}
However I'm getting the runtime exception on startup:
org.glassfish.hk2.api.UnsatisfiedDependencyException:
There was no object available for injection at SystemInjecteeImpl(
requiredType=IAuthenticationService,
parent=AuthenticationRequestFilter,
qualifiers={},
position=-1,
optional=false,
self=false,
unqualified=null,
452444366
)
I have no idea why the registration in my AbstractBinder is ignored by the HK2 Container! Can someone please help me out? Thanks!
Related
With JAXRS-2.0 (Jersey 2.2, specifically) I'm trying to apply a request interceptor to a specific resource provider class (which is in a 3rd party library), and I'm obviously doing it wrong. I am getting the error below - I'm a bit baffled as to the cause. The net effect is that the interceptor is being invoked on every request to every provider instead of the 1 provider. This is the error:
2017-11-26 10:43:51.061
[localhost-startStop-1][WARN][o.g.j.server.model.ResourceMethodConfig]
- The given contract (interface javax.ws.rs.container.DynamicFeature) of class com.idfconnect.XYZ provider cannot be bound to a resource
method.
The interceptor class is defined as:
#Provider
public class XYZ implements WriterInterceptor, DynamicFeature {
In my ResourceConfig I'm registering the interceptor for the specific provider as follows (I suspect this is where I've gone astray):
#ApplicationPath("service")
public class MyApp extends ResourceConfig {
public MyApp() {
ResourceConfig rc = register(SomeThirdPartyResource.class);
rc.register(XYZ.class);
...
Can someone help me figure out how to bind the interceptor to SomeThirdPartyResource class only?
You shouldn't make your provider implement DynamicFeature. This is probably the cause of the warning. You are trying to register the interceptor, which is also a DynamicFeature, and Jersey is telling you that DynamicFeature is not something that is supposed to be registered to a method.
You should make a separate class for the DynamicFeature and inside the configure check for the resource you want to attach your provider to (using the ResourceInfo, then register it accordingly. For example
class XYZ implements DynamicFeature {
#Override
public void configure(ResourceInfo info, FeatureContext ctx) {
if (info.getResourceClass().equals(ThirdPartyResource.class) {
ctx.register(YourWriterImplementation.class);
// or
ctx.register(new YourWriterImplementation());
}
}
}
The reason you are getting all the resources hit by the interceptor is because you are registering the interceptor with the ResourceConfig. This will attach it all resources. You only want to register the DynamicFeature and let it determine which resource to tie to.
I've been searching for hours to find a solution for my problem but I can't get it to work. I want to inject my Weld-managed service into a ConstraintValidator that is used to validate a User-Object that is posted to my JAX-RS Rest-Service. Everything is deployed to a glassfish 4.1 server.
I have a Service like this
#ApplicationScoped
public class UserService {
}
and I want to inject it into a ConstraintValidator like this
public class UniqueUserNameValidator implements ConstraintValidator<UniqueUserName, ApiUser> {
#Inject
private UserService service;
#Override
public void initialize(UniqueUserName constraintAnnotation) {
}
#Override
public boolean isValid(ApiUser value, ConstraintValidatorContext context) {
return service.getByUserName(value.getUserName()) == null;
}
}
the REST resource looks like this
#Path("users")
#Produces(MediaType.APPLICATION_JSON)
public class UserResource {
#Inject
UserService userService;
#POST
public Response createUser(#Valid ApiUser apiUser) {
ApiRepresentation created = userService.create(apiUser);
return Response.created(createURL(created)).build();
}
}
When I Post a json user object i get the following exception:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=UserService,parent=UniqueUserNameValidator,qualifiers={},position=-1,optional=false,self=false,unqualified=null,173822971)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:947)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:902)
at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:977)
at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:968)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:173)
I'm aware that jersey uses hk2 as DI provider and the ConstraintValidator is created using the InjectingConstraintValidatorFactory which in return uses the ResourceContext. Since HK2 doe know nothing about my WELD container managed beans it can not inject the proper service when creating the ConstraintValidator.
To fix this I am searching for
a) A way to provide JAX-RS (preferable a pure JAX-RS way without a dependency to jersey) with a custom ConstraintValidatorFactory to create the validator.
or
b) A way to force jersey to use WELD as the DI provider or tell hk2 to pickup all container managed beans WITHOUT manually adding every single bean to hk2.
I have no Idea how to use the bridge proposed here .
I appreciate any help.
Cheers
I also faced this issue with Jersey 2.25.x, Weld 2.4.x and Tomcat 8.x and haven't found a proper solution with #Inject.
As a workaround, I programmatically looked up for the bean instance using:
SomeSortOfBean bean = CDI.current().select(SomeSortOfBean.class).get();
Do you have the possibility to change the underlying JAX-RS implementation for your project?
When I had the same problem, I just switched from Jersey to RestEasy (fully certified JAX-RS implementation). http://resteasy.jboss.org/
Changing the implementation was easy enough: Just include the dependy through your favorite build automation tool (I use gradle):
compile 'org.jboss.resteasy:resteasy-servlet-initializer:3.0.11.Final'
Additionally, to make CDI work, include the resteasy-cdi JAX-RS CDI bridge:
compile 'org.jboss.resteasy:resteasy-cdi:3.0.11.
Lastly if you want the same JSON format, include the resteasy-jackson-provider:
compile 'org.jboss.resteasy:resteasy-jackson-provider:3.0.11.Final'
In the end, switching to resteasy gave me a lot less headache than trying to implement a Jersey fix.
I have an ExceptionMapper defined as following
#Provider
public class MyExceptionMapper implements ExceptionMapper<Throwable> {
#Inject
private Manager myManager;
#Override
public Response toResponse(Throwable exception) {
// My implementation
}
}
Deploying this code on glassfish 4 results with exception:
org.glassfish.hk2.api.UnsatisfiedDependencyException:
There was no object available for injection at
Injectee(requiredType=Manager,parent=MyExceptionMapper,qualifiers {}),position=-1,optional=false,self=false,unqualified=null,955636053)
When I use #Context instead of #Inject I do not see the exception but myManager is null
I tried making MyManager as #ManagedBean, #Singleton or an EJB (Stateless, Singleton) and non works
In JEE6 (with glassfish 3) you have to add
#javax.annotation.ManagedBean
to the provider implementation. Possibly this works also for Glassfish 4
As far as I know, this issues comes from the following. CDI is not in place to manage the dependencies of restful services and providers by default. But when adding #ManagedBean you enable CDI to create the instance.
Here is an example where I introduced CDI to a restful service using jersey.
You can follow the updates regarding this issue here https://java.net/jira/browse/JERSEY-2393
You could use OmniFaces Beans to get a CDI managed bean instance in your ExceptionMapper:
Beans.getInstance(Bean.class)
I'm using this with #javax.ejb.Stateless beans containing #javax.persistence.PersistenceContext.
I'm writing custom JAX-RS 2.0 application (under Jersey 2.3.1) which holds some data for use by all the resources.
public class WebApp extends org.glassfish.jersey.server.ResourceConfig {
public WebApp() {
packages("my.resources.package");
}
}
(I could use API's javax.ws.rs.core.Application as well, the described result is the same)
Then I inject the object into a resource
#Path("test")
public class Test {
#Context
Application app;
#GET
#Path("test")
public String test() {
return "Application class: " + app.getClass();
}
}
However, the result of a call is
Application class: class org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig
which makes me use some ugly tricks like
if (app instanceof WebApp) {
return (WebApp) app;
} else if (app instanceof ResourceConfig) {
return (WebApp) ((ResourceConfig) app).getApplication();
}
My understanding of JAX-RS 2.0 spec section 9.2.1:
The instance of the application-supplied Application subclass can be injected into a class field or method parameter using the #Context annotation. Access to the Application subclass instance allows configuration information to be centralized in that class. Note that this cannot be injected into the Application subclass itself since this would create a circular dependency.
is that application-supplied Application subclass is mine WebApp, not JAX-RS implementation-specific wrapper.
Also, changing this fragment
#Context
Application app;
to this
#Context
WebApp app;
causes app to be null, due to ClassCastException during context injection, so the declared type doesn't matter.
Is it a bug in Jersey or my misunderstanding?
UPDATE: I checked the behaviour under RESTEasy 3.0. The injected object is my WebApp, without any wrappers. I'd call it a bug in Jersey.
This doesn't seem like a bug. According to JAX-RS 2.0 spec you can inject Application into your resource classes (for example) but it does not say anything about directly injecting custom extensions of the Application. Not sure what your use-case is but you can register custom HK2 binder that will allow you to inject directly WebApp into resources:
public class WebApp extends org.glassfish.jersey.server.ResourceConfig {
public WebApp() {
packages("my.resources.package");
register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
#Override
protected void configure() {
bind(WebApp.this);
}
});
}
}
I too have encountered this using Jersey 2.4.1.
FWIW: I agree it seems like a bug according to the spec para 8.2.1. The statement "The instance of the application-supplied Application subclass" seems perfectly clear.
I have an alternative workaround that doesn't involve glassfish.hk2 but still concentrates the Jersey-specific code in the Application-derived class.
public class MyApp extends ResourceConfig {
...
static MyApp getInstance( Application application) {
try {
// for a conformant implementation
return (MyApp) application;
} catch (ClassCastException e) {
// Jersey 2.4.1 workaround
ResourceConfig rc = (ResourceConfig) application;
return (MyApp) rc.getApplication();
}
}
...
}
public class MyResource {
...
#Context Application application;
...
SomeMethod() {
... MyApp.getInstance( application);
}
}
Hope this is useful.
This appears to be fixed in a later version og Jersey. The same approach works for me with Jersey 2.16 at least. My injected Application object is of the correct subclass without any wrapping whatsoever.
Edit: Or maybe the version is irrelevant after all. Please see the comments to this answer.
I am using Jersey 1.17 and Jersey-Spring 1.8.
I am trying to get a jersey ResourceFilter to install correctly. I want this filter to be application-wide.
I have defined the ResourceFilter implementation as a bean in spring (defined in XML) and annotated it with the #Provider annotation.
#Provider
public class ContainerResourceFilterTest implements ResourceFilter
{
public ContainerRequestFilter getRequestFilter()
{
return null; //TODO
}
public ContainerResponseFilter getResponseFilter()
{
return null; //TODO
}
}
But it doesn't get hit when I send a request in.
If I use the #ResourceFilters annotation on a particular resource then it works as expected, but I don't want to do that on every single class.
How do I register a filter that is application wide?
Answering my own questions.
Doing some further reading of the jersey source code I have found that the #Provider annotation doesn't do anything for ResourceFilterFactory or ContainerRequest/Response. These can only be registered in one of the following 2 ways
Using the META-INF/services
Using the init params of the servlet, for example in web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.my.path.ResourceFilterFactory</param-value>
</init-param>
Not sure why the code doesn't inspect the registered spring beans for #Providers that implement the appropriate interfaces.
Worth noting that registering a class that implements ResourceFilter does not work for either of the above methods. You must do it for ResourceFilterFactory and have that return ResourceFilter implementations.
The good news is once you have made jersey aware of the classes that need to be registered it calls into spring to provide those classes so auto-wiring etc works as per usual.
Jersey doesn't scan the classpath for everything - ResourceFilters are one of the things it won't find automagickally. If you're using a ResourceConfig class, register your ResourceFilter class:
#ApplicationPath("/myapp")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages(getClass().getPackage().toString());
register(ContainerResourceFilterTest.class);
}
}
Jersey will then scan that class for the filter interface, create an instance (all filters are singleton scope according to their docs) and insert it into the Jersey filter chain.