I've got a session scoped CDI bean, and I need to somehow access the HttpServletRequest object in this bean's #PostConstruct method. Is it possible? I've tried to Inject such an object, but it results in:
WELD-001408 Unsatisfied dependencies for type [HttpServletRequest] with qualifiers [#Default] at injection point [[field] #Inject ...]
As I understood while googling, the Seam framework has such a functionality, but I have a standard Java EE application on a GlassFish server.
Is it even possible to somehow pass the request to a CDI bean's #PostConstruct method?
As per your comment, you want access to the user principal. You can just inject it like this: #Inject Principal principal; or #Resource Principal principal;, see Java EE 6 Tutorial.
Update
I'll answer your direct question. In Java EE 7 (CDI 1.1) injection of HttpServletRequest is supported out of the box. In Java EE 6 (CDI 1.0) however, this is not supported out of the box. To get it working, include the class below into your web-app:
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class CDIServletRequestProducingListener implements ServletRequestListener {
private static ThreadLocal<ServletRequest> SERVLET_REQUESTS = new ThreadLocal<>();
#Override
public void requestInitialized(ServletRequestEvent sre) {
SERVLET_REQUESTS.set(sre.getServletRequest());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
SERVLET_REQUESTS.remove();
}
#Produces
private ServletRequest obtain() {
return SERVLET_REQUESTS.get();
}
}
Note: Tested only on GlassFish 3.1.2.2
When using the code from rdcrng be aware of the following:
* The producer-method obtain is dependent-scoped, thus is only called once for application scoped beans (and will resolve to problems for every other request except the first)
* You can solve this with #RequestScoped
* When RequestScoped annotated, you will only get a proxy, and thus you cannot cas it to HttpServletRequest. So you maybe want a producer for HttpServletRequest.
Also note: As per CDI specification link passage 3.6, java ee beans are NOT consideres managed beans. Thus you will end up with two instances of CDIServletRequestProducingListener - one managed by the Java EE container, one managed by the CDI-container. It only works because SERVLET_REQUESTS is static.
Following the modified code for your convenience.
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
#WebListener
public class CDIServletRequestProducingListener implements ServletRequestListener {
private static ThreadLocal<ServletRequest> SERVLET_REQUESTS = new ThreadLocal<ServletRequest>();
#Override
public void requestInitialized(ServletRequestEvent sre) {
SERVLET_REQUESTS.set(sre.getServletRequest());
}
#Override
public void requestDestroyed(ServletRequestEvent sre) {
SERVLET_REQUESTS.remove();
}
#RequestScoped
#Produces
private HttpServletRequest obtainHttp() {
ServletRequest servletRequest = SERVLET_REQUESTS.get();
if (servletRequest instanceof HttpServletRequest) {
return (HttpServletRequest) servletRequest;
} else {
throw new RuntimeException("There is no HttpServletRequest avaible for injection");
}
}
}
Related
I'm trying to print a message after the application startup with #PostConstruct, but nothing is printed.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
I have read that #PostConstruct is Lazy. Does this mean that I need to do
something else for this to work?
You can also use #EventListener annotation to acheive what you what, if using #PostConstruct is not that important to you.
For example in your case, you can add following code in any class to listen for application startup event.
#EventListener
void onStartup(ServerStartupEvent event) {
println("Hey, I work from anywhere in project..")
}
Code shared above is in Groovy
Keep in mind, the event listener added in main application class is usually called first from what I have observed.
The problem (aka feature) is, as you already mentioned, the lazy loading.
I see two solutions:
You have to do something to cause that bean to be initialized.
Change the scope of the bean from #Singleton to #Context
Micronaut has a few built-in scopes (see https://docs.micronaut.io/latest/guide/index.html#scopes) and the documentation of #Context states (see https://docs.micronaut.io/latest/api/io/micronaut/context/annotation/Context.html)
Context scope indicates that the classes life cycle is bound to that of the BeanContext and it should be initialized and shutdown during startup and shutdown of the underlying BeanContext.
Micronaut by default treats all Singleton bean definitions as lazy and will only load them on demand. By annotating a bean with #Context you can ensure that the bean is loaded at the same time as the context.
package dev.renansouza.server;
import javax.annotation.PostConstruct;
import io.micronaut.context.annotation.Context;
#Context
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
See the project at https://github.com/jeffbrown/renansouzapostconstruct.
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/ServerService.java
package renansouzapostconstruct;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class ServerService {
#PostConstruct
public void print() {
System.out.println("Hello!");
}
}
https://github.com/jeffbrown/renansouzapostconstruct/blob/master/src/main/java/renansouzapostconstruct/DemoController.java
package renansouzapostconstruct;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.HttpStatus;
#Controller("/demo")
public class DemoController {
private ServerService serverService;
public DemoController(ServerService serverService) {
this.serverService = serverService;
}
#Get("/")
public HttpStatus index() {
return HttpStatus.OK;
}
}
When you start the app you won't see the message printed to standard out because the service bean won't have been initialized. Send a request to http://localhost:8080/demo/ and then you will see the message printed to stdout.
I hope that helps.
I'm using JWT (Json Web Token) to secure my Spring boot application.
There's a class which doesn't accept any bean.
First, I thought maybe the bean I want to inject is not defined. So I decided to print the list of beans names using spring ApplicationContext. But I found out that even ApplicationContext can not be injected into this class:
Any idea why this happens ?
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private TokenAuthenticationService tokenAuthenticationService;
#Autowired
private ApplicationContext applicationContext;
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException,
IOException, ServletException {
CustomUserDetails creds = new ObjectMapper().readValue(
req.getInputStream(), CustomUserDetails.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(creds.getUsername(),
creds.getPassword()));
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res, FilterChain chain, Authentication auth) {
java.util.List s = Arrays.asList(applicationContext.getBeanDefinitionNames());
System.out.println(s);
tokenAuthenticationService.addAuthentication(res, auth.getName());
}
}
The JWTLoginFilter has to be a bean as well to allow Spring to inject other beans into. Currently, Spring doesn't have any control over this. Marking the class with the #Component / #Service / #Repository annotations (depends on what role your filter plays, I assume the #Component is a good choice) is going to resolve the issue.
EDIT 1:
JWTLoginFilter required a bean of type java.lang.String that could not be found. Can a component have a constructor?
The problem: Spring tried to use a two-argument constructor to create a bean and was expected that these two arguments are its beans. But it's not true because there is no bean with the String class.
The solution: You should define a non-argument constructor to allow Spring to make an untuned instance without issues. Then create setters to provide methods which Spring will use to inject needed dependencies.
EDIT 2:
The workaround is to define a String bean (in a #Configuration class) which will be injected into the JWTLoginFilter constructor, but I'm not sure that your filter needs to have some external dependencies.
#Bean
public String getStringPatternBean() {
return "pattern";
}
Well, I finally decided to change how these classes are designed. I made methods inside TokenAuthenticationService static.
Since I'm struggling on documentation about CDI, I hope this question could become a useful resource for the correct CDI annotations to use in Jersey/Glassfish.
Say we have an application BookStore:
package my.bookstore;
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
#ApplicationPath("/bookstore")
public class BookStore extends ResourceConfig {
public BookStore() {
this.packages("my.bookstore.resource");
}
}
We want to make Book entities accessible via a RESTful service:
package my.bookstore.entity;
public class Book {
public String isbn;
public String title;
public String author;
public Book(String isbn, String title, String author) {
this.isbn = isbn;
this.title = title;
this.author = author;
}
}
So we need a DAO to access the datastore:
package my.bookstore.dao;
import my.bookstore.entity.Book;
import java.util.List;
public interface BookDAO {
public List<Book> getAllBooks();
}
And its implementation:
package my.bookstore.dao;
import my.bookstore.entity.Book;
import java.util.List;
import java.util.ArrayList;
public class DefaultBookDAO implements BookDAO {
public List<Book> getAllBooks() {
List<Book> bookList = new ArrayList<>();
list.add(new Book("1234", "Awesome Book", "Some Author"));
return bookList;
}
}
Then I want to inject the DefaultBookDAO into the RESTful service:
package my.bookstore.resource;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/books")
public class BookResource {
#Inject
BookDAO dao;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Book> getBooks() {
return this.dao.getAllBooks();
}
}
Now, when deploying the application I get:
Unsatisfied dependencies for type BookDAO with qualifiers #Default
since I need to make CDI aware of it; but how? I tried various combinations of #Named, #Default, #Model, #Singleton, #Stateless and many resources such as questions and blog articles have they own interpretation of them.
What are the correct, plain CDI annotation to use to make this injection work in Jersey/Glassfish?
To me, it seems that you did not put beans.xml file into your application. With Glassfish 4 (generally with Java EE 7) this file is not required, however, if you omit it, only beans annotated with scope annotations are considered. Therefore, as DefaultBookDAO is not marked by any annotation, it is not considered by CDI as a candidate for injection.
You have 2 options to fix it and make CDI mechanism consider DefaultBookDAO:
put #Dependent annotation on DefaultBookDAO class - this will not change its scope, as #Dependent is the default scope, but will make CDI to consider this class
create beans.xml file in either META-INF or WEB-INF (for web apps) with the value of bean-discovery-mode="all"
In my opinion, the first option is cleaner - you may easily separate code which can be injected and which cannot. But if you want to increase productivity by omitting unnecessary annotations, go with the second option. It is more complicated, but you have to do it only once per module.
Please see this oracle blog post about beans.xml in Java EE 7 and the default behavior if it is omitted.
Since it is a service you can annotate your DefaultBookDAO with #Stateless.
Then you need an additional class implementing the AbstractBinder class.
It should look like this for your case:
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class MyApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bind(DefaultBookDAO.class).to(BookDAO.class);
}
}
You have to register this class in the class where you extend ResourceConfig like this:
#ApplicationPath("/bookstore")
public class BookStore extends ResourceConfig {
public BookStore() {
register(new MyApplicationBinder());
this.packages("my.bookstore.resource");
}
}
Then the #Inject should work.
See also:
Jersey docs: Chapter 22. Custom Injection and Lifecycle Management
Dependency injection with Jersey 2.0
I'm trying to create a custom Appender that will persist logs to the database using JPA.
The thing is that I'm using PersistenceContext attribute like this
package com.foobar.logging;
import com.foobar.model.SysLog;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.MDC;
import org.apache.log4j.spi.LoggingEvent;
import javax.ejb.Stateless;
#Stateless
public class LogManager extends AppenderSkeleton {
#PersistenceContext(unitName = "primary")
private EntityManager em;
#Override
protected void append(LoggingEvent le) {
SysLog log = new SysLog();
log.setDescripcion(le.getMessage().toString());
if (MDC.get("IdUsuario") != null) {
log.setIdUsuario(MDC.get("IdUsuario").toString());
}
log.setSysAccionLog(null);
this.em.persist(log);
}
#Override
public void close() {
}
#Override
public boolean requiresLayout() {
return false;
}
}
Now when I'm deploying the WAR to JBoss AS 7.1, it fails, and I get the error:
java.lang.VerifyError: class com.foobar.logging.LogManager$Proxy$_$$_Weld$Proxy$ overrides final method getName.()Ljava/lang/String;
How can I use CDI to inject my EntityManager inside an AppenderSkeleton? Has anyone accomplished JPA persistance in an AppenderSkeleton using CDI?
I also tried not using CDI, but since every other object in my app uses it (JAX-RS classes), it collapses.
EJBs are proxies. AppenderSkeleton has a getName method that is final. I think for your use case, you need to implement Appender directly. This will avoid the bean method getName
However, I have to question the idea of trying to make an appendar an EJB. How are you instantiating it?
I have this class (mix of JAX-RS/Jersey and JPA/Hibernate):
public class Factory {
#PersistenceContext(unitName = "abc")
EntityManager em;
#Path("/{id}")
#GET
public String read(#PathParam("id") int i) {
return em.find(Employee.class, i).getName();
}
}
This is the unit test:
public class FactoryTest extends JerseyTest {
public FactoryTest() throws Exception {
super("com.XXX");
}
#Test
public void testReadingWorks() {
String name = resource().path("/1").get(String.class);
assert(name.length() > 0);
}
}
Everything is fine here except one this: em is NULL inside read(). Looks like Grizzly (I'm using this server together with Jersey Test Framework) is not injecting PersistenceContext. What am I doing wrong here?
Everything is fine here except one this: em is NULL inside read(). Looks like Grizzly (I'm using this server together with Jersey Test Framework) is not injecting PersistenceContext. What am I doing wrong here?
I'm not sure Grizzly offers injection.
I'm not sure injection is supported in "any" Jersey resource anyway (Paul Sandoz seems to imply it should be in this thread but I couldn't find clear evidence of that claim).
So to my knowledge, the easiest solution would be to inject the EntityManager into an EJB 3.1 Stateless Session Bean (SLSB) which can be exposed directly as a REST resources (by annotating it with JAX-RS annotations).
Another option would make the JAX-RS resource a managed bean and to use CDI for injection. That's the approach of the TOTD #124: Using CDI + JPA with JAX-RS and JAX-WS.
In both cases, I think you'll need to use the Embedded GlassFish container as container for your Jersey Tests.
Resources
Jersey Test Framework makes it easy!
Jersey Test Framework re-visited!
RESTFul Calculator With JavaScript And ...EJB 3.1
TOTD #124: Using CDI + JPA with JAX-RS and JAX-WS
I found a solution for com.sun.jersey/jersey-grizzly2 version 1.x. I implemented a custom InjectableProvider. The following code is taken from an Oracle article:
import javax.ejb.EJB;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.ws.rs.ext.Provider;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
#Provider
public class EJBProvider implements InjectableProvider<EJB, Type> {
public Scope getScope() {
return Scope.Singleton;
}
public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) {
if (!(t instanceof Class)) return null;
try {
Class c = (Class)t;
Context ic = new InitialContext();
final Object o = ic.lookup(c.getName());
return new Injectable<Object>() {
public Object getValue(HttpContext c) {
return o;
}
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
I had to slightly adapt it to fit my environment. Also note that the provider has to be in the same package as your service class, otherwise it won't be picked up (it does not say that in the article).