Produce an Entitymanager with extended persistence context via CDI - ejb

We are trying to build a system, which "produces" an entitymanager depending on the logged-in user (kind of multitenancy). Therefor we implemented a stateless ejb like this:
#Stateless
#TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class CustomEntityManagerFactory {
#PersistenceContext(unitName = "EM1")
private EntityManager em1;
#PersistenceContext(unitName = "EM2")
private EntityManager em2;
#Produces
#RequestScoped
public EntityManager getEntityManager() {
// check which entitymanager to return
}
}
The entitymanager is injected like this:
#Stateless
public class EmployeeService {
#Inject
private EntityManager em;
...
}
This producer works as long as only an entitymanager without extended persistence context is needed (in stateless ejb). Unfortunately we also have some stateful ejbs, which need the extended persistence context. Is there a way to implement a CDI producer for this purpose or does this approach only work for stateless ejb with transactional entitymanager?

My guess is you need this:
Create two factories here:
#ApplicationScoped
public class EntityManagerFactoryProducer {
private static EntityManagerFactory factory;
private static EntityManagerFactory factory2;
#Produces
public EntityManagerFactory getEntityManagerFactory(InjectionPoint ip) {
// if the field is named exactly factory2 then factory2 is produced
if (ip.getMember().getName().equals("factory2")) {
if (factory2 == null) {
factory2 = Persistence.createEntityManagerFactory("EM2");
}
return factory2;
}
else {
if (factory == null) {
factory = Persistence.createEntityManagerFactory("EM1");
}
return factory;
}
}
Use factories to createEntityManagers
public class EntityManagerProducer {
#Inject
private EntityManagerFactory factory;
#Inject
private EntityManagerFactory factory2;
#Produces
#RequestScoped
public EntityManager getEntityManager() {
return factory.createEntityManager();
}
#Produces
#RequestScoped
#MyCustomQualifier
public EntityManager getEntityManager2() {
return factory2.createEntityManager();
}
}
The qualifier used to differ between entitymanagers
#Qualifier
#Retention(RUNTIME)
#Target({FIELD })
public #interface MyCustomQualifier {
#Nonbinding
public String value() default "";
}
end usage:
#Inject
EntityManager em;
#Inject
#MyCustomQualifier
EntityManager em2;
Of course you can use the qualifier to differ between factories instead of checking the name of the field. I just wanted to get some more depth into the answer.

Related

Can two stateless EJB's share the same request scoped entity manager?

I've decided to use a request scoped container managed EntityManager and I've created a producer for that reason:
#RequestScoped
public class EntityManagerProducer {
#PersistenceContext(unitName = "PU", type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
#Produces
public EntityManager getEntityManager() {
return entityManager;
}
}
I've got two EJB's that expose a remote client view:
#Remote
public interface EJB1 {
void createPerson(int id, String firstName, String lastName);
}
#Remote
public interface EJB2 {
void containsEntity(Person person);
}
#Stateless
public class EJB1Impl implements EJB1 {
#Inject
private EntityManager entityManager;
#EJB
private EJB2 ejb2;
#Override
public void createPerson(final int id, final String firstName, final String lastName) {
Person person = new Person();
person.setId(id);
person.setFirstName(firstName);
person.setLastName(lastName);
entityManager.persist(person);
System.out.println("EJB1Impl: persistence context contains entity: " + entityManager.contains(person));
ejb2.containsEntity(person);
}
}
#Stateless
public class EJB2Impl implements EJB2 {
#Inject
private EntityManager entityManager;
#Override
public void containsEntity(final Person person) {
System.out.println("EJB2Impl: PersistenceContext contains entity: " + entityManager.contains(entity));
person.setLastName("new name");
//entityManager.merge(person);
}
}
The EJB's are deployed on WildFly 10. I access them by a remote client using this tutorial. The current version throws this exception: org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.RequestScoped. If I remove the #RequestScoped annotation from the producer, I don't get an exception but the EntityManager injected into the second EJB returns false when asked if it contains the entity and if I want the changes made to the entity (the change of the last name) to have any effect, I have to call entityManager.merge(person), which apparently means that the entity is detached. I'm sure that the second EJB executes withing the same transaction because if I inject an EJBContext and call setRollbackOnly() the transaction started in EJB1 is rolled back and a new person is not inserted into the data base.
The documentation of javax.enterprise.context.RequestScoped says that the request scope is active during any remote method invocation of any EJB, so what gives? How can I share an EntityManager during a request across multiple EJB's

Annotations on Interfaces in Spring

I have problems autowiring beans which are derived from interfaces with annotations.
#Component
#EnableAsync
public interface Calculator {
#Async
public Future<String> calculate();
}
public interface SpecificCalculator extends Calculator {
}
public class ConcreteSpecificCalculator implements SpecificCalculator {
#Override
public Future<String> calculate() {
// do calculation here
return new AsyncResult<String>("hello");
}
}
From what I understood from: Annotations on Interfaces? the #Component Annotation should also apply to all subclasses. Now I have a Mapper class returning all types that are derived from SpecificCalculator.
#Component
public class CalculatorMapper {
#Autowired
private List<SpecificCalculator> specificCalculators;
public List<Calculator> retrieveCalculatorsByModuleId(Integer moduleId) {
if(moduleId==...){
return specificCalculators;
}else{
...
}
}
This is not working. Spring does not find the ConcreteSpecificCalculator and is not injecting it. If I Annotate ConcreteSpecificCalculator with #Component again, it is working. Can anybody explain this to me?

Is PersistenceContext allowed in RequestScoped bean?

With following class,
#Path("/somes")
#RequestScoped
public class SomesResource {
#PersistenceContext(unitName = "some")
private EntityManager entityManager;
#Inject
private SomeService someService;
}
Where,
#Stateless
public class SomeService {
#PersistenceContext(unitName = "some")
private EntityManager entityManager;
}
Question 1: Is it O.K. to be injected with #PersistenceContext in #RequestScoped bean?
Question 2: Is entityManager in SomesResource need to be worked with an UserTransaction in any modification job?
Question 3: Are two entityManagers effectively same instance?

Spring MVC - How to create a proper Service layer?

I'm using SpringBoot and I am trying to create a service layer for my web application but i cant make it work.
My classes look like this
ServiceFactory
#Service
public class ServiceFactory {
#Autowired
public static EncuestaService getEncuestaService()
{
return new EncuestaServiceImpl();
}
}
EncuestaService
public interface EncuestaService {
void crearEncuesta(Encuesta encuesta, Map<String,String> parametros);
}
EncuestaServiceImpl
#Service
public class EncuestaServiceImpl implements EncuestaService {
#Override
public void crearEncuesta(Encuesta encuesta, Map<String, String> parametros) {
CrearEncuesta nueva = new CrearEncuesta(encuesta,parametros);
nueva.execute();
}
}
CrearEncuesta
#Service
public class CrearEncuesta {
private Encuesta encuesta;
private Map<String,String> parametros;
#Autowired
private RespuestasRepository respuestasRepository;
#Autowired
private EncuestasRepository encuestasRepository;
public CrearEncuesta(Encuesta encuesta, Map<String,String> parametros) {
super();
this.encuesta = encuesta;
this.parametros = parametros;
}
public void execute()
{
encuestasRepository.save(encuesta);
}
}
Everytime I call ServiceFactory.getEncuestasService().crearEncuesta() from any Controller it returns me a NullPointerException.
From what I have been reading I should not be creating a new EncuestsaServiceImpl() in my ServiceFactory but I don't really know the correct way to do so. I would appreciate if anyone could help me out :P.
Edit:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Controller
#Controller
public class EncuestaController {
#RequestMapping(value ="registrarEncuesta", method = RequestMethod.POST)
private String formularioEncuesta(#Valid #ModelAttribute("formEncuesta") EncuestaForm formEncuesta, BindingResult bindingResult,#RequestParam Map<String,String> allRequestParams)
{
if (bindingResult.hasErrors()) {
return "nuevaEncuesta";
}
try {
Encuesta nueva = formEncuesta.toEncuesta();
ServiceFactory.getEncuestaService().crearEncuesta(nueva,allRequestParams);
} catch (DataIntegrityViolationException e) {
return "nuevaEncuesta";
}
return "redirect:/encuestas";
}
}
You will have to read a little bit more about dependency injection. The central principle in Spring Framework is dependency injection which should be used to avoid referencing beans (service implementations, repository implementations etc...) statically. Spring container also servers as a bean factory that will instantiate and inject (autowire) implementations to beans that need them.
Because Spring will instantiate service interface implementations for you, you don't need ServiceFactory. In your controller you need to add a reference (a field) to EncuestaService and annotate it as Autowired and Spring will wire in the implementation. And then you can just use it in your controller.
#Controller
public class EncuestaController {
#Autowired
EncuestaService encuestaService;
#RequestMapping(value ="registrarEncuesta", method = RequestMethod.POST)
private String formularioEncuesta(#Valid #ModelAttribute("formEncuesta") EncuestaForm formEncuesta, BindingResult bindingResult,#RequestParam Map<String,String> allRequestParams)
{
if (bindingResult.hasErrors()) {
return "nuevaEncuesta";
}
try {
Encuesta nueva = formEncuesta.toEncuesta();
encuestaService.crearEncuesta(nueva,allRequestParams);
} catch (DataIntegrityViolationException e) {
return "nuevaEncuesta";
}
return "redirect:/encuestas";
}
}

init-instantiated variables

Suppose I have an EntityManagerFactory reference instantiated within an init-method of a servlet:
StartServlet.java:
#WebServlet("/start")
public class StartServlet extends HttpServlet {
private EntityManagerFactory factory;
#Override
public void init() {
factory = Persistence.createEntityManagerFactory("jpa");
getServletContext().setAttribute("factory", factory);
}
}
Which I also do visible for all other servlets within the "container". Now, lets say I want to access this reference in another servlet, like:
AnotherServlet.java:
#WebServlet("/another")
public class AnotherServlet extends HttpServlet {
private EntityManagerFactory factory;
#Override
public void init() {
factory = (EntityManagerFactory) getServletContext().getAttribute("factory");
if(factory == null) {
//factory not instantiated from StartServlet, what to do?
}
}
}
What do I do if its not instantiated from StartServlet.java (init has not been invoked)? Hope my question is precise enough!
-Superdids
Don't depend on the servlet container's order of Servlet initialization.
Instead, do the initialization in a ServletContextListener.

Resources