Hot questions for Using Enterprise JavaBeans in spring

Question:

Consider a stateless EJB

from ejb 3.1 spec containers will support many instances of a session bean executing concurrently; however, each instance sees only a serialized sequence of method calls. Therefore, a stateful or stateless session bean does not have to be coded as reentrant

So a Stateless seession bean can "serve" at most one request at a time, this is usually implemented by the container managing a pool of beans. The great goal of all this is Thread safety.

My question is why do we need this form of thread safety ? I mean Spring beans are singleton and not thread safe (they can serve any number of request at a time) and we have no problem with that.


Answer:

You need that form of thread safety if the stateless session bean has member variables that are themselves not thread safe (e.g., a SAXParser). However, since stateless session beans don't have client affinity like stateful session beans, the use cases are admittedly relatively rare, and the servlet programming model seems to have shown that this level of protection is probably not necessary, so if you don't need the thread safety then as of EJB 3.1, you can use a singleton session bean with bean-managed concurrency.

The thread safety restriction also gives some additional freedom to the EJB container for optimization. For example, if the EJB container knows that only one thread at a time can be using a stateless session bean, then it can manipulate the state of injected objects at method entry/exit so that those objects don't need use ThreadLocal (e.g., UserTransaction or SessionContext).

Question:

I'm trying to figure out the best way to define Remote EJB 3 beans in Spring 4.x using JavaConfig (annotation-based configuration).

I've looked at the Spring Docs for <jee:remote-slsb> and have hacked together a functional configuration, but it is terrible:

@Bean
public LoginManager getLoginManager(){
    SimpleRemoteStatelessSessionProxyFactoryBean factory = new SimpleRemoteStatelessSessionProxyFactoryBean();
    String beanName = "jndi.ejb3.LoginManager";
    factory.setJndiName(beanName);
    factory.setBusinessInterface(LoginManager.class);
    Properties p = new Properties();
    p.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory" );
    p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" );
    p.setProperty("java.naming.provider.url", "jnp:localhost:1099");
    factory.setJndiEnvironment(p);
    try {
        factory.afterPropertiesSet();
    } catch (NamingException e1) {
        e1.printStackTrace();
    }
    return (LoginManager) factory.getObject();
}

I shouldn't be calling afterPropertiesSet() in the bean definition, and I would have expected that the getObject() should be automatically called by Spring. Furthermore, it also means defining the factory for every Remote EJB that I want to load, which doesn't seem right. I would have liked/expected a way I could define a reusable factory and just pass it the Interface/JNDI name for each bean creation, but that doesn't work.

The spring docs indicate:

Also, with @Bean methods, you will typically choose to use programmatic JNDI lookups: either using Spring’s JndiTemplate/JndiLocatorDelegate helpers or straight JNDI InitialContext usage, but not the JndiObjectFactoryBean variant which would force you to declare the return type as the FactoryBean type instead of the actual target type, making it harder to use for cross-reference calls in other @Bean methods that intend to refer to the provided resource here.

So now I'm confused what to do.

The EJB Specific spring docs also recommend using the SimpleRemoteStatelessSessionProxyFactoryBean:

Defining explicit <jee:local-slsb> / <jee:remote-slsb> lookups simply provides consistent and more explicit EJB access configuration.

So how do I do this cleanly?


Answer:

You don't need to call explicity the afterProperties method since that's part of spring bean lifecycle. Additionally if you declare the bean as a factory bean, spring will automatically use the getObject to get the real object when it is needed. Here is the modified code

    @Bean
public FactoryBean getLoginManagerFactory(){
    SimpleRemoteStatelessSessionProxyFactoryBean factory = new SimpleRemoteStatelessSessionProxyFactoryBean();
    String beanName = "jndi.ejb3.LoginManager";
    factory.setJndiName(beanName);
    factory.setBusinessInterface(LoginManager.class);
    Properties p = new Properties();
    p.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory" );
    p.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces" );
    p.setProperty("java.naming.provider.url", "jnp:localhost:1099");
    factory.setJndiEnvironment(p);
return factory;
}

Question:

I am upgrading an application from Java7 on Glassfish 3.1.2.2, to Java8 on Glassfish 4.1. The application is packaged as an ear file, containing a jar-file with remote EJBs and Spring beans, as well as a war-file with a couple of servlets and some webservices.

There are only some minor changes done to the actual application, compared to how it was with Glassfish 3.x, the total changes made are:

  • Built with Java8 instead of Java7.
  • Deployed on Glassfish 4.1 instead of 3.1.2.2
  • Newer version of Hibernate
  • Newer version of ActiveMQ (client)

I can't see any difference between the previous ear-file and the new one (except the abovementioned lib-jars), but still when I try to deploy I get errors like this for all my EJBs:

org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type SomethingLogic with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private com.my.application.server.service.SomethingServiceSession.somethingLogic

Where SomethingService is an EJB, SomethingLogic is a Spring bean.

My EJBs are defined like this:

@Stateless
@RolesAllowed("secure")
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class SomethingServiceSession implements SomethingService {

    @Inject
    private SomethingLogic somethingLogic; //Spring bean

SomethingLogic is an interface.

I have a beanRefContext.xml containing:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="beanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
    <constructor-arg value="classpath*:applicationContext-glassfish.xml"/>
  </bean>

</beans>

The EJB services are defined in glassfish-ejb-jar.xml, like this:

<ejb>
  <ejb-name>SomethingServiceSession</ejb-name>
  <ior-security-config>
    <as-context>
      <auth-method>USERNAME_PASSWORD</auth-method>
      <realm>AD</realm>
      <required>true</required>
    </as-context>
  </ior-security-config>
</ejb>

I have tried adding a beans.xml with the following content to the resources\META-INF folder of my EJB-project:

<?xml version="1.0"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd" />

No luck.

I have also tried adding @LocalBean to all my EJBs, also with no luck.

In addition, I have tried to disable CDI all together, using this command:

asadmin set configs.config.server-config.cdi-service.enable-implicit-cdi=false

This actually allows my EJB services to be deployed, but I'm suspecting that it has the not so good side effect of my webservices not being discovered, so I want to be able to deploy with CDI enabled to rule this out.

Update:

I have tried changing @Inject with @Autowired in all my non-Spring-handled classes (EJBs, webservices and servlets). Now I get the same error for actual Spring beans. It seems that Glassfish will try to look for EJB beans when encountering @Inject, no matter where they occur.


Answer:

Fixed my problem by adding bean-discovery-mode="none" to my various beans.xml files.

    <?xml version="1.0"?>
    <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_2.xsd"
    bean-discovery-mode="none" />

Question:

I have a situation to integrate two independent systems. One is using J2EE,EJBs and other is Spring based. Now the problem is both the systems can call methods of each other and i want to manage transactions as well. I am not sure how to coordinate both applications transaction managers as both are using different one (Spring and EJB).

Any one has an idea how to do this ?


Answer:

First of all, both applications should use Extended transactions (XA transactions) on DataSource level (and for other resources such as Message Queues, JCA adapters or whatever resources you use in application).

Method calls should be done through Remote EJB call or web services that use WS-AtomicTransaction to handle transaction boundaries. If you do not want to change your architecture to add Remote EJBs, your best bet is to use web services with WS-AT that use XA transactions under the hood.

Question:

EJB stateless beans are thread safe in the sense that they can be accessed by many clients at the same time without concerns, in fact each client will be served by a different instance.

On the client side we use proxy references to a particular stateless EJB. Are EJB proxy/remote stubs also thread safe to call?

I mean, if my Spring controller is Autowired with an EJB, its methods will be called possibly by many threads at a time.

Is it all thread safe?


Answer:

I asked a similar question about RMI/JRMP stubs some years ago on the RMI mailing list (defunct). The answer from Sun was that there is a presumption that a JDK class is to be considered thread-safe unless the Javadoc states to the contrary.

So assuming that still holds good, which it seems to, the answer is 'yes'. And thinking about client-side connection management, the answer must also be 'yes'.

Question:


Answer:

If it's stateless, making it a singleton or creating a new instance every time you want an instance won't make any significant difference. Using any of those 3 approaches makes it even harder to unit test your code.

If DI is completely out of the question, you could use the poor man's dependency injection to at least make your code testable:

public class SomeService {

    private SomeDao someDao;

    /**
     * Constructor used in production. Creates or looks up its own DAO
     */
    public SomeService() {
        this.someDao = new SomeDao();
    }

    /**
     * Constructor used by unit tests, which can pass a mock DAO instance.
     */
    public SomeService(SomeDao someDao) {
        this.someDao = someDao;
    }

    ...
}

Question:

Can we avoid the static data ref. in the EJB proxy object that we get at the client using rmi call.

I am using the spring Di for getting ejb object and can see lots of Static ref. in my object.

Also i am calling from local only but doing a remote call.

I have checked for the same on the net but not found much. can anyone advice here..

Thanks in advance !!! :)


Answer:

Short answer: No.

Long answer: EJB proxies are container-generated remote proxies, you should interact with their interfaces, not their implementations. The actual implementation is vendor-specific, server-generated code which deals with all the complexity of EJB services. It should only be of interest if you want to know how the container itself works.

On using remote interfaces: It's fine to use remote connections to EJBs which are in fact hosted locally. This makes your application more extensible, as it doesn't make local hosting a dependency. It does add an overhead to calls, so there is a performance cost to this, but it's not that large.

Question:

I have configured a Spring Boot project with connection to Keycloak, and also I want to install a Custom SPI User Provider external to Keycloak.

I did all the steps to do the Provider and ProviderFactory, and also the file in META-INF/services, and now Wildfly fails with,

14:20:43,656 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-2) MSC000001: Failed to start service jboss.deployment.unit."focusoc-0.0.1-SNAPSHOT.jar".POST_MODULE: org.jboss.msc.service.StartException in service jboss.deployment.unit."focusoc-0.0.1-SNAPSHOT.jar".POST_MODULE: WFLYSRV0153: Failed to process phase POST_MODULE of deployment "focusoc-0.0.1-SNAPSHOT.jar"
    at org.jboss.as.server@9.0.2.Final//org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:183)
    at org.jboss.msc@1.4.8.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1737)
    at org.jboss.msc@1.4.8.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1699)
    at org.jboss.msc@1.4.8.Final//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1557)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.util.ServiceConfigurationError: org.keycloak.storage.UserStorageProviderFactory: Provider gcs.fds.focusoc.keycloak.spi.LoginStorageProviderFactory not found
    at java.base/java.util.ServiceLoader.fail(ServiceLoader.java:588)
    at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass(ServiceLoader.java:1211)
    at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1220)
    at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1264)
    at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1299)
    at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1384)
    at org.keycloak.keycloak-services@7.0.0//org.keycloak.provider.DefaultProviderLoader.load(DefaultProviderLoader.java:60)
    at org.keycloak.keycloak-services@7.0.0//org.keycloak.provider.ProviderManager.load(ProviderManager.java:92)
    at org.keycloak.keycloak-services@7.0.0//org.keycloak.services.DefaultKeycloakSessionFactory.loadFactories(DefaultKeycloakSessionFactory.java:214)
    at org.keycloak.keycloak-services@7.0.0//org.keycloak.services.DefaultKeycloakSessionFactory.deploy(DefaultKeycloakSessionFactory.java:115)
    at org.keycloak.keycloak-services@7.0.0//org.keycloak.provider.ProviderManagerRegistry.deploy(ProviderManagerRegistry.java:42)
    at org.keycloak.keycloak-wildfly-server-subsystem@7.0.0//org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor.deploy(KeycloakProviderDeploymentProcessor.java:55)
    at org.jboss.as.server@9.0.2.Final//org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:176)
    ... 8 more

14:20:43,657 ERROR [org.jboss.as.controller.management-operation] (management-handler-thread - 4) WFLYCTL0013: Operation ("add") failed - address: ([("deployment" => "focusoc-0.0.1-SNAPSHOT.jar")]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"focusoc-0.0.1-SNAPSHOT.jar\".POST_MODULE" => "WFLYSRV0153: Failed to process phase POST_MODULE of deployment \"focusoc-0.0.1-SNAPSHOT.jar\"
Caused by: java.util.ServiceConfigurationError: org.keycloak.storage.UserStorageProviderFactory: Provider gcs.fds.focusoc.keycloak.spi.LoginStorageProviderFactory not found"}}
14:20:43,658 ERROR [org.jboss.as.server] (management-handler-thread - 4) WFLYSRV0021: Deploy of deployment "focusoc-0.0.1-SNAPSHOT.jar" was rolled back with the following failure message:
{"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"focusoc-0.0.1-SNAPSHOT.jar\".POST_MODULE" => "WFLYSRV0153: Failed to process phase POST_MODULE of deployment \"focusoc-0.0.1-SNAPSHOT.jar\"
Caused by: java.util.ServiceConfigurationError: org.keycloak.storage.UserStorageProviderFactory: Provider gcs.fds.focusoc.keycloak.spi.LoginStorageProviderFactory not found"}}

Anyone knows what it is happening?

Edit:

After differents modifications (adding the persistence.xml, jboss-deployment-structure.xml, ...) now I still have the next error:

12:25:47,301 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-2) MSC000001: Failed to start service jboss.deployment.unit."login-provider-focusoc.jar".POST_MODULE: org.jboss.msc.service.StartException in service jboss.deployment.unit."login-provider-focusoc.jar".POST_MODULE: WFLYSRV0153: Failed to process phase POST_MODULE of deployment "login-provider-focusoc.jar"
    at org.jboss.as.server@9.0.2.Final//org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:183)
    at org.jboss.msc@1.4.8.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1737)
    at org.jboss.msc@1.4.8.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1699)
    at org.jboss.msc@1.4.8.Final//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1557)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.3.3.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NoClassDefFoundError: Failed to link gcs/fds/focusoc/keycloak/spi/LoginStorageProvider (Module "deployment.login-provider-focusoc.jar" from Service Module Loader): org/keycloak/storage/UserStorageProvider
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1095)
    at org.jboss.modules.ModuleClassLoader.doDefineOrLoadClass(ModuleClassLoader.java:424)
    at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:555)
    at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:339)
    at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:126)
    at org.jboss.modules.Module.loadModuleClass(Module.java:731)
    at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:247)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
    at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
    at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
    at org.jboss.as.ejb3@17.0.1.Final//org.jboss.as.ejb3.deployment.processors.BusinessViewAnnotationProcessor.getEjbClass(BusinessViewAnnotationProcessor.java:238)
    at org.jboss.as.ejb3@17.0.1.Final//org.jboss.as.ejb3.deployment.processors.BusinessViewAnnotationProcessor.deploy(BusinessViewAnnotationProcessor.java:89)
    at org.jboss.as.server@9.0.2.Final//org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:176)
    ... 8 more

12:25:47,302 ERROR [org.jboss.as.controller.management-operation] (DeploymentScanner-threads - 2) WFLYCTL0013: Operation ("full-replace-deployment") failed - address: ([]) - failure description: {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"login-provider-focusoc.jar\".POST_MODULE" => "WFLYSRV0153: Failed to process phase POST_MODULE of deployment \"login-provider-focusoc.jar\"
Caused by: java.lang.NoClassDefFoundError: Failed to link gcs/fds/focusoc/keycloak/spi/LoginStorageProvider (Module \"deployment.login-provider-focusoc.jar\" from Service Module Loader): org/keycloak/storage/UserStorageProvider"}}

Anyone knows what it is happeing? Or it's happenned to anyone else?


Answer:

I solved it doing the manual installation with the command explained in the documentation and adding the provider in the standalone.

Question:

I have a maven project that implements apache cxf web service and a ejb object, I'm trying inject the ejb instance using spring, when I run the program, the spring container returns the ejb bean as a null. I don't understand how to relate the ejb implementation with the spring bean.

The project has a apache cxf implementation, this way:

ServiceBindingImpl

@WebService(endpointInterface = "cl.flying.binding.ServiceBinding")
public class ServiceBindingImpl implements ServiceBinding { 
    private ServiceBusinessLocal serviceEjb
    public void setServiceEjb(ServiceBusinessLocal serviceEjb) {
        this.serviceEjb = serviceEjb
    }
    public String sayHello(String request) {
        return serviceEjb.sayHello(request);
    }
}

It also has a spring configuration in order to achieve DI, applicationContext.xml

<bean id="serviceEjb"
    class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean" scope="request">
    <property name="jndiName" value="ejb/ServiceBusinessImpl" />
    <property name="businessInterface" value="cl.service.business.ServiceBusinessLocal" />
    <property name="resourceRef" value="true" />
</bean>
<bean id="ServiceController" class="cl.flying.binding.ServiceBindingImpl">
    <property name="serviceEjb" ref="serviceEjb" />
</bean>

That is the business tier, Stateless ejb implementation:

@Stateless(mappedName="ejb/ServiceBusiness")
public class ServiceBusinessImpl implements ServiceBusinessLocal
    public String sayHello(String request) {
        return "hello: " + request;
    }
}

How can I instantiate the ejb object?


Answer:

Here is the bean configuration file I used as part POC sometime back

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <jee:remote-slsb id="calculatorRemote" business-interface="com.kp.swasthik.remote.CalculatorRemote" jndi-name="java:kp-ejb/Calculator!com.kp.swasthik.remote.CalculatorRemote" lookup-home-on-startup="true" >
        <jee:environment>
             java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
             java.naming.provider.url=http-remoting://localhost:8080
             jboss.naming.client.ejb.context=true
             java.naming.security.principal=username
             java.naming.security.credentials=password
        </jee:environment>
    </jee:remote-slsb>

    <context:component-scan base-package="com.kp.swasthik.jaxws"></context:component-scan>

    <util:properties id="remoteRef">
        <prop key="java.naming.factory.initial">org.jboss.naming.remote.client.InitialContextFactory</prop>
        <prop key="java.naming.provider.url">remote://localhost:4447</prop>
        <prop key="java.naming.factory.url.pkgs">org.jboss.ejb.client.naming</prop>
    </util:properties>

</beans>

And injecting the bean as shown below.

@Service
@WebService
public class KPService {

    @Autowired
    CalculatorRemote calc;

    @WebMethod
    public int add(int a, int b){
        return calc.add(a, b);
    }


    @WebMethod
    public Result subtract(Input num){
         return calc.subtract(num);
    }

}

NOTE: The above configuration is for Wildfly/JBOSS EAP, you might need to change accordingly to your app server

Question:

I have working code that saves entity to DB with using EJB+JPA+Hibernate. Now I need to change EJB to Spring.

Below is my simplified manager class.

//@Stateless - changed to @Service
@Service
public class Manager {

    //@EJB - changed to Autowired
    @Autowired
    private ClientDao clientDao;

    public void addClient(Client client) {
        clientDao.addClient(client);
    }
}

Below is my DAO class.

//@Stateless - changed to @Repository
@Repository
public class JpaClientDao implements ClientDao {

    @PersistenceContext(unitName="ClientsService")
    private EntityManager em;

    public void addClient(Client client) {
        em.persist(client);
    }
}

Below is persistence.xml.

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
                               http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
         version="2.0">

  <persistence-unit name="ClientsService" transaction-type = "JTA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <jta-data-source>myJtaDatabase</jta-data-source>

    <class>com.entity.Client</class>

    <properties>
      <property name="hibernate.archive.autodetection" value="class"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hbm2ddl.auto" value="update"/>
    </properties>
  </persistence-unit>
</persistence>

applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:component-scan base-package="com" />

    <context:annotation-config/>
</beans>

resoures.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <Resource id="myJtaDatabase" type="DataSource">
        JdbcDriver org.apache.derby.jdbc.ClientDriver
        JdbcUrl jdbc:derby://localhost:1527/C:/ClientDB
        UserName test
        Password 123456
        validationQuery = SELECT 1
        JtaManaged true
    </Resource>
</resources>

Questions. 1) When I used EJB, I had Container-Managed Transactions. Who should manage transactions with using Spring? 2) Do I need to necessarily use Spring Framework transaction management? Is there any alternatives? I found some examples like this http://www.codingpedia.org/ama/java-persistence-example-with-spring-jpa2-and-hibernate/, and I cant undestand is code below spring specific or it suits for me.

    <!-- ************ JPA configuration *********** -->
    <tx:annotation-driven transaction-manager="transactionManager" />  
    <bean id="transactionManager"   class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean id="entityManagerFactory"   class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation"     value="classpath:config/persistence-demo.xml" />
        <property name="dataSource" ref="restDemoDS" />
        <property name="packagesToScan" value="org.codingpedia.demo.*" />
        <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
        </bean>
    </property>
</bean> 

3) Do I need to edit Java code or should my steps be in xml configuration?

Any useful links are encouraged.


Answer:

1) When I used EJB, I had Container-Managed Transactions. Who should manage transactions with using Spring?

Answer : Spring provides container managed transaction support too (See JTA transaction manager , sample oracle article) and also application managed transaction (which means your application can declaratively/programatically manage the transactions with minimal effort by using Spring transaction API ) . See Spring transaction doc

2) Do I need to necessarily use Spring Framework transaction management? Is there any alternatives?

Answer: If spring framework is not managing your transactions, then your container will need to manage them..you have alternatives of any Java EE JTA implementations like opensource JBossTS or enterprise JTA implementations Oracle-WebLogicJtaTransactionManager or IBM-WebSphereUowTransactionManager, you can find how to use them in the same point 1 spring documentation. You can even have your own transaction manager implemented.

But if you are already using Spring framework why not benefit from spring transaction management with plenty of configurations possible, (Spring + Hibernate transaction manager, Spring + JPAContainerManager, Spring+ JTAContainerManager etc..)

3) Do I need to edit Java code or should my steps be in xml configuration?

Answer: Your transcation manager xml config seems fine to use a JpaTransactionManager, now you can initiate transactions in your service layer java code by annotating @Transactional which typically handles your service methods to participate in a transaction according to the configured transaction manager.

@Service
@org.springframework.transaction.annotation.Transactional
public class Manager {

Question:

In my application we have the following architecture:

  • Web Layer: JSF+Rich Faces
  • Service Layer: EJB
  • DAO Layer: EJB Classes [consist of JDBC Queries]

We want to use Hibernate as ORM framework instead of JDBC in DAO layer. I want to use Spring ORM feature for integrating Hibernate in DAO layer. Now, challenges that we are facing:

DAO layer classes are stateless EJB classes. So,to use Spring DI inside EJB classes, I have to go for an interceptor as follows:

@Stateless(mappedName = "myAppDao")
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class MyAppDaoImpl implements MyAppDaoRemote {

@Autowired
private SessionFactory sessionFactory;

@Override
public void getSession() {
     if(sessionFactory!=null){
        Session session = null;
        try{
            session = sessionFactory.getCurrentSession();
        }catch (Exception e) {
            session = sessionFactory.openSession();
            System.out.println("Exception:"+e.getMessage()); 
        }
}

With sessionFactory.getCurrentSession() I am getting this exception:

Exception:Could not obtain transaction-synchronized Session for current thread.

The reason behind this I am not able to use Spring transaction at Service layer since I have EJB classes in my service layer.

Moreover I don't want to use sessionFactory.openSession() as in that case I would have to manually close the session.

These are my Spring configuration files:

1) beanRefContext.xml -> Used by Interceptors

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">

     <bean id="businessBeanFctory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
          <constructor-arg value="classpath*:daoConfig.xml" />
     </bean>
 </beans>

2) daoConfig.xml -> Hibernate Configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:p="http://www.springframework.org/schema/p"
   xmlns:jee="http://www.springframework.org/schema/jee"
   xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="http://www.springframework.org/schema/jee    http://www.springframework.org/schema/jee/spring-jee-4.2.xsd
                    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                    http://www.springframework.org/schema/tx 
                    http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">

  <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
      <property name="dataSource" ref="dataSource"/>
      <property name="packagesToScan" value="com.myapp.model"/>
      <property name="hibernateProperties">
         <props>
            <prop  key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
            <prop key="hibernate.show_sql">false</prop>
          </props>
        </property>
    </bean>

   <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MyAppDS" expected-type="javax.sql.DataSource"/>

   <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>    
   <tx:annotation-driven transaction-manager="transactionManager" />

   </beans>

I know that there is no point in using EJB along with Spring since they are alternate to each other.But due to some restriction at project level I can't remove EJB for the time being.

So, is there any solution by which we can get the Hibernate current session inside an EJB class through Spring?


Answer:

If you're using JBossAS/WildFly then you just need:

@Stateless(mappedName = "myAppDao")
public class MyAppDaoImpl implements MyAppDaoRemote {

    @PersistenceContext
    private Session session;

    public void someDaoMethod(YourEntity e) {
        // use session directly
        // Transactions are automatically managed by the EJB container
        // because that's one of EJB's raison d'être
    }

}

Ensure that your (traditional) Hibernate configuration specifies JTA transactions.

See the WildFly JPA Reference Guide for more information.

If you're not using one of these JavaEE implementations then you can still use the Spring configuration that you have, but remember to include:

 <property name="jtaTransactionManager" value="transactionManager"/>

in your sessionFactory bean.

Additionally, if you're using WebLogic or WebSphere you may need to specify platform dependent JTA transaction managers for these servers, such as WebLogicJtaTransactionManager or org.springframework.transaction.jta.WebSphereUowTransactionManager.