How can we use instance variables in RestEasy? - java

I am trying to learn Restful Web Service with RestEasy. I created a Java class and tried to use a instance variable but default scope is Request Scope and after an event, the value of variable is cleaned. How can we use a instance variable in different methods in RestEasy class.
My code is below:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package restService;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
/**
*
* #author ilkaygunel
*/
#Path("/service")
public class Service {
Map<Integer, String> employeeMap = new HashMap<>();
#GET
#Path("/show")
#Produces("application/json")
public Map show()
{
return employeeMap;
}
#POST
#Path("/addRecord")
#Consumes("application/json")
#Produces("application/json")
public Map addRecord(Employee employee) {
employeeMap.put(employee.getEmployeeId(), employee.getEmployeeName()+" "+employee.getEmployeeSurname());
System.out.println("#POST ile işaretlenmiş metoda gelen veri:");
System.out.println(employee.getEmployeeId());
System.out.println(employee.getEmployeeName());
System.out.println(employee.getEmployeeSurname());
System.out.println("---HashMap İçindeki Veri (POST Metodundan)---");
for (Map.Entry<Integer, String> entry : employeeMap.entrySet()) {
System.out.println(entry.getKey() +" " +entry.getValue());
}
return employeeMap;
}
#PUT
#Path("/updateRecord")
#Consumes("application/json")
public void updateRecord(Employee employee) {
System.out.println("#PUT ile işaretlenmiş metoda gelen veri:");
System.out.println(employee.getEmployeeId());
System.out.println(employee.getEmployeeName());
System.out.println(employee.getEmployeeSurname());
System.out.println("HashMap'in Boyutu:"+employeeMap.size());
for(Integer key : employeeMap.keySet()) {
if(key ==employee.getEmployeeId())
{
System.out.println("Buraya Bak:"+employeeMap.get(key));
employeeMap.put(key, employee.getEmployeeName()+" "+employee.getEmployeeSurname());
}
break;
}
System.out.println("---HashMap İçindeki Veri (PUT Metodundan)---");
for (Map.Entry<Integer, String> entry : employeeMap.entrySet()) {
System.out.println(entry.getKey() +" " +entry.getValue());
}
}
#DELETE
public void deleteRecord() {
}
}

The best approach is to inject some singleton service class e.g. EmploeeManager which contains map of employees. (I don't know what technology are you using spring/cdi)
On the other hand you can use
private static Map<Integer, String> employeeMap = new HashMap<>();
and think also about synchronization since it can be accessed by multiple thread at the same time.

Have a look at what the JAX-RS 2.0 specification defines regarding the lifecycle of the resource classes:
3.1.1 Lifecycle and Environment
By default a new resource class instance is created for each request
to that resource. First the constructor is called, then any requested
dependencies are injected, then the appropriate method is invoked and
finally the object is made available for garbage collection. [...]
The behavior you described in the question is expected: a new instance of the resource class is getting created for each request.
To store data, you can use the following approaches:
Use a static field, that is, a field that is shared by all instances of a class;
Use dependency injection frameworks such as CDI or Spring and store your data into application scoped bean or singleton. This bean can be injected into your resource class.
Use a database.

Related

#AroundInvoke interceptor is called twice on a #WebService class

Summary
#AroundInvoke interceptor is called twice on a #WebService class,
if the intercepted method is called from outside of the application via endpoint as a SOAP web service.
If the very same method is called internally from another bean, it's called only once (as I would expect).
The intercepted method itself is always called only once!
Question 1: Can I make the interceptor to be called only once?
Question 2: If I cannot, is there a transferable (server independent) way to decide in which interceptor I am, so I can ignore the redundant one?
Question 3: Is this behaviour common (and defined and described in some documentation),
or is it dependent on my specific environment (JBoss EAP 6.4.0)?
Observation:
The two calls are not in the same interceptor chain.
It is not the same instance of the interceptor class.
The implementation class of the InvocationContext is different for both the calls.
It's funny that one of the contextData, the InvocationContext's field for passing data along the interceptor chain, is not an instance of the HashMap, but WrappedMessageContext, but it does not wrap the other contextData anyway.
Minimal reproducible code
(I removed the package name.)
MyEndpoint interface
import javax.jws.WebService;
#WebService
public interface MyEndpoint {
public static final String SERVICE_NAME = "MyEndpointService";
public String getHello();
}
MyEndpointImpl class
import javax.interceptor.Interceptors;
import javax.jws.WebService;
#WebService(endpointInterface = "MyEndpoint", serviceName = MyEndpoint.SERVICE_NAME)
#Interceptors({TestInterceptor.class})
public class MyEndpointImpl implements MyEndpoint {
#Override
public String getHello() {
System.out.println("MyEndpointImpl.getHello() called");
return "Hello";
}
}
TestInterceptor class
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
public class TestInterceptor {
#AroundInvoke
private Object countCalls(InvocationContext ic) throws Exception {
System.out.println("Interceptor called");
return ic.proceed();
}
}
Output
Interceptor called
Interceptor called
MyEndpointImpl.getHello() called
More details
To get more runtime information, I added more logging.
MyEndpointImpl class
import java.lang.reflect.Method;
import java.util.Map;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestInterceptor {
private static Logger logger = LoggerFactory.getLogger(TestInterceptor.class);
private static int callCnt = 0;
#AroundInvoke
private Object countCalls(InvocationContext ic) throws Exception {
final String interceptorClass = this.toString();
final String invocationContextClass = ic.getClass().getName();
final Method method = ic.getMethod();
final String calledClass = method.getDeclaringClass().getName();
final String calledName = method.getName();
final String message = String.format(
"%n INTERCEPTOR: %s%n InvocationContext: %s%n %s # %s()",
interceptorClass, invocationContextClass, calledClass, calledName);
logger.info(message);
final int call = ++callCnt;
final Map<String, Object> contextData = ic.getContextData();
contextData.put("whoami", call);
logger.info("BEFORE PROCEED {}, {}", call, contextData);
final Object ret = ic.proceed();
logger.info("AFTER PROCEED {}, {}", call, contextData);
return ret;
}
}
Output
INTERCEPTOR: TestInterceptor#74c90b72
InvocationContext: org.jboss.invocation.InterceptorContext$Invocation
MyEndpointImpl # getHello()
BEFORE PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext#2cfccb1d
INTERCEPTOR: TestInterceptor#5226f6d8
InvocationContext: org.jboss.weld.interceptor.proxy.InterceptorInvocationContext
MyEndpointImpl # getHello()
BEFORE PROCEED 2, {whoami=2}
MyEndpointImpl.getHello() called
AFTER PROCEED 2, {whoami=2}
AFTER PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext#2cfccb1d
I cannot answer your questions directly, but maybe some clarification about the contexts may help you.
The Java EE JAX-WS implementation varies from server to server. For Example Glassfish uses Metro and JBoss uses Apache CXF.
There are different kind of interceptors chains which allow to control programmatically the conditions before and after the request/response processing.
The interceptors for the SOAP web service calls are SOAP handlers and logical handlers (See Oracle documentation). Both can access SOAP message on different levels (the whole or only the payload).
My assumption is that your the interceptor called twice, once for accessing through HTTP/SOAP, and once for access over RMI.
In the first interceptor invocation, what you see as context is org.apache.cxf.jaxws.context.WrappedMessageContext which is a Map implementation. See WarppedMessageContext, Apache CXF web service context. It is invoked for HTTP/SOAP access.
The second invocation is what you expect when using the RMI (probably triggered from Apache CXF, after the SOAP message is processed).
To avoid this you can use third class for logic implementation with interceptor defined. The existing web service implementation class will only delegate to it and will not contain interceptor annotation anymore.
Example code can be seen here: OSCM Project
I had the exact same problem and found a solution.
If you instead of using the #Interceptors style binding use a #InterceptorBinding style binding, then the interceptor is only instantiated and invoked once (at least in my case on WildFly 10.1.0.Final).
This is what your example would look like using #InterceptorBinding style.
Your custom interceptor binding annotation:
import javax.interceptor.InterceptorBinding;
...
#Inherited
#InterceptorBinding
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface MyInterceptorBinding {
Your endpoint:
#WebService(endpointInterface = "MyEndpoint", serviceName =
MyEndpoint.SERVICE_NAME)
#MyInterceptorBinding
public class MyEndpointImpl implements MyEndpoint {
Your interceptor:
import javax.interceptor.Interceptor;
import javax.annotation.Priority;
...
#Interceptor
#MyInterceptorBinding
#Priority(Interceptor.Priority.APPLICATION) //we use #Priority to enable this interceptor application-wide, without having to use beans.xml in every module.
public class TestInterceptor {
#AroundInvoke
private Object countCalls(InvocationContext ic) throws Exception {
System.out.println("Interceptor called");
return ic.proceed();
}
I never figured out exactly what the problem is, but I suspect that the #Interceptors style binding is valid for multiple types of interceptors (EJB and CDI) while the #InterceptorBinding style is maybe only valid for CDI interceptors.
Maybe a JAX-WS #WebService is both an EJB and a CDI bean?

Spring 4 Request driven Bean creation

I am implementing a Rest WS using Spring 4 (Spring Boot).
The basic idea is I want to consume a JSON payload specifying an identifier (e.g. social security number or something) and run multiple subServices on that identifier.
Here is a sample payload:
{
"ssNumber" : "1111111111111111",
"subServicesDetails" :
[
{ "subServiceName" : "Foo" , "requestParameters" : {} },
{ "subServiceName" : "Dummy", "requestParameters" : {} }
]
}
In my code I have multiple "sub-services" (FooService, DummyService) implementing the SubService interface:
package com.johnarnold.myws.service;
import com.johnarnold.myws.model.SubServiceDetails;
public interface SubService {
public boolean service(String ssNumber, SubServiceDetails ssd);
}
And below is the FooService code.
package com.johnarnold.myws.service;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.johnarnold.myws.dao.FooDao;
import com.johnarnold.myws.model.Foo;
import com.johnarnold.myws.model.SubServiceDetails;
#Component
public class FooService implements SubService{
private static Logger log = Logger.getLogger(FooService.class);
#Autowired
private FooDao dao;
public FooService()
{
log.debug("FooService ctor");
}
public boolean service(String ssNumber, SubServiceDetails ssd)
{
log.debug("FooService service");
Map <String, String> responseParameters = new HashMap<String, String>();
try
{
Foo foo = dao.getFoo(ssNumber);
if(foo.isCompromised())
{
responseParameters.put("listed", "true");
}
else
{
responseParameters.put("listed", "false");
}
ssd.setResponseParameters(responseParameters);
return true;
}
catch(Throwable ex)
{
log.error("Exception in service ", ex);
}
return false;
}
}
Now I wrote my own factory to create the subservices but when I did that of course because I am explictly creating my beans (e.g. FooService) below - my container is not auomatically injecting any of the #Autowired members - FooDao for example:
package com.johnarnold.myws.service;
public class SubServiceFactory {
/*
* Instantiates a SubService for the supplied subServiceName or throws an exception if
* no valid SubService exists
*/
public static SubService createSubService(String subServiceNameStr)
{
SubService subService = null;
System.out.println("subServiceName [" + subServiceNameStr + "]");
if(subServiceNameStr.equals("Foo"))
{
subService = new FooService();
}
if(subServiceNameStr.equals("Dummy"))
{
subService = new DummyService();
}
else
{
System.out.println("subServiceName [" + subServiceNameStr + "] is not defined");
}
return subService;
}
}
For completeness here is the Controller:
package com.johnarnold.myws.controller;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.apache.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.johnarnold.myws.model.RawsPayload;
import com.johnarnold.myws.model.SubServiceDetails;
import com.johnarnold.myws.service.SubService;
import com.johnarnold.myws.service.SubServiceFactory;
import com.johnarnold.myws.web.ValidMessage;
#RestController
#RequestMapping("/raws/")
public class RawsController {
private static final Logger logger = Logger.getLogger(RawsController.class);
//#Autowired
//SubService [] subSvcs;
#RequestMapping(value="/{version}/status", method=RequestMethod.GET)
public ResponseEntity<ValidMessage> getServiceStatus()
{
return new ResponseEntity<>(new ValidMessage() , HttpStatus.OK);
}
/*
* Main entry point - orchestrates all of the WS Sub Services
*/
#RequestMapping(value="/{version}/raws", method=RequestMethod.PUT)
public ResponseEntity<String> raws(#Valid #RequestBody RawsPayload rawsPayload,
HttpServletRequest request)
{
logger.info("Request received");
System.out.println("payl " + rawsPayload);
System.out.println("ssNumber=" + rawsPayload.getSsNumber());
System.out.println("sub svcs details=" + rawsPayload.getSubServicesDetails().length);
SubServiceDetails[] subServiceDetails = rawsPayload.getSubServicesDetails();
for(SubServiceDetails ssd : subServiceDetails)
{
String subServiceNameStr = ssd.getSubServiceName();
System.out.println("svcname=" + subServiceNameStr);
System.out.println("svc req params=" + ssd.getRequestParameters());
System.out.println("svc resp params=" + ssd.getResponseParameters());
SubService subService = SubServiceFactory.createSubService(subServiceNameStr);
// Probably wrap the below with some timings
subService.service(rawsPayload.getSsNumber(), ssd);
}
//System.out.println("svcs are " + subSvcs + "size=" + subSvcs.length);
return new ResponseEntity<>("foo" , HttpStatus.OK);
}
}
And here is the main payload class:
package com.johnarnold.myws.model;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.apache.log4j.Logger;
import org.hibernate.validator.constraints.Length;
public class RawsPayload {
static Logger log = Logger.getLogger(RawsPayload.class);
#NotNull
#Length(min=16, max=19)
private String ssNumber;
#Valid
#NotNull
#Size(min=1, max=3)
private SubServiceDetails [] subServicesDetails;
public String getSsNumber() {
return ssNumber;
}
public void setSsNumber(String ssNumber) {
log.info("setSsNumber()");
this.ssNumber = ssNumber;
}
public SubServiceDetails[] getSubServicesDetails() {
return subServicesDetails;
}
public void setSubServicesDetails(SubServiceDetails[] subServicesDetails) {
this.subServicesDetails = subServicesDetails;
}
}
I've read a number of answers on StackOverflow regarding Spring 4 Conditional Beans - but this functionality appears to be targeted at Context / Configuration type information rather than Request message content (as in this case).
Can anyone point me in the right direction. I can provide further context if necessary
KRgds
John
Two possible ways of solving this problem:
Add all your subService beans to the Spring context then select from them using a ServiceLocatorFactoryBean. This is the nicer approach (from architectural point of view), but it might require a bit more time to implement if you have never used this concept before.
There is a simpler alternative below if you want to stick with basic Spring solutions:
Have the subservice beans injected into your main service as a list, and then select from that. It would look something like this:
#Bean
public List<SubService> subServices(){
List<SubService> list = new SubService<>();
list.add(new AService());
list.add(new BService());
return list;
}
THEN
public SubService selectServiceByName() {
//iterate through the list, pick the service with the right name and return - this solution will require you to bind by beannames
}
#john-arnold First, crate all the services like this, or annotate them with #Service/#Component with explicit names like below: names are start with the values of subServiceName param and contains a common suffix, "Service" here, thats important.
#Bean("FooService")
public SubService fooService() {
return new FooService();
}
#Bean("DummyService")
public SubService dummyService() {
return new DummyService();
}
Then change your factory like this:
#Component
public class SubServiceFactory implements BeanFactoryAware{
private BeanFactory beanFactory;
private static final String MY_SERVICE_SUFFIX = "Service";
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public <T> T getServiceImplementation(String name, Class<T> requiredType) {
return beanFactory.getBean(name + MY_SERVICE_SUFFIX, requiredType);
}
}
Now what we have here is a BeanFactoryAware class that you can inject to your Rest Endpoint and instead of if statement, try this:
subServiceFactory.getServiceImplementation(subServiceNameStr, SubService.class);
This will return your bean or an exception if it doesn't find one. If you don't want an exception, you can catch it and return null or you can create a Service imp. just for these and return that instance. Your choice.
Edit:
As a shortcut, you can define your imp. Beans and than add this to your rest endpoint
#Autowired
private Map<String, SubService> mySubServices;
Spring will automatically inject all your imp. ref. so you can just use get() method of the map. But i prefer the first one..
You don't need anything fancy here. Just implement all your services that implement your service interface, annotate them all with either #Component or #Service and scan them as usual.
Then, wherever you have to choose a concrete service implementation, autowire all implementations of your service like this:
#Autowired
Map<String, SubService> subServices;
The key of the map will be the name of the service as specified in the #Component annotation of every sub service implementation, and the value will be the instance.
So, when you receive you JSON, just get the name of the sub service (i.e. Foo), and get the specific service of the map:
SubService fooSubService = subServices.get(subServiceName + "Service");
where subServiceName is the uncapitalized name of the sub service you're receiving in your JSON (i.e. if you're receiving Foo this would be foo).
The convention is to use the uncapitalized name of the class that implements the interface as the bean name, i.e. for the FooService class the bean name will be fooService, and this is the key you have to look for in the map.

Dropwizard + Jersey : "Not inside a request scope" when creating custom annotation

I have a simple Dropwizard 0.8.1 REST service that pulls in Jersey 2.17. Upstream of the REST/Jetty service I have some authentication service that adds some nice authorization information to the HTTP Header that gets passed to my Dropwizard app.
I would love to be able to create a custom annotation in my Resource that hides all the messy header-parsing-to-POJO garbage. Something like this:
#Path("/v1/task")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {
#UserContext // <-- custom/magic annotation
private UserContextData userContextData; // <-- holds all authorization info
#GET
public Collection<Task> fetch() {
// use the userContextData to differentiate what data to return
}
I've spent the last day looking around stackoverflow and found several other people who had the same issue and appeared (?) to get some satisfaction, but I can't seem to avoid getting a "Not inside a request scope" stack trace when I try to do this.
So I stashed all my changes and tried to implement the example provided in sections 22.1 and 22.2 by the Jersey documentation directly: https://jersey.java.net/documentation/2.17/ioc.html
Following along with their example (but in my Dropwizard app), I'm trying to get a "#SessionInject" annotation in my Resource, but it also blows up with "Not inside a request scope" stack trace each time. What am I doing wrong here?
Resource:
#Path("/v1/task")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {
private final TaskDAO taskDAO;
#Context
private HttpServletRequest httpRequest;
#SessionInject
private HttpSession httpSession;
public TaskResource(TaskDAO taskDAO) {
this.taskDAO = taskDAO;
}
#GET
public Collection<Task> fetch(#SessionInject HttpSession httpSession) {
if (httpSession != null) {
logger.info("TOM TOM TOM httpSession isn't null: {}", httpSession);
}
else {
logger.error("TOM TOM TOM httpSession is null");
}
return taskDAO.findAllTasks();
}
The SessionInjectResolver:
package com.foo.admiral.integration.jersey;
import com.foo.admiral.integration.core.SessionInject;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionInjectResolver implements InjectionResolver<SessionInject> {
private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
#Inject
#Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
InjectionResolver<Inject> systemInjectionResolver;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (HttpSession.class == injectee.getRequiredType()) {
return systemInjectionResolver.resolve(injectee, handle);
}
return null;
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}
The HttpSessionFactory:
package com.foo.admiral.integration.jersey;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Singleton
public class HttpSessionFactory implements Factory<HttpSession> {
private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
private final HttpServletRequest request;
#Inject
public HttpSessionFactory(HttpServletRequest request) {
logger.info("Creating new HttpSessionFactory with request");
this.request = request;
}
#Override
public HttpSession provide() {
logger.info("Providing a new session if one does not exist");
return request.getSession(true);
}
#Override
public void dispose(HttpSession t) {
}
}
The annotation:
package com.foo.admiral.integration.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface SessionInject {
}
And, finally, the binding in the Dropwizard Application class:
#Override
public void run(TodoConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HttpSessionFactory.class).to(HttpSession.class);
bind(SessionInjectResolver.class)
.to(new TypeLiteral<InjectionResolver<SessionInject>>() { })
.in(Singleton.class);
}
});
Ye old stack trace:
Caused by: java.lang.IllegalStateException: Not inside a request scope.
at jersey.repackaged.com.google.common.base.Preconditions.checkState(Preconditions.java:149)
at org.glassfish.jersey.process.internal.RequestScope.current(RequestScope.java:233)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:74)
at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:62)
at com.sun.proxy.$Proxy72.getSession(Unknown Source)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:29)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:14)
Some clues that may be useful:
1) I'm noticing is that the logging statements in my HttpSessionFactory are never getting fired, so I don't think the Factory is correctly identified to DropWizard.
2) If I change the annotation to be a Parameter instead of a Field and move the use of the annotation into the fetch( ) method signature like this, it doesn't throw the stack trace (but the httpSession is still null, presumably because the Factory isn't firing...)
public Collection<Task> fetch(#SessionInject HttpSession httpSession) {
3) It doesn't appear to matter if I "register" the binder with environment.jersey().register() or environment.jersey().getResourceConfig().register()... they appear to do the same thing.
Do you see any obvious problems? Thanks in advance!
This is weird behavior. But what looks like is going on is the following
You have registered TaskResource as an instance and not as a .class. This I'm pretty sure of (though you have not mentioned).
register(new TaskResource());
/* instead of */
register(TaskResource.class);
Doing the former, it set the resource in a singleton scope. The latter in a request scope (unless annotated otherwise - see below)
When the resource model is loading it sees the TaskResource is a singleton, and that the HttpServletRequest is in a request scope. Either that or that the factory is in a per request scope. I'm guessing one of the two.
I thought that it might actually be a scope issue, as mentioned in the error message, but what I'm pretty sure of is that at runtime, it will get handled with a thread local proxy, because of the lesser scope.
You can see it fixed by registering the TaskResource as a class, and then annotating the TaskResource with #Singleton. This is if you actually do want the resource class to be a singleton. If not, then just leave off the #Singleton.
The odd thing to me is that it the fact that it fails on startup when the resource is explicitly instantiated on startup, but works when the framework loads on the first request (which is what happens when you register it as a class). They are both still in a singleton scope.
One thing you might want to take into consideration is whether you actually want the resource to be a singleton or not. You do have to worry about thread safety issues with singletons, and there are are some other limitations. Personally, I prefer to keep them in a request scope. You would have to do some performance testing to see if there is much of an impact for your application.
UPDATE
For parameter injection you may want to take a look at this post
UPDATE 2
See Also
jersey 2 context injection based upon HttpRequest without singleton. My answer should shed some more light.

Spring-security-oauth OAuthProviderTokenServices custom implementation in OAuth 1.0

I'm using Spring-security-oauth to secure a RESTful application, and I'm triying to implement a custom OAuthProviderTokenServices class in order to store tokens in a database.
All I got from the docs is:
When creating your OAuthProviderTokenServices implementation, you may
want to consider extending the RandomValueProviderTokenServices which
creates tokens via random value and handles everything except for the
persistence of the tokens. There is also an in-memory implementation
of the OAuthProviderTokenServices that may be suitable [...]
which is fine, so I created a new custom class:
package experiments;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.security.oauth.provider.token.OAuthProviderTokenImpl;
import org.springframework.security.oauth.provider.token.RandomValueProviderTokenServices;
/**
* Implementation of TokenServices that stores tokens in a database.
*
* #author Seether
*/
public class DatabaseProviderTokenServices extends RandomValueProviderTokenServices {
protected final ConcurrentHashMap<String, OAuthProviderTokenImpl> tokenStore = new ConcurrentHashMap<String, OAuthProviderTokenImpl>();
protected OAuthProviderTokenImpl readToken(String token) {
return tokenStore.get(token);
}
protected void storeToken(String tokenValue, OAuthProviderTokenImpl token) {
tokenStore.put(tokenValue, token);
}
protected OAuthProviderTokenImpl removeToken(String tokenValue) {
return tokenStore.remove(tokenValue);
}
}
which for now, as you can see, is identical to the InMemoryProviderTokenServices class.
My application uses the AccessConfirmationController from sparkl example, which is this:
package experiments;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.oauth.provider.ConsumerDetails;
import org.springframework.security.oauth.provider.ConsumerDetailsService;
import org.springframework.security.oauth.provider.token.OAuthProviderToken;
import org.springframework.security.oauth.provider.token.OAuthProviderTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for retrieving the model for and displaying the confirmation page for access to a protected resource.
*
* #author Ryan Heaton
*/
#Controller
public class AccessConfirmationController {
private OAuthProviderTokenServices tokenServices;
private ConsumerDetailsService consumerDetailsService;
#RequestMapping("/oauth/confirm_access")
public ModelAndView getAccessConfirmation(HttpServletRequest request, HttpServletResponse response)
throws Exception {
String token = request.getParameter("oauth_token");
if (token == null) {
throw new IllegalArgumentException("A request token to authorize must be provided.");
}
OAuthProviderToken providerToken = tokenServices.getToken(token);
ConsumerDetails consumer = consumerDetailsService
.loadConsumerByConsumerKey(providerToken.getConsumerKey());
String callback = request.getParameter("oauth_callback");
TreeMap<String, Object> model = new TreeMap<String, Object>();
model.put("oauth_token", token);
if (callback != null) {
model.put("oauth_callback", callback);
}
model.put("consumer", consumer);
return new ModelAndView("access_confirmation", model);
}
public void setTokenServices(OAuthProviderTokenServices tokenServices) {
this.tokenServices = tokenServices;
}
public void setConsumerDetailsService(ConsumerDetailsService consumerDetailsService) {
this.consumerDetailsService = consumerDetailsService;
}
}
Now the question is: how do I tell my application to use my tokenServices implementation rather than the default one (which right now I belive is InMemoryProviderTokenServices)?
I tried messing around with the controller, but the fews attempt all led me to java.lang.IllegalStateExceptions.
I also noticed that there is a line in the config XML:
<oauth:token-services id="tokenServices"/>
Which might be a critical piece of the puzzle, as the related help reads:
Element for declaring and configuring an in-memory implementation of
the provider token service.
If I just remove it, I get:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'accessConfirmationController' defined in
ServletContext resource [/WEB-INF/mvc-dispatcher-servlet.xml]: Cannot
resolve reference to bean 'tokenServices' while setting bean property
'tokenServices'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'tokenServices' is defined
Funny as answers come out of nowhere sometimes, expecially after it's been some time since you've started looking for them and you've just posted here. Oh well.
According to a code snippet randomly found here, it looks like all I needed to do was to comment out that line from the XML configuration file:
<!-- <oauth:token-services id="tokenServices"/> -->
and replace it with this one:
<beans:bean id="tokenServices" class="experiments.DatabaseProviderTokenServices" />
where the class is of course my implementation.
By doing this, the DatabaseProviderTokenServices class is used in place of the default InMemoryProviderTokenServices class.

Why does Jersey say "Array of packages must not be null or empty" when I create my own PackagesResourceConfig?

I created my own PackagesResourceConfig that looks like this:
import com.sun.jersey.api.core.PackagesResourceConfig;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;
public class ResourceConfigClass extends PackagesResourceConfig {
#Override
public Map<String, MediaType> getMediaTypeMappings() {
Map<String, MediaType> map = new HashMap<String, MediaType>();
map.put("xml", MediaType.APPLICATION_XML_TYPE);
map.put("json", MediaType.APPLICATION_JSON_TYPE);
return map;
}
}
But now when I start my app, it gives me an error that says:
Array of packages must not be null or empty
That comes from this source code in Jersey:
/**
* Search for root resource classes declaring the packages as an
* array of package names.
*
* #param packages the array package names.
*/
public PackagesResourceConfig(String... packages) {
if (packages == null || packages.length == 0)
throw new IllegalArgumentException("Array of packages must not be null or empty");
init(packages.clone());
}
But I've already set the packages in my web.xml by setting the com.sun.jersey.config.property.packages param so it shouldn't be null.
This is actually a Java issue. Unlike normal constructors with parameters, if the constructor only has varargs, it's valid to pass in nothing. As a result, you don't have to override the constructor like you would if it took a String or Integer or any non-vararg parameter. Changing my class to this fixed the problem:
import com.sun.jersey.api.core.PackagesResourceConfig;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;
public class ResourceConfigClass extends PackagesResourceConfig {
public ResourceConfigClass(String... packages) { //this constructor needs to be here, do not delete it or else the com.sun.jersey.config.property.packages param can't be passed in.
super(packages);
}
public ResourceConfigClass(Map<String, Object> props) { //this constructor needs to be here, do not delete it or else the com.sun.jersey.config.property.packages param can't be passed in.
super(props);
}
#Override
public Map<String, MediaType> getMediaTypeMappings() {
Map<String, MediaType> map = new HashMap<String, MediaType>();
map.put("xml", MediaType.APPLICATION_XML_TYPE);
map.put("json", MediaType.APPLICATION_JSON_TYPE);
return map;
}
}

Categories