I have a simple AttributeConverter implementation in which I try to inject an object which have to provide the conversion logic, but #Inject seem not to work for this case. The converter class looks like this:
#Converter(autoApply=false)
public class String2ByteArrayConverter implements AttributeConverter<String, byte[]>
{
#Inject
private Crypto crypto;
#Override
public byte[] convertToDatabaseColumn(String usrReadable)
{
return crypto.pg_encrypt(usrReadable);
}
#Override
public String convertToEntityAttribute(byte[] dbType)
{
return crypto.pg_decrypt(dbType);
}
}
When the #Converter is triggered it throws an NullPointerException because the property crypto is not being initialized from the container. Why is that?
I'm using Glassfish 4 and in all other cases #Inject works just fine.
Is it not possible to use CDI on converters?
Any help will be appreciated :)
The accent of my question is more the AttributeConverter part. I understand that for the CDI to work a bean must meet the conditions described here http://docs.oracle.com/javaee/6/tutorial/doc/gjfzi.html.
I also have tried to force the CDI to work by implementing the following constructor:
#Inject
public String2ByteArrayConverter(Crypto crypto)
{
this.crypto = crypto;
}
And now I got the following exception which doesn't give me any clue:
2015-07-23T01:03:24.835+0200|Severe: Exception during life cycle processing
org.glassfish.deployment.common.DeploymentException: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [PU_VMA] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-7172] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Error encountered when instantiating the class [class model.converter.String2ByteArrayConverter].
Internal Exception: java.lang.InstantiationException: model.converter.String2ByteArrayConverter
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createDeployFailedPersistenceException(EntityManagerSetupImpl.java:820)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:760)
...
I even tried using #Producer or #Decorator in order to have the CDI working on that place, but I still think there is something specific with the AttributeConverter which doesn't allow CDI. So problem not solved yet.
Unfortunately you can't inject CDI beans into a JPA converter, however in CDI 1.1 you can inject your Crypto programmatically :
Crypto crypto = javax.enterprise.inject.spi.CDI.current().select(Crypto.class).get()
For reference, JPA 2.2 will allow CDI to be used with AttributeConverter, and some vendors already support this (EclipseLink, DataNucleus JPA are the ones I know of that do it).
You're trying to combine two different worlds, as CDI doesn't know about JPA Stuff and vice-versa. (One annotation parser of course doesn't know about the other)
What you CAN do, is this:
/**
* #author Jakob Galbavy <code>jg#chex.at</code>
*/
#Converter
#Singleton
#Startup
public class UserConverter implements AttributeConverter<User, Long> {
#Inject
private UserRepository userRepository;
private static UserRepository staticUserRepository;
#PostConstruct
public void init() {
staticUserRepository = this.userRepository;
}
#Override
public Long convertToDatabaseColumn(User attribute) {
if (null == attribute) {
return null;
}
return attribute.getId();
}
#Override
public User convertToEntityAttribute(Long dbData) {
if (null == dbData) {
return null;
}
return staticUserRepository.findById(dbData);
}
}
This way, you would create a Singleton EJB, that is created on boot of the container, setting the static class attribute in the PostConstruct phase. You then just use the static Repository instead of the injected field (which will still be NULL, when used as a JPA Converter).
Well, CDI still doesn't work for AttributeConverter, which would be the most elegant solution, but I have found a satisfying workaround. The workaround is using #FacesConverter. Unfortunately per default CDI doesn't work in faces converters and validators either, but thanks to the Apache MyFaces CODI API you can make it work unsing the #Advaced annotation :) So I came up with an implementation like this:
#Advanced
#FacesConverter("cryptoConverter")
public class CryptoJSFConverter implements Converter
{
private CryptoController crypto = new CryptoController();
#Inject
PatientController ptCtrl;
public Object getAsObject(FacesContext fc, UIComponent uic, String value)
{
if(value != null)
return crypto.pg_encrypt(value, ptCtrl.getSecretKey());
else
return null;
}
public String getAsString(FacesContext fc, UIComponent uic, Object object)
{
String res = crypto.pg_decrypt((byte[]) object, ptCtrl.getSecretKey());
return res;
}
}
The injected managed bean has to be explicitly annotated with #Named and some scope definition. A declaration in faces-config.xml doesn't work! In my solution it looks like this:
#Named
#SessionScoped
public class PatientController extends PersistanceManager
{
...
}
Now one has a context information in the converter. In my case it is session/user specific cryptography configuration.
Of course in such a solution it is very likely that a custom #FacesValidator is also needed, but thanks to CODI one have the possibility for using CDI here also (analog to converter).
Related
I read a lot about the possibility of injection with jax rs 2.0 and in particular with jersey.
I even read that ejb injection is expected in jax rs 2.0 spec. But i still haven't found a unique solution among the variety of posts i read over the net.
In my use case i'm working with:
WildFly 9.0 and Jersey 2.x
I have a webapplication exposing my REST services and importing a jar implementing my model data.
This is the CDI approach:
#RequestScoped
#Path("/myPath")
public class ModelRetriever {
#Context
SecurityContext securityContext;
#Inject
private IMyModel MyModel;
#Path("{i}")
#GET
#Produces("application/json")
public Response countries(#PathParam("i") String countryId)
throws JSONException, Failure, IOException {
MyModel.doSomething();
}
This is my IMyModel interface
public interface IKasPrincipal extends Principal {
public void doSomething();
}
And this is MyModel implementation:
#RequestScope
public class MyModelImpl implements IMyModel {
public void doSomehting() {
doSomething();
}
}
Another method i tried is to use EJB injection changing my previous annotations like this:
#Stateless
#Path("/myPath")
public class ModelRetriever {
#EJB
private IMyModel MyModel;
#Path("{i}")
#GET
#Produces("application/json")
public Response countries(#PathParam("i") String countryId)
throws JSONException, Failure, IOException {
MyModel.doSomething();
}
This is my IMyModel interface
#Local
public interface IKasPrincipal extends Principal {
public void doSomething();
}
And this is MyModel implementation:
#Stateless
public class MyModelImpl implements IMyModel {
public void doSomehting() {
doSomething();
}
}
i get a null object using EJB approach and i get this exception using CDI
Caused by: org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=IMyModel,parent=ModelRetriever,qualifiers={},position=-1,optional=false,self=false,unqualified=null,616459318)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:75)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:211)
at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:234)
So is there anything i'm missing?
see other Stack Overflow related posts:
Dependency injection with Jersey 2.0
HK2 Jersey EJB 3 injection
The problem you are having is that HK2 does not know about anything that was not registered directly into it, and HK2 tries to to satisfy all dependency in your Jersey aware class.
I has this issue a while back. Then I discovered that Jersey uses HK2 internally. HK2 is a JSR-330 implementation (CDI).
One would think that a open-source project would declares it's CDI beans and use them regardless of the CDI implementation, but it looks like its not that way.
see : https://java.net/jira/browse/JERSEY-1933
see : https://hk2.java.net/integration.html
You can register your components into HK2...
see : https://jersey.java.net/documentation/latest/ioc.html
For all I know, you cannot inject CDI components (or anything else, as EJB) into Jersey's classes using your own (or your container's) CDI implementation, unless you use Glassfish (which personally I would never use) which in turn uses HK2 as its CDI implementation.
To me, this is a major draw back. But the only(?) draw back of Jersey.
-Maybe I missed something (which is very possible)
-Maybe this is a trick from Oracle so that you can't use Jersey in, let's say, your Websphere app which uses OpenWeb Beans as CDI implementation.
-Maybe they hardwired it to HK2, and just don't care that Jersey can't be used as a drop in component in your application, which relies on CDI or EJB
I'm not aware of why #EJB not worked, but, you can use #Produces/#Disposes bean.
#ApplicationScoped // or some other scoped
public class MyModelProducer {
#Produces public MyModel produceMyModel() {
}
public void disposeMyModel#Disposes final MyModel model) {
}
}
I am trying to do something like
https://wiki.eclipse.org/EclipseLink/Release/2.5/JPA21#CDI_Entity_Listeners
but non of my injections are working.
Does Entity Listener doesn't inject CDI dependencies ?
Do we have to do something different.
public class EntityListener {
#Inject
private UserUtility userUtility;
#PrePersist
public void prePersist(Object object) {
logger.log("prepersist", object);
}
#PostPersist
public void postPersist(Object object){
object.createUser(userUtility.getLoggedInUser())
}
}
But my userUtility is always null, I am using userUtility in different places in my project and It is properly injected.
Just not in here.
Any clues, Please help me.
I try to autowire my custom mongo repository (and it seems the constructor is executed) but still the result is null
I've looked at some similar questions
Spring Data Neo4j - #Autowired Repository == null
and
spring data mongo repository is null
but I still don't know how to solve this.
public class TestRepo {
#Autowired
PersonRepository repository;
public void find(String name)
{
System.out.println(repository.findByName(name));
}
}
config
<mongo:repositories base-package="com.yyyy.zzz" />
PersonRepository
public interface PersonRepository extends Repository<Person, BigInteger> {
#Query("{name : ?0}")
public Person findByName(String name);
}
Implementation
public class PersonRepositoryImpl implements PersonRepository{
PersonRepositoryImpl()
{
System.out.println("constructing");
}
public Person findByName(String name) {
...
}
}
if I get the repository bean directly from context it works
Your repository setup looks suspicious. To execute query methods, you don't need to provide an implementation at all. I suspect in your current setup the custom implementation you have in PersonRepositoryImpl "overrides" the query method and thus will be preferred on execution.
If you simply drop your implementation class, Spring Data will automatically execute the query for you on invocation.
Generally speaking, custom implementation classes are only needed for functionality you cannot get through other means (query methods, Querydsl intergration etc.).
I'm writing a Java EE 6 application that makes use of Morphia to persist objects to MongoDB. I'd like to be able to #Inject my DAO classes where necessary, so I created a Factory class that instantiates the DAO appropriately. It looks something like this:
public class MyDAOFactory {
#Inject
private Datastore mongoDatastore = null;
/**
* Creates the DAO
*/
#Produces
#ApplicationScoped
public MyDAO createDAO() {
MyDAO dao = new MyDAO(
this.mongoDatastore);
return dao;
}
}
The code compiles fine, but when I run my application on JBoss EAP 6.1 it complains because MyDAO does not have a no-arg constructor. I would add one, but the Morphia BasicDAO class does not have one either, so I don't know that it would work that way.
Is there a way to #Inject a DAO instance into my EJB, Servlet, etc.? Or do I need to manually instantiate it every time?
It seems that CDI needs the no-arg constructor for MyDAO for some reason. Maybe because of how you use this bean (see specs ch.5.4 "Client Proxies" for possible reasons).
You cannot create a default constructor, because the base class does not have one and, from what I see from the code the super constructors make immediate use of their args. Therefore passing null to super() from a no-arg constructor will throw errors.
My suggestion is to create an interface (optionally extending org.mongodb.morphia.dao.DAO), e.g. MyDAOInterface that has all public business methods of MyDAO. Then modify MyDAO to implement this interface and change your producer to return MyDAOInterface:
public interface MyDAOInterface extends DAO {...}
public class MyDAO implements MyDAOInterface {
// same implementation
}
public class MyDAOFactory {
#Inject
private Datastore mongoDatastore = null;
/**
* Creates the DAO
*/
#Produces
#ApplicationScoped
public MyDAOInterface createDAO() {
MyDAO dao = new MyDAO(this.mongoDatastore);
return dao;
}
}
By the way, programming to interfaces has the extra benefit of making your code more testable, so it is worth the minor hassle.
I follow the Weld's doc
in the section 4.11. The InjectionPoint object
There is a very interesting example about how to obtain the http parameter using CDI
but i copy-pasted the code into netbeans, everything compiles, but has an deployment error
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Injection point has unsatisfied dependencies. Injection point: parameter 1 of java.lang.String com.test.HttpParamProducer.getParamValue(javax.enterprise.inject.spi.InjectionPoint,javax.servlet.ServletRequest); Qualifiers: [#javax.enterprise.inject.Default()]
how to solve this problem???
public class HttpParamProducer {
#HttpParam("")
#Produces
String getParamValue(
InjectionPoint ip, ServletRequest request) {
return request.getParameter(ip.getAnnotated().getAnnotation(HttpParam.class).value());
}
}
Every parameter on a producer method is injected, and none of your beans (including producers) provides the API type ServletRequest to satisfy this injection point.
it seems that after two years, this question is still interested
this is a short coming of the CDI spec, where it doesn't require the container to expose HttpServletRequest as injectable bean
here is a reasonable work around
#WebListener
public class HttpServletRequestProducer implements ServletRequestListener {
private final static ThreadLocal<HttpServletRequest> holder = new ThreadLocal<HttpServletRequest>();
#Override
public void requestDestroyed(ServletRequestEvent sre) {
holder.remove();
}
#Override
public void requestInitialized(ServletRequestEvent sre) {
holder.set((HttpServletRequest)sre.getServletRequest());
}
#Produces #RequestScoped HttpServletRequest get() {
return holder.get();
}
}
now #Inject HttpServletRequest will be working as expected
happy coding