MavenSpringEJBsOnGlassfish

EJB development for Glassfish using Maven2 and Spring

Contributed By; Kristian Rink


[[{TableOfContentsTitle=TableOfContents} | {TableOfContents title='Table of Contents'}]]

Purpose, scope and requirements


So far quite some of the applications in our environment are wired using the Spring Framework and built making extensive use of Maven 2. In order to make migration to EJB3 as seamless and convenient as possible, in a first attempt we want some services to be exposed using both stateless session beans and JAX-WS web services to as good as possibly comply with Java EE standard technologies. But, along with this, we want to throw away as little code and configuration and tools as somewhat possible, making us go for an approach integrating all these platforms - Maven2, Spring, Glassfish and EJB3 - in a hopefully straightforward and homogenous way, and hopefully leaving it even usable with the NetBeans IDE. This document mainly is a collection of resources on the various aspects of the issue as well as a working, simple sample project built on top of that.

Tested with the following tool chain:

  • NetBeans IDE 6.5 nightly builds and the corresponding Maven2 tooling
  • Glassfish V2U2 application server
  • Spring Framework 2.5.5
  • SLF4J Logging Framework

Other requirements:

  • The reader should be familiar with the technologies mentioned above (especially the maven2 tooling in NetBeans). This documentation mainly aims at more advanced users trying to get through this very use case.


Sample application


Our sample application to outline our solution basically is rather straightforward:

  • A simple class (
    SampleBean
    ) is exposed as a Spring singleton.
  • A simple EJB (
    FooBean
    ) recieves and uses this bean via Dependency Injection.
  • FooBean
    itself is exposed as a JAX-WS web service using the {@WebService} annotation.
  • A common logging configuration should capture output from classes relying upon different logging implementations in a homogenous way to allow dumping log messages to the default Glassfish
    server.log
    .

The full example is attached to this tutorial so looking around there should be easy.


Tasks and solutions


In course of doing this kind of development, a bunch of issues have bugged us so we got most of them resolved in one way or the other. Most of the solutions outlined here are possibly a little clumsy, yet they do provide a working code foundation for similar projects. Corrections and additions to that are very welcome. In order to keep things short, I abstain from including screenshots and just use minor code snippets wherever it seems necessary. Please, in all cases, refer to the sample code for the full configuration files and code.


Maven2 and EJB3


Building an "ejb" packaged Maven2 artifact, to begin with, is pretty straightforward an can easily be accomplished following the instructions outlined in resource (3). In addition to that, our sample code just differs in two points, looking at the
pom.xml
:
 <dependencies>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-ejb_3.0_spec</artifactId>
            <version>1.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-interceptor_3.0_spec</artifactId>
            <version>1.0.1</version>
            <scope>provided</scope>
        </dependency>
	[...]
 </dependencies>
  • At first, we also include the
    geronimo-interceptor_3.0_spec
    as we want to make use of the {@Interceptors} annotation to have our EJBs configured / wired up using Spring.
  • Second thing is using a
    provided
    scope as we are about to get our EJB module packaged with its dependencies later on and we don't want to have the Geronimo Java EE specs deployed to the Glassfish server along with our code.


Including dependencies

Building an EJB module containing dependent jars using plain NetBeans / ant tooling is likely to result in all the required .jars being packaged to the EJB jar automatically. In Maven, this kind of packaging doesn't happen out of the box so we needed some kind of extension to emulate this behaviour. Similar to another maven2 project sharing the same requirements about dependency inclusion, we chose to use the assembly:assembly Plugin for Maven2 in this case.
pom.xml
configuration snippet for that:
    <build>
	[...]
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
        <finalName>spring-ejb</finalName>
    </build>
Invoking
mvn assembly:assembly
on the project now will result in the .jar file {target/spring-ejb-with-dependencies.jar} being created which, opposed to target/spring-ejb.jar (the artifact created and installed to your local repository running mvn install), does contain all the content provided by any of the dependencies of the project. This file, by then, easily could be deployed to an application server like Glassfish.


Logging

Dealing with logging initially was one of the most uncomfortable things when trying to move to Glassfish: In our tomcat environment we were making use of log4j exclusively, some third-party libraries (like Spring) use commons-logging, and in Glassfish, we just wanted all the logging output to

  • ... simply go to the
    server.log
    output file of the application server instance and
  • ... be configurable in terms of log level settings using the Glassfish administration console, thus eliminating the need to include something like a custom (Spring-exposed) MBean to remotely (via JMX) set log levels in case of debugging.
In order to solve this we finally got SLF4J integrated into our applications which does exactly what we need here, adding the following entries to
pom.xml
:
	[...]
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.5.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl104-over-slf4j</artifactId>
            <version>1.5.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>1.5.0</version>
            <scope>compile</scope>
        </dependency>
	[...]

  • slf4j-jdk14
    provides a logging adapter to output messages using the standard JDK logging facility which seems the right thing to do in Glassfish.
  • jcl104-over-slf4j
    implements the commons-logging API, thus "redirecting" commons-logging dependent log output to the SLF4J configured logging adapter.
  • log4j-over-slf4j
    does the same for classes depending upon log4j.
Only thing left to mention here is to, in
pom.xml
, make sure the "real" implementations of commons-logging and log4j are exclused in case any packages (like Spring) depend upon them:
	[...]
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>2.5.5</version>
            <scope>compile</scope>
            <exclusions>
                <exclusion>
                    <artifactId>commons-logging</artifactId>
                    <groupId>commons-logging</groupId>
                </exclusion>
            </exclusions>
        </dependency>
	[...]


Spring Wiring

Getting Spring to wire up EJB3 components is quite straightforward as well - once you know how to get it done. In most situations, we were so far using Spring contexts in .war applications, with the context being started when the webapp got deployed. In EJB3 modules, there ain't such mechanisms. However, making use of annotations provided in Spring 2.5 and EJB 3, getting a Spring-defined bean into an EJB couldn't be more simple:

[...]
@Stateless
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class FooBean implements FooLocal {

    @Autowired
    private SampleBean sampleBean;
[...]
The Spring documentation on these issues surely could be more verbose names a few things how to extend and customize this mechanism, however in the most "common" use case, to make use of this approach a
beanRefContext.xml
file needs to be defined containing a definition of an implementation of the Spring BeanFactory interface, like, for example, the {ClassPathXmlApplicationContext}:
     [...]
    <bean id="factoryKey" class="org.springframework.context.support.ClassPathXmlApplicationContext">
        <constructor-arg value="application.xml"/>
    </bean>
     [...]
Configured this way, the beans to be exposed via Spring (and subsequently used within EJB classes) go to the
application.xml
context configuration:
     [...]
     <bean id="sampleBean" class="de.planconnect.ejb.test.samples.SampleBean"/>
     [...]
Both files need to be found on the application classpath at runtime, putting them right into
main/resources/
in the maven2 project structure has proven to work rather well.

Deployment

Even while deploying web applications to Glassfish using Maven2, we have quite a while ago adopted Wouter van Reevens hints on that, relying upon the
exec:exec
plugin provided by Maven2, to be configured in {pom.xml}:
    [...]
    <profiles>
        <profile>
            <id>production</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>exec-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>exec</goal>
                                </goals>
                                <phase>deploy</phase>
                            </execution>
                        </executions>
                        <configuration>
                            <executable>/opt/appsrv/glassfish-v2/bin/asadmin</executable>
                            <arguments>
                                <argument>deploy</argument>
                                <argument>--precompilejsp</argument>
                                <argument>--user=admin</argument>
                                <argument>--passwordfile=${user.home}/.asadminpass</argument>
                                <argument>--host=appserver</argument>
                                <argument>--port=4848</argument>
                                <argument>target/spring-ejb-jar-with-dependencies.jar</argument>
                            </arguments>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
        <profile>
            <id>testing</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>exec-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>exec</goal>
                                </goals>
                                <phase>deploy</phase>
                            </execution>
                        </executions>
                        <configuration>
                            <executable>/opt/appsrv/glassfish-v2/bin/asadmin</executable>
                            <arguments>
                                <argument>deploy</argument>
                                <argument>--precompilejsp</argument>
                                <argument>--user=admin</argument>
                                <argument>--passwordfile=${user.home}/.asadminpass</argument>
                                <argument>--host=testserver</argument>
                                <argument>--port=4848</argument>
                                <argument>target/spring-ejb-jar-with-dependencies.jar</argument>
                            </arguments>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
    [...]

We do have two different profiles aiming at two different servers, one being our internal testbed / staging system, the other one our production environment. There are two caveats to take care of, here:

  • First of all, as we need to deploy the EJB module along with its dependencies, we also need to specify the "right" jar file to de deployed by
    asadmin
    ( {<argument>target/spring-ejb-jar-with-dependencies.jar</argument>}).
  • Second, and maybe a little more important: Talking about the jar-with-dependencies, of course
    assembly:assembly
    has to be invoked before actually deploying the module to make sure the full jar file is built.

Done so, the module can be deployed to the servers using

  • mvn -Ptesting clean assembly:assembly exec:exec
    ... to the test system and
  • mvn -Pproduction clean assembly:assembly exec:exec
    ... to the production environment.

Web Service and Testing

We made the
FooBean
an annotated web service in order to be capable of testing this facility easily from within the Glassfish administration console. To do so, the JAX-WS libraries need to be added to the {pom.xml}:
	[...]
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
            <version>2.1EA1</version>
            <scope>provided</scope>
        </dependency>
	[...]
By then the
@WebService
/ {@WebMethod} annotations were usable in the EJB code and the web service got deployed and started well.

Notes, conclusions and caveats


After extracting the sample code attached ( ejb3-spring_MavenSpringEJBsOnGlassfish.tar.gz), care should be taken in order to configure the deployment profiles according to your local settings (regarding the path configuration of
asadmin
, the server host name and login credentials) before the application might be built and deployed without further modifications. Though, as pointed out, some (all?) of the approaches provided in here possibly could be beautified, this might serve as a working skeleton for building Spring-enabled, maven-managed EJB3 modules. Things we discovered in course of getting here:
  • Though the Maven2 integration in NetBeans is exceptional, integration of Java EE technologies (in this case most notably EJB3 and JAX-WS / Web Services) could still be improved to more tightly integrate with the IDE. Please see Issue 129053 for further information and discussion.
  • Generally, adding Java EE jars off the Apache Geronimo project feels a little strange while using another open-source application server so hopefully one day the Glassfish Java EE specs (or maybe even vendor-independent artifacts?) will be available in the Maven2 repositories.
  • Once started however, working with this kind of projects is pretty pleasant even while the initial configuration is a little more "rough". For internal purposes we are about to provide this module as a Maven2 archetype to allow for easy creation of similar projects.


Resources


Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo