Spring-security-oauth OAuthProviderTokenServices custom implementation in OAuth 1.0 - java

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.

Related

Implement an Undertow reverse proxy that behaves like nginx

For development purposes, not everyone can install nginx on their machines (like our developers on Windows environments), but we want to be able to do a reverse proxy that behaves like nginx.
Here's our very specific case:
we have a spring boot REST service running on http://0.0.0.0:8081
we have spring boot web application running on http://0.0.0.0:8082
We would like to serve both services from http://0.0.0.0:8080
So we would like to map it like this:
requests to http://0.0.0.0:8080/ get proxied to http://0.0.0.0:8082
requests to http://0.0.0.0:8080/api get proxied to http://0.0.0.0:8081
That way it works like nginx with url rewrite reverse proxying.
I checked out the Undertow source code and examples, and even this specific example: Reverse Proxy Example, but this is a load balancer example, I haven't found any example that covers what I need.
Also, I know Undertow is capable of this, because we know we can configure WildFly to cover this specific case without issues through the Undertow component configuration, but we would like to implement it ourselves as a lightweight solution for local development.
Does anyone know of an example to do this? or any documentation that has enough info to implement this? because I've also read Undertow's documentation on reverse proxying and it's not helpful at all.
Thanks
This should do the job.
It's Java8 so some parts may not work on your setup.
You can start it in a similar way as the example you've mentioned in your question.
package com.company
import com.google.common.collect.ImmutableMap;
import io.undertow.client.ClientCallback;
import io.undertow.client.ClientConnection;
import io.undertow.client.UndertowClient;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.ServerConnection;
import io.undertow.server.handlers.proxy.ProxyCallback;
import io.undertow.server.handlers.proxy.ProxyClient;
import io.undertow.server.handlers.proxy.ProxyConnection;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Start the ReverseProxy with an ImmutableMap of matching endpoints and a default
*
* Example:
* mapping: ImmutableMap("api" -> "http://some-domain.com")
* default: "http://default-domain.com"
*
* Request 1: localhost:8080/foo -> http://default-domain.com/foo
* Request 2: localhost:8080/api/bar -> http://some-domain.com/bar
*/
public class ReverseProxyClient implements ProxyClient {
private static final ProxyTarget TARGET = new ProxyTarget() {};
private final UndertowClient client;
private final ImmutableMap<String, URI> mapping;
private final URI defaultTarget;
public ReverseProxyClient(ImmutableMap<String, URI> mapping, URI defaultTarget) {
this.client = UndertowClient.getInstance();
this.mapping = mapping;
this.defaultTarget = defaultTarget;
}
#Override
public ProxyTarget findTarget(HttpServerExchange exchange) {
return TARGET;
}
#Override
public void getConnection(ProxyTarget target, HttpServerExchange exchange, ProxyCallback<ProxyConnection> callback, long timeout, TimeUnit timeUnit) {
URI targetUri = defaultTarget;
Matcher matcher = Pattern.compile("^/(\\w+)(/.*)").matcher(exchange.getRequestURI());
if (matcher.find()) {
String firstUriSegment = matcher.group(1);
String remaininguri = matcher.group(2);
if (mapping.containsKey(firstUriSegment)) {
// If the first uri segment is in the mapping, update the targetUri
targetUri = mapping.get(firstUriSegment);
// Strip the request uri from the part that is used to map upon.
exchange.setRequestURI(remaininguri);
}
}
client.connect(
new ConnectNotifier(callback, exchange),
targetUri,
exchange.getIoThread(),
exchange.getConnection().getByteBufferPool(),
OptionMap.EMPTY);
}
private final class ConnectNotifier implements ClientCallback<ClientConnection> {
private final ProxyCallback<ProxyConnection> callback;
private final HttpServerExchange exchange;
private ConnectNotifier(ProxyCallback<ProxyConnection> callback, HttpServerExchange exchange) {
this.callback = callback;
this.exchange = exchange;
}
#Override
public void completed(final ClientConnection connection) {
final ServerConnection serverConnection = exchange.getConnection();
serverConnection.addCloseListener(serverConnection1 -> IoUtils.safeClose(connection));
callback.completed(exchange, new ProxyConnection(connection, "/"));
}
#Override
public void failed(IOException e) {
callback.failed(exchange);
}
}
}
As per M. Deinum's comment suggestion, I'll use Zuul Spring Boot component instead of trying to do this with Undertow, as it's more fit for this task.
Here's a link on a tutorial to do this:
https://spring.io/guides/gs/routing-and-filtering/
Hope this helps anyone else, as this is a pretty common case, and I didn't know about Zuul on Spring Boot.

How can we use instance variables in RestEasy?

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.

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.

How to execute a Struts 2 Action from inside a Quartz job. How to obtain the Container?

I am trying to execute a Struts2 Action from inside a Quartz job -- generalizing, from any context which is not the processing of an HTTP request.
I started here http://struts.apache.org/2.0.6/docs/how-can-we-schedule-quartz-jobs.html but the document seems to be pretty obsolete.
I believe (but I may be wrong) I've boiled it down to the need to obtain a Container object:
import java.util.HashMap;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
...
HashMap ctx = new HashMap();
DefaultActionProxyFactory factory= new DefaultActionProxyFactory();
factory.setContainer(HOW DO I GET THE CONTAINER??);
ActionProxy proxy = factory.createActionProxy("", "scheduled/myjob", ctx);
One solution would be to issue an http request (via TCP) against localhost. I would prefer to avoid that.
I somewhat fear what providing this answer may encourage some people to do, but as a proof of concept and to actually provide a solution to anyone who may, for whatever reason (maybe they are inheriting some whacked out application for which this is needed?), need to execute Struts2 actions outside of a normal request context.
But, here is a raw (it is provided as a starting point, not an optimal implementation), but working, solution:
First, add these three classes to a package called com.stackoverflow.struts2.quartz:
A simple job that just asks for a proxy for the given job context and executes it:
package com.stackoverflow.struts2.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class ActionJob implements Job {
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
QuartzActionProxyFactory.getActionProxy(context).execute();
} catch (Exception e) {
e.printStackTrace();
throw new JobExecutionException(e);
}
}
}
Some constants for passing around the action details:
package com.stackoverflow.struts2.quartz;
public class QuartzActionConstants {
public static final String NAMESPACE = "struts.action.namespace";
public static final String NAME = "struts.action.name";
public static final String METHOD = "struts.action.method";
}
A custom ActionProxyFactory that can be accessed statically from the ActionJob:
package com.stackoverflow.struts2.quartz;
import java.util.HashMap;
import java.util.Map;
import org.apache.struts2.impl.StrutsActionProxyFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.ActionProxyFactory;
public class QuartzActionProxyFactory extends StrutsActionProxyFactory {
private static ActionProxyFactory actionProxyFactory;
public QuartzActionProxyFactory() {
actionProxyFactory = this;
}
public static ActionProxy getActionProxy(JobExecutionContext context) throws JobExecutionException {
ActionProxy actionProxy = null;
try {
#SuppressWarnings("unchecked")
Map<String, Object> actionParams = context.getJobDetail().getJobDataMap();
Map<String, Object> actionContext = new HashMap<String, Object>();
actionContext.put(ActionContext.PARAMETERS, actionParams);
actionProxy = actionProxyFactory.createActionProxy(
(String) actionParams.get(QuartzActionConstants.NAMESPACE),
(String) actionParams.get(QuartzActionConstants.NAME),
(String) actionParams.get(QuartzActionConstants.METHOD),
actionContext,
false, //set to false to prevent execution of result, set to true if this is desired
false);
} catch (Exception e) {
throw new JobExecutionException(e);
}
return actionProxy;
}
}
Then, in your struts.xml, add:
<bean name="quartz" type="com.opensymphony.xwork2.ActionProxyFactory" class="com.stackoverflow.struts2.quartz.QuartzActionProxyFactory"/>
<constant name="struts.actionProxyFactory" value="quartz"/>
Then you can schedule action executions with some simple code:
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
scheduler.start();
JobDetail jobDetail = new JobDetail("someActionJob", Scheduler.DEFAULT_GROUP, ActionJob.class);
#SuppressWarnings("unchecked")
Map<String, Object> jobContext = jobDetail.getJobDataMap();
jobContext.put(QuartzActionConstants.NAMESPACE, "/the/action/namespace");
jobContext.put(QuartzActionConstants.NAME, "theActionName");
jobContext.put(QuartzActionConstants.METHOD, "theActionMethod");
Trigger trigger = new SimpleTrigger("actionJobTrigger", Scheduler.DEFAULT_GROUP, new Date(), null, SimpleTrigger.REPEAT_INDEFINITELY, 1000L);
scheduler.deleteJob("someActionJob", Scheduler.DEFAULT_GROUP);
scheduler.scheduleJob(jobDetail, trigger);
And that's it. This code will cause the action to be executed every second indefinitely, and the interceptors will all fire and the dependencies will be injected. Of course, any logic or interceptors that depend on Servlet object like an HttpServletRequest are not going to operate properly, but then it wouldn't make sense to schedule those actions outside of the servlet context, anyway.
You don't need HttpServletRequest to format email in freemarker. See the following answer.
Create multi-part message in MIME format Freemarker template via Spring 3 JavaMail
For sending mail , you can inject the mail component to your Quartz job using spring. Even there is a RequestContextHolder class to retrieve the HttpServlet request, you won't get HttpServletRequest from Quartz job.

Categories