I'm in an Maven EJB Module-Project in Netbeans using:
- Netbeans 7.0
- GlassFish Server Open Source Edition 3.1 (43)
- maven 3.0.3 (bundled)
The module is just a scaffold consisting of:
- A JPA entity bean (provider: Eclipselink 2.0.2, jdbc: Derby 10.6.2.1 client)
- An EJB implementing CRUD on this bean
- A remote interface for the EJB
- A web service
My goal is to test the EJB with the EJB 3.1 embedded container
EJBContainer.createEJBContainer()
First of all, when you generate an unit test for the EJB in Netbeans and use the provided quick fix for missing EJBContainer, it modifies the pom.xml to use the so-called Glassfish static shell for the embedded container.
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-static-shell</artifactId>
<version>3.1</version>
<scope>system</scope>
<systemPath>${glassfish.embedded-static-shell.jar}</systemPath>
</dependency>
It then sets the property ${glassfish.embedded-static-shell.jar} to a Glassfish installation of your choice, for example the bundled one. The jar file contains nothing but classpath entries for the jars needed for Glassfish embedded. This is suboptimal, as the build is now dependent on a local installation in the right version, and is not portable due to the hard-code path in the property. When using Hudson or similar CI, having to have a Glassfish installation on your build server is sometimes not preferable.
Alternatively, you can specify to download an embedded glassfish installation with the following pom.xml snippet:
<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1</version>
<scope>test</scope>
</dependency>
Warning: This is a big download, around 70 MB.
It works out of the box for simple EJB, but I still had a few issues in my case:
- As soon as you put a remote interface in your bean, the embedded glassfish container starts a CORBA IIOP listener on default port 3700, resulting in a BindException: Address already in use if there is another instance of glassfish running
- As soon as you put a JAX-WS web service in your bean, the embedded container throws a java.lang.NullPointerException at java.util.PropertyResourceBundle.handleGetObject(PropertyResourceBundle.java:136)
- The embedded container can't find your JTA jdbc resources referenced in persistence.xml. I also wanted to use a different persistence.xml using an embedded Derby database and using drop-and-create for eclipselink.ddl-generation
To solve issue 1, I needed to use a different domain.xml than the default one for the embedded container to change the port numbers. Pointing the embedded container to a different domain.xml by putting the property org.glassfish.ejb.embedded.glassfish.configuration.file in a map and passing it to EJBContainer.createEJBContainer() as indicated by the Oracle GlassFish Server 3.1 Embedded Server Guide didn't work for me. Instead i had to use the org.glassfish.ejb.embedded.glassfish.instance.root, and point it to a glassfish domain directory. I have created it under src/test/resources/testing-domain. The following files are needed:
- config/admin-keyfile
- config/cacerts.jks
- config/domain.xml
- config/keyfile
- config/keystore.jks
- config/login.conf
- config/server.policy
To solve issue 2, I had to put the property org.glassfish.ejb.embedded.glassfish.web.http.port when calling EJBContainer.createEJBContainer(props);
The name of the property is a bit misleading, as the value is ignored completely and can be an empty String. It just means that the embedded glassfish instance should start the http listeners (just putting them as enabled="true" in domain.xml wasn't enough).
To solve issue 3, I created an additional persistence.xml and put it to src/test/resources/META-INF:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
<persistence-unit name="bookstore-ejb" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property
name="eclipselink.target-database"
value="DERBY" />
<property
name="eclipselink.ddl-generation"
value="drop-and-create-tables" />
<property
name="eclipselink.logging.level"
value="FINE" />
<property
name="javax.persistence.jdbc.driver"
value="org.apache.derby.jdbc.EmbeddedDriver" />
<property
name="javax.persistence.jdbc.url"
value="jdbc:derby:memory:bookstore-ejb;create=true;" />
<property
name="javax.persistence.jdbc.user"
value="APP" />
<property
name="javax.persistence.jdbc.password"
value="APP" />
</properties>
</persistence-unit>
</persistence>
Next, I copy target/classes to target/embedded-classes and afterwards copy target/test-classes to target/embedded-classes to overwrite my production META-INF/persistence.xml with the maven-resources-plugin:
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>copy-classes-to-embedded</id>
<phase>compile</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/embedded-classes</outputDirectory>
<overwrite>true</overwrite>
<resources>
<resource>
<directory>target/classes</directory>
</resource>
<resource>
<directory>src/test/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Then, I point the EJBContainer.MODULES property to target/embedded-classes.
We can now write our unit-test like this:
public class BookEJBTest {
private static EJBContainer ec=null;
private static Context ctx=null;
public BookEJBTest() {
}
@BeforeClass
public static void initContainer() throws Exception {
Map<String, Object> props=new HashMap<String, Object>();
props.put(EJBContainer.MODULES, new File("target/embedded-classes"));
props.put("org.glassfish.ejb.embedded.glassfish.instance.root","./src/test/testing-domain");
props.put("org.glassfish.ejb.embedded.glassfish.web.http.port","");
ec = EJBContainer.createEJBContainer(props);
ctx = ec.getContext();
}
@AfterClass
public static void closeContainer() throws Exception {
if(ctx!=null)
ctx.close();
if(ec!=null)
ec.close();
}
@Test
public void shouldCreateABook() throws Exception {
Book book = new Book();
book.setTitle("The Hitchhiker's Guide to the Galaxy");
book.setPrice(12.5F);
book.setDescription("Scifi book created by Douglas Adams");
book.setIsbn("1-84023-742-2");
book.setNbOfPages(354);
book.setIllustrations(false);
BookEJB bookEJB = (BookEJB) ctx.lookup("java:global/embedded-classes/BookEJB!de.familienservice.bookstoreejb.BookEJB");
book = bookEJB.createBook(book);
assertNotNull("ID should not be null", book.getId());
}
}
The test runs with mvn test as well as with the embedded test runner in Netbeans 7.0. The build is portable und should run in my Hudson now.
Note that I didn't create a test for the webservice yet. The URL the webservice binds to is always http://localhost:28080/... though, so this should be an easy task.
FYR, I have put up the code for this project on github.
If you have any questions or tips for me, feel free to comment :-)
Edit: You have to have the glassfish maven repository in addition to the eclipselink repository in your pom.xml
<repositories>
<repository>
<url>http://ftp.ing.umu.se/mirror/eclipse/rt/eclipselink/maven.repo</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library Library[eclipselink]</name>
</repository>
<repository>
<id>glassfish-repo</id>
<url>http://download.java.net/maven/glassfish/</url>
<name>Repository for glassfish artifacts</name>
</repository>
</repositories>
I'm having this problem when i try to build the project downloaded from the page you supplied. Would you help me please?
AntwortenLöschenFailed to execute goal on project bookstore-ejb: Could not resolve dependencies for project de.familienservice:bookstore-ejb:ejb:1.0-SNAPSHOT: The following artifacts could not be resolved: org.glassfish.extras:glassfish-embedded-all:jar:3.1, org.glassfish:javax.ejb:jar:3.1: Could not find artifact org.glassfish.extras:glassfish-embedded-all:jar:3.1 in eclipselink (http://ftp.ing.umu.se/mirror/eclipse/rt/eclipselink/maven.repo) -> [Help 1]
To see the full stack trace of the errors, re-run Maven with the -e switch.
Re-run Maven using the -X switch to enable full debug logging.
For more information about the errors and possible solutions, please read the following articles:
[Help 1] http://cwiki.apache.org/confluence/display/MAVEN/DependencyResolutionException
Thanks for the hint! I had some extra repositories in my ~/.m2/settings.xml. You need to enable the glassfish maven repository in your pom in adition to the eclipselink repo. I have edited the blog posting.
AntwortenLöschenI also have updated the github project accordingly.
Hi
AntwortenLöschenreally nice post. I am out of my desk now so i had no chance to check out your code but can you provide me any reference that clearly states that when starting EjbContainer (by EJBContainer.createEJBContainer) HTTP listener handling SOAP requests is started on port 28080.
According to embedded glassfishes log the listener is started:
AntwortenLöschenINFO: Grizzly Framework 1.9.31 started in: 148ms - bound to [0.0.0.0:28080]
and my test web service is also started:
INFO: WS00019: EJB Endpoint deployed
embedded-classes listening at address at http://Euklid:28080/NewWebService/NewWebService
I didn't test with actual HTTP request though, not doing anything currently with EJBs :-)
Hi folks,
AntwortenLöschenthis example I find up to now the only one that really works with embedded Glassfish ver. 3.
DeepZajac
Hi Paul,
AntwortenLöschenThanks for the example and the sources on github.
I was extending the example with CDI, but when adding the beans.xml, I got an "java.lang.OutOfMemoryError: PermGen space".
Any idea's to get CDI working?
ps, I upgraded to 3.1.1.
Thanks,
Duncan
Hi Paul,
AntwortenLöschenthanks for your nice post. Unfortunately I still face with the problem point 2#. Setting property to empty string I get: "javax.ejb.EJBException: org.glassfish.embeddable.GlassFishException: PlainTextActionReporterFAILURENo configuration found for server.network-config.network-listeners.network-listener.http-listener". Setting it to "8080" outputs "using 8080" with same exception above.
I use glassfish-embedded-all-3.1.1-b11.jar.
Any idea?
Thanks
Vins
@Duncan: Try to start maven with increased permanent geneneration size.
AntwortenLöschenIf you run from command line, append -XX:MaxPermSize=256m like in
mvn -XX:MaxPermSize=256m test
If you run from eclipse, I think the best way is to set MAVEN_OPTS env variable.
In OSX, for example, run
export MAVEN_OPTS="-XX:MaxPermSize=256m" && open /Applications/eclipse/Eclipse.app
For windows, there is somewhere a setting for environment var, don't ask me where :-)
Sorry, just realized you can't add JVM options to mvn like this. Try
AntwortenLöschenexport MAVEN_OPTS="-XX:MaxPermSize=256m" && mvn test.
Paul,
AntwortenLöschenreally appreciate your article,
It's a nb bug that the editor doesnt recognize ${glassfish.embedded-static-shell.jar} if you set it up in MAVEN_OPTS. The editor and nb project complains with 'missconfigured project' but if you run the tests from nb or the command line it does pick up the gf location from the MAVEN_OPTS propertye, this would make the first approach installation independent.
I've opened a NB bug for this:
http://netbeans.org/bugzilla/show_bug.cgi?id=202202
I am going to try the embedded-all now.
I think this doesn't work in 3.1.1?
AntwortenLöschen>> I think this doesn't work in 3.1.1?
AntwortenLöschenWas having issues with ddl for entities not being generated with 3.1.1, sequence table was created but nothing else.
Great post btw.
Thanks, was toooo helpful.
AntwortenLöschenIf this works for someone:
I was having the same error about the iiop issue over and over again when I was trying the 3701 port. Then I changed it to 23700 and it worked just fine.
Just FYI, I'm using the embedded container glassfish-embedded-all 3.1.1 and the value for the property org.glassfish.ejb.embedded.glassfish.web.http.port can be changed there now. I had it changed in the domain.xml but in the logs, it would always say "Using 8080" until I added the port number to the map.
AntwortenLöschenIs there any way to run the tests as an authenticated user? Is it possible to use ProgrammaticLogin? A lot of my code depends on the session user.
AntwortenLöschen