I am able to access HttpServletRequest by using #Context annotation in my rest service. But unable to access the same in repository class.I do not want to pass the request form MyService to MyRespository while calling methods.
#Path("/someUrl")
public MyService{
#Context
private HttpServletRequest request;
#Get
public void someMethod()
{
myRepository.someMethod();
}
}
But same annotation not working for my Repository class
#Repository
public MyRepository
{
#Context
private HttpServletRequest request;
public void someMethod()
{
//need request here
}
}
it injection null request. Not sure why this is not working.
The problem is the way Jersey (and its DI framework HK2) is integrated, is that Spring components can be injected into Jersey (HK2) components, but not vice versa. HttpServletRequest is bound as a Jersey component.
What you can do is create an HK2 service, that wraps the Spring repo, and the HttpServletRequest. IMO, it is better design anyway. A repository shouldn't be concerned with the HttpServletRequest, it is only concerned with data.
So you can have
public class MyService {
#Inject // or #Autowired (both work)
private MyRepository repository;
#Context
private HttpServletRequest request;
}
Then bind the service with HK2
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.process.internal.RequestScoped;
public class AppBinder extends AbstractBinder {
#Override
public void configure() {
bindAsContract(MyService.class).in(RequestScoped.class);
// note, if `MyService` is an interface, and you have
// an implementation, you should use the syntax
//
// bind(MyServiceImpl.class).to(MyService.class).in(...);
//
// Then you inject `MyService`. Whatever the `to(..)` is,
// that is what you can inject
}
}
And register the binder with Jersey
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(new AppBinder());
}
}
Then you can inject MyService into your resource class.
If you don't want to go this route, then you need to make MyRepository an HK2 service, or use a an HK2 Factory to wrap the repository, and explicitly inject it. Something like
import javax.inject.Inject;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.springframework.context.ApplicationContext;
public class MyRepositoryFactory implements Factory<MyRepository> {
private final MyRepository repo;
#Inject
public MyRepositoryFactory(ApplicationContext ctx, ServiceLocator locator) {
MyRepository r = ctx.getBean(MyRepository.class);
locator.inject(r);
this.repo = r;
}
#Override
public MyRepository provide() {
return repo;
}
#Override
public void dispose(MyRepository t) {/* noop */}
}
Then register the factory
#Override
public void configure() {
bindFactory(MyRepositoryFactory.class).to(MyRepository.class).in(Singleton.class);
}
If you do the above, then you just use the MyRepository, instead of adding the service layer. Basically you need to get the repo from Spring, and explicitly inject it with the HK2 ServiceLocator (which is the HK2 analogue of the Spring ApplicationContext).
Related
I'm having an issue while testing Rest request.
However test is being intercepted by one of my interceptors.
In short I want to mock interceptors behaviour rather than calling actual implementation.
For simplicity purposes to visualise the issue here is a simply Get request
#Controller
public class HiController {
#ResponseBody
#GetMapping(value = "/hi", produces = {"application/json"})
public String hi() {
return "hi";
}
}
with simple interceptor and config file
#Component
public class LoggingInterceptor implements HandlerInterceptor {
#Override
public final boolean preHandle(HttpServletRequest request, final HttpServletResponse response, final Object handler) {
System.out.println("Logging token interceptor");
return true;
}
}
#Component
public class InterceptorAppConfig implements WebMvcConfigurer {
#Autowired
private LoggingInterceptor loggingTokenInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingTokenInterceptor);
}
}
the purpose of test was to call rest endpoint and to mock interceptor
#RunWith(SpringRunner.class)
#WebMvcTest(HiController.class)
public class HiControllerTest extends MvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean
RestTemplate restTemplate;
#Mock
private HiController controller;
private static final String AUTHENTICATE_USER = "/hi";
#Test
public void mandatoryFieldMissing() throws Exception {
this.mockMvc.perform(get(AUTHENTICATE_USER))
.andExpect(content().string(containsString("hi")))
.andExpect(status().isOk());
}
}
#RunWith(SpringRunner.class)
public abstract class MvcTest {
#MockBean
LoggingInterceptor loggingTokenInterceptor;
}
When I run this test ^ LoggingInterceptor is still being called.
How can I configure tests to have mock interceptors?
You are using #MockBean. From the documentation,
If a bean, compatible with the declared class exists in the context, it replaces it by the mock. If it is not the case, it adds the mock in the context as a bean.
The mocked bean does not have behavior. Realize that by mocking the bean, the onus is on you to define the behavior.
You can either disable mocking the bean(to allow for default behavior)
Mockito.doCallRealMethod().when(loggingTokenInterceptor).preHandle(Mockito.any(), Mockito.any(), Mockito.any());
Or you can define the behavior for the mocked bean yourself.
I'm writing a RESTful web service where I'm trying to inject a request scoped service class into a filter.
I have gone through Paul Samsotha's blog on how to inject request scoped services using proxies.
Here's my implementation:
The supplier for this:
public class FileServiceSupplier implements Supplier<FileService> {
#Inject
ContainerRequestContext context;
public FileService get() {
String something = context.get("something");
new FileService(something);
}
}
and I'm binding the supplier here
import org.glassfish.jersey.internal.inject.AbstractBinder;
...
public class CustomDependencyBinder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(FileServiceSupplier.class)
.proxy(true)
.proxyForSameScope(false)
.to(FileService.class)
.in(RequestScoped.class);
}
}
public class MyWebService extends ResourceConfig {
public MyWebService() {
register(new CustomDependencyBinder());
}
}
But now when I'm injecting this into a filter:
public class FileScanningFilter implements ContainerRequestFilter {
#Inject
private FileService fileService;
#Inject
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
boolean status = fileService.assertStatus();
}
}
#Provider
public class FileScanningFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext featureContext) {
featureContext.register(FileScanningFilter.class);
}
}
But now when I send a request I get the following error:
"There is more than one active context for org.glassfish.jersey.process.internal.RequestScoped" "java.lang.IllegalStateException: There is more than one active context for org.glassfish.jersey.process.internal.RequestScoped
at org.jvnet.hk2.internal.ServiceLocatorImpl._resolveContext(ServiceLocatorImpl.java:2193)
at org.jvnet.hk2.internal.ServiceLocatorImpl.access$000(ServiceLocatorImpl.java:105)
at org.jvnet.hk2.internal.ServiceLocatorImpl$3.compute(ServiceLocatorImpl.java:165)
at org.jvnet.hk2.internal.ServiceLocatorImpl$3.compute(ServiceLocatorImpl.java:161)
at org.glassfish.hk2.utilities.cache.Cache$OriginThreadAwareFuture$1.call(Cache.java:74)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.glassfish.hk2.utilities.cache.Cache$OriginThreadAwareFuture.run(Cache.java:131)
at org.glassfish.hk2.utilities.cache.Cache.compute(Cache.java:176)
at org.jvnet.hk2.internal.ServiceLocatorImpl.resolveContext(ServiceLocatorImpl.java:2207)
at org.jvnet.hk2.internal.MethodInterceptorImpl.internalInvoke(MethodInterceptorImpl.java:64)
at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:101)
at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:39)
at com.sun.proxy.$Proxy88.getResourceMethod(Unknown Source)
at com.test.FileScanningFilter.filter(AuthFilter.java:56)
Even though I inserted it once in the filter, it is saying there are multiple contexts.
I'm using Jersey 2.28
P.S: For the AbstractBinder , I followed this answer
This is my jersey config class
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeService.class);
}
}
Here autowiring of employeeService is giving a null pointer exception
#Path("/ems")
#Component
public class EmployeeRestController {
#Autowired
private EmployeeService employeeService;
#GET
#Path("/employees")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public List<Employee> getEmployees() {
return employeeService.getEmployees();
}
}
I have tried everything
In my employeeServiceImpl I have #service annotation
Still, it is not working.
To configure the dependency injection using the built in DI framework (HK2), you should use an AbstractBinder, as mentioned in some answers in Dependency injection with Jersey 2.0.
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("com.ems");
register(new AbstractBinder() {
#Override
protected void configure() {
bind(EmployeeService.class)
.to(EmployeeService.class)
.in(Singleton.class);
}
});
}
}
Secondly, you do not use the #Autowired annotation. This annotation is specifically for Spring. For standard injection with Jersey, just use the #Inject annotation. Also remove the #Component annotation, as this is also for Spring.
As an aside, if you do want to integrate Spring with Jersey, you should read Why and How to Use Spring With Jersey. It will break down what you need to understand about integrating the two frameworks.
You should register Controller not Service class.
Sample
#ApplicationPath("services")
public class JerseyApplication extends ResourceConfig{
public JerseyApplication() {
packages("com.ems");
register(EmployeeRestController.class);
}
}
How can/should I pass an object from a ContainerRequestFilter to a (post-matching) resource in (JAX-RS) Resteasy version 3.0.11 that has undertow embedded and uses Guice?
The method ContainerRequestContext#setProperty stores values which are synced with the HttpServletRequest. So with plain JAX-RS you can store an attribute like this:
#Provider
public class SomeFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setProperty("someProperty", "someValue");
}
}
And afterwards you can obtain it in your resource class:
#GET
public Response someMethod(#Context org.jboss.resteasy.spi.HttpRequest request) {
return Response.ok(request.getAttribute("someProperty")).build();
}
With CDI you also can inject any bean in the filter and resource class:
#Provider
public class SomeFilter implements ContainerRequestFilter {
#Inject
private SomeBean someBean;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
someBean.setFoo("bar");
}
}
In your resource class:
#Inject
private SomeBean someBean;
#GET
public Response someMethod() {
return Response.ok(someBean.getFoo()).build();
}
I'd expect the same to be working with Guice.
Update: As #bakil pointed out correctly you should use a #RequestScoped bean if the object you want to pass should only be associated with the current request.
I have this Restful service:
#Service
public class MyRESTServiceImpl implements MyRESTService {
#Inject
private Helper helper;
public MyRESTServiceImpl() {
}
#Override
public List<Something> getThings() {
return helper.getThings(); // NPE here!
}
}
The problem here is that when getThings is accessed through /api/getThings, although the method is invoked the helper is not injected. Whereas on the other parts of the app it injected properly. I am missing some annotation for this MyRESTServiceImpl?