Search code examples
javajpaeclipselinkconverterscdi

CDI Inject Class in JPA Converter (EclipseLink)


I have a question, it is possible to use java CDI within a JPA convert?

I'm doing some tests to study and I am not able to inject the objects within my converter:

I'm using a eclipseLink, please see my code, Please analyze my code where I am going wrong? And how can I do this in the best way ?

Basically to greater understanding, I'll have a session bean that represents my User logged in, this session Bean I have the User TimeZone, I would like to inject this Time Zone within My Converter to write the data to UTC in database

My Code:

JPA Converter: org.eclipse.persistence.mappings.converters.Converter

package joda;

import inject.qualifier.UserTimeZoneQualifier;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import enumerator.UserType;
import security.UserSession;

@RequestScoped
public class JodaDateTimeUTCConverter implements Converter {
private static final long serialVersionUID = 1L;

// JUST TEST IT'S WAS INJECT AND REMOVE
private UserSession userSession = new UserSession("America/Mexico_City", UserType.HIGH_HISK);

@Inject
@UserTimeZoneQualifier
String userTimeZone;

//TODO FOR TEST
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss:SSS - z - ZZZZZZZZZZZZZZZZZZ");
SimpleDateFormat dt = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss:SSS - z - ZZZZZZZZZZZZZZZZZZ"); 

@Override
public Object convertDataValueToObjectValue(Object dataValue, Session session) {
    // TODO REMOVE
    DateTimeZone timeZone = DateTimeZone.forID(userSession.getTimeZoneLocale());
    System.out.println("BEFORE OF CONVERTION : " + dt.format(dataValue));
    System.out.println("AFTER  OF CONVERTION : " + dtf.print(new DateTime((Timestamp) dataValue).withZone(timeZone)));
    System.out.println("userTimeZone INJECT" + userTimeZone);


    return dataValue instanceof Date ? new DateTime((Timestamp) dataValue).withZone(timeZone) : null;

}

@Override
public Object convertObjectValueToDataValue(Object objectValue, Session session) {

    System.out.println("GO TO DB(DATAVALUE)");
    System.out.println("AFTER  OF CONVERTION : " + dtf.print(((DateTime) objectValue).withZone(DateTimeZone.UTC)));

    return objectValue instanceof DateTime?((DateTime) objectValue).withZone(DateTimeZone.UTC).toLocalDateTime().toDate() : null;
}

@Override
public void initialize(DatabaseMapping mapping, Session session) {
}

@Override
public boolean isMutable() {
    return false;
}

public String getUserTimeZone() {
    return userTimeZone;
}

public void setUserTimeZone(String userTimeZone) {
    this.userTimeZone = userTimeZone;
}
}

My @UserTimeZoneQualifier:

package inject.qualifier;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, TYPE, PARAMETER})
public @interface UserTimeZoneQualifier {

}

And My UserSessionProduce:

package inject;

import inject.qualifier.UserTimeZoneQualifier;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Produces;

import enumerator.UserType;
import security.UserSession;

public class UserSessionProduce {

private UserSession userSession;

@PostConstruct
public void init(){
    this.userSession = new UserSession("America/Mexico_City", UserType.ADMINISTRATOR);
}

@Produces
public UserSession getUserSessionInstance(){
    return this.userSession;
}

@Produces
@UserTimeZoneQualifier
public String getUserSessionTimeZone(){
    return this.userSession.getTimeZoneLocale();
}

@Produces
public UserType getUserType(){
    return this.userSession.getUserType();
}
}

Note: Outside the injection into the Converter, all the other features are working perfectly, I can eject the EntityManager and other classes as well as persist the data in the database


Solution

  • Unfortunately, you can't. The spec mandates support for CDI injection in EntityListener implementations. It was not applied to converters.

    If you want to access your injection points, you can use CDI.current() to get access to a CDI<Object> instance. Using this is just like using Instance<Object> - you can do things like .select(qualifier).select(clazz).get() to retrieve the bean instance.

    If you need to use the qualifier, you need a literal first.

    public class UserTimeZoneQualifierLiteral extends AnnotationLiteral<UserTimeZoneQualifier> implements UserTimeZoneQualifier {
    
    }
    

    Then instantiate

    UserTimeZoneQualifier qualifier = new UserTimeZoneQualifier(); // or use a singleton here.