I am building an application providing a JAX-RS REST service using JPA (EclipseLink). When exposing User entities over JSON, I am using the @XmlTransient
annotation on some fields (e.g. the password field) to hide them from the JSON representation. When sending a create or update (POST/PUT) operation, I would like to populate the missing fields again so JPA will correctly perform the operation.
My current approach is that I have a custom JsonDeserializer
that is used to deserialize the User and to add the missing fields. For this I would like to inject (using @Inject
) a UserFacadeREST
bean which handles the JPA-stuff. However, this injection fails and the bean instance is null
(which then of course causes a NullPointerException
).
My UserFacadeREST
bean is annoted as follows:
@Stateless
@LocalBean
@Path(UserFacadeREST.PATH)
public class UserFacadeREST extends AbstractFacade<User> {
//...
}
My UserDeserilizer
(custom JsonDeserializer
):
public class UserDeserializer extends JsonDeserializer<User> {
@Inject
private UserFacadeREST userFacade;
@Override
public User deserialize(JsonParser parser, DeserializationContext context) throws IOException,
JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
int userId = (Integer) ((IntNode) node.get("userID")).numberValue();
System.out.println(userId);
User user = userFacade.find(userId); // This line produces the NullPointerException
return user;
}
}
which I then use on my User entity with @JsonDeserialize
:
@Entity
@Table(name = "User")
@XmlRootElement
@JsonDeserialize(using = UserDeserializer.class)
public class User implements Serializable {
// ...
}
I have included a bean.xml file in my WEB-INF folder with bean-discovery-mode
set to all
. What am I missing?
Jon Peterson pointed me to the right direction. I finally chose to implement the 'hackish' solution, in a way. Please note that there are basically 2 options here (if you know another one, please let me know!). Short version:
javax.enterprise.inject.spi.CDI.current().select(UserFacadeRest.class).get()
as described in the accepted answer of the question mentioned by Jon orSo for my question, the solution looks as follows:
1.
import javax.enterprise.inject.spi.CDI;
public class UserDeserializer extends JsonDeserializer<User> {
private final UserFacadeREST userFacade =
CDI.current().select(UserFacadeREST.class).get();
// Rest as before
}
2. In this case, in the deserialize
method of my JsonDeserializer
I would construct a User that just holds the userID. In every request method I would then have to examine all the users and replace them by the actual user by calling EntityManager.find(User.class, user.getUserID())
. This means more effort in the business logic as you have to keep in mind that everytime you need to work on a User
in a request method, you first have to do a query to get the 'full' User
object. In the first solution, this query is hidden from the business logic and already happens in the JsonDeserializer
.
public class UserDeserializer extends JsonDeserializer<User> {
@Override
public User deserialize(JsonParser parser, DeserializationContext context) throws IOException,
JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
int userId = (Integer) ((IntNode) node.get("userID")).numberValue();
return new User(userId); // Placeholder User object containing only the user ID, needs to be replaced in business logic
}
}