This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 180767 - Embedded EJB container in tests does not work in Maven project
Summary: Embedded EJB container in tests does not work in Maven project
Status: RESOLVED FIXED
Alias: None
Product: javaee
Classification: Unclassified
Component: Maven (show other bugs)
Version: 6.x
Hardware: All All
: P2 normal (vote)
Assignee: Petr Jiricka
URL:
Keywords:
Depends on: 193368
Blocks:
  Show dependency tree
 
Reported: 2010-02-16 03:55 UTC by Petr Jiricka
Modified: 2011-01-13 18:06 UTC (History)
4 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
Proposed patch (7.66 KB, patch)
2010-03-19 20:08 UTC, Petr Jiricka
Details | Diff
A patch using the new approach (11.50 KB, patch)
2010-04-16 13:27 UTC, Petr Jiricka
Details | Diff
Hopefully final patch (32.29 KB, patch)
2010-05-06 09:26 UTC, Petr Jiricka
Details | Diff
Another revision of the patch - the previous one was missing one file. (34.96 KB, patch)
2010-05-06 11:49 UTC, Petr Jiricka
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Petr Jiricka 2010-02-16 03:55:17 UTC
1. Create a new Maven web application
2. Create a session bean and put in a simple method:
@Stateless
public class NewSessionBean {

    public String sayHello() {
        return "x";
    }

}

3. Generate a unit test for the SB using the Tools -> Create JUnit Tests action
4. Run this test

The test fails with the following exception:
Testcase: testSayHello(beans.NewSessionBeanTest):        Caused an ERROR
Absent Code attribute in method that is not native or abstract in class file javax/ejb/embeddable/EJBContainer
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/ejb/embeddable/EJBContainer
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:698)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
        at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:315)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:330)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:250)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:398)
        at beans.NewSessionBeanTest.testSayHello(NewSessionBeanTest.java:35)
Comment 1 Petr Jiricka 2010-02-16 05:41:04 UTC
The workaround is to add the following repository to the pom.xml:

        <repository>
            <id>java.net.glassfish</id>
            <name>Repository hosting the jee6 artifacts</name>
            <url>http://download.java.net/maven/glassfish</url>
        </repository>

And the following dependency:

        <dependency>
            <groupId>org.glassfish.extras</groupId>
            <artifactId>glassfish-embedded-all</artifactId>
            <version>3.0</version>
            <scope>test</scope>
        </dependency>

This has to be the first dependency, declared before the dependency on javaee-web-api.

See also: http://blogs.sun.com/alexismp/entry/testing_ejb_3_1_s
Comment 2 Petr Jiricka 2010-02-16 08:19:02 UTC
Milos, what would need to be done to fix it as the workaround above suggests? Dafe says a new version of the archetype should be created and uploaded to http://repository.codehaus.org/org/codehaus/mojo/archetypes/webapp-javaee6/, correct? Thanks.
Comment 3 Petr Jiricka 2010-02-25 06:04:41 UTC
Based on offline discussion, the right fix is to add the dependency when needed (while paying attention to the order!), rather than putting it directly to the archetype.
Comment 4 Quality Engineering 2010-03-17 05:12:57 UTC
Integrated into 'main-golden', will be available in build *201003170201* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/b2d0bd16bf23
User: pjiricka@netbeans.org
Log: #180767, #181861 - partial fix for the dependency ordering aspect. Making sure that new libraries are added before the Java EE API stub jar files, so the stub classes are overridden on the compilation classpath.
Comment 5 Petr Jiricka 2010-03-19 20:08:32 UTC
Created attachment 95458 [details]
Proposed patch

I am attaching a patch based on a suggestion by Tomas Zezula. The basic flow is that the JUnit module needs to add an entry to Maven project classpath, but:
- I can not create a library, because the jars may not exist locally (in case the server is not registered, which is a valid situation for Maven project, but not for Ant web project).
- ProjectClassPathModifier is not optimized for Maven usage, it basically assumes local files

So the idea is that Maven's implementation of ProjectClassPathModifier.addRoots would recognize also .pom URLs, and add this pom itself to the classpath. (Other implementations of PCPM.addRoots may throw an exception when receiving a .pom URL).

Then from the JUnit module, the classpath is extended through PCPM. Note that for now, the assumption that we are using GlassFish is hardcoded (GlassFish v3 is right now the only server that supports this feature). In the future, server should be queried for the correct classpath item/pom to include. In order to do that, j2eeserver module would need to add new API for this - something like J2eePlatformImpl.getToolPoms (analogous to J2eePlatformImpl.getToolClasspathEntries, but geared towards Maven). Also, the EJB-specific part would need to be extracted from the JUnit module to a specialized junit.j2ee module (junit module would need to provide a new SPI for this), so that junit.j2ee can depend on the j2eeserver module.
Comment 6 Petr Jiricka 2010-03-19 20:11:35 UTC
Milos or Dafe, can you please review? This patch makes changes to the Maven module.
Comment 7 David Konecny 2010-03-21 20:36:34 UTC
The patch looks it can do the job. I'm not sure adding pom via classpath API is good idea or not. It has certain appeal though. I wonder how it worked so far? If 3rd party module (eg. web services support) needs to add something to maven project classpath in order to enable some functionality what happens? Does it work? Does Maven translates the jar being added to classpath to some POM? Consequence of allowing adding POM files via classpath is that clients will need to be aware of whether the underlying project is maven or ant based. .... I just remember that each NB library descriptor has POM file section and that's how Maven knows what to use. Perhaps JUnit should then try to add (even fake?) NB library with right POM identifier?

Another question: adding jars for embedded EJB container is for Ant project done during project creation and for Maven it is done when first EJB test is generated. Is it desirable to be consistent? I would be tempted to answer yes. I also think that adding jars on as-needed basis is better but the problem arise when user does not use "Generate JUnit Test" wizard but write the unit test by hand. In Ant case test execution will just work because classpath was altered when project was created. For Maven user will have to fix the classpath by hand otherwise test execution will fail that 'no container is available'.
Comment 8 Petr Jiricka 2010-03-22 10:27:34 UTC
> Perhaps JUnit should then try to add (even fake?)
> NB library with right POM identifier?

I discussed this with Tomas Zezula and the thing is that there is no way to create a fake NB library - it has to be a "real" library, that will be visible in the library manager. But since the physical jar files may not be present, and if they do, they depend on the GF installation, this is not a 100% solution either. On the other hand, having such a library present (when GF is registered) would allow us to support EJB development + unit testing in a Java SE project - which is a valid use case. Maybe we can require such a library, and if it exists, we display a warning message "Without setting a target server for this Maven project, test classpath will not be setup correctly".

> Is it desirable to be consistent? I would be tempted to answer yes.

So what would this mean? Would we need to require the user to select the server when creating a project? And would this mean that 45MB file is downloaded when you just create a HelloWorld web project that does not use EJB?
Comment 9 David Simonek 2010-03-22 17:23:49 UTC
I don't have enough JavaEE and projects knowledge to be able to make an advice. However I know that a lot (nearly 1/2) of maven JavaEE users never assign application server - this could mean that for web apps they are using jetty and jetty plugin and deploy to jetty using "mvn jetty:run-war"
Comment 10 David Konecny 2010-03-22 19:49:03 UTC
More out loud thinking:

Embeddable EJB container is part of EE6 spec (in Web and Full profile) and therefore if user creates an EE 6 project testing of EJBs in embeddable container should work out of the box. On the other hand not all EE 6 projects will contain EJBs and it does not make sense to enable embeddable EJB container always (unless it is negligible overhead; which 45MB is not). So ideally workflow from user point of view would be like this:

#1) project is created and user does some coding, perhaps write some unit tests by hand or generate them

#2) some unit tests are executed and one fails with "javax.ejb.EJBException: No EJBContainer provider available: no provider names had been found" (thrown from javax.ejb.embeddable.EJBContainer class)

#3) IDE notice the error and shows dialog box saying something like "In order to test your EJB you need embeddalbe EJB container. Application server associated with your project provides one. Do you want to add it to unit test execution classpath now? (You can perform this step later by adding library 'some name' to unit test execution classpath" in project properties)" or message could just inform user that server they are using does not provide any embeddalbe EJB container.

Showing this message box after execution failed would ensure that message is shown only when relevant. It might be hard or time consuming to try to detect such a situation in advance, eg. before any execution. And also by the time test is executed it is reasonable to expect/require a server. And once a server is registered in the IDE it should register a library for its embeddable EJB container. Just brainstorming...
Comment 11 Petr Jiricka 2010-03-23 16:20:54 UTC
I agree that registering a server-specific library is reasonable, let's go this way. On the other hand, I feel uneasy about showing a (modal) dialog based on the contents of the output window, that seems too intrusive. One thing I thought of - Maven project has a warning badge, that warns about problems with the project (such as missing dependency). When this badge appears on the project node, there is also an item "Show and Resolve Problems..." in its popup menu, which opens a problem resolution dialog. I am thinking whether it would be possible to plug into this mechanism, and show a problem "Embeddable EJB Container is missing" with an offer to fix this for the user - Milos/Dafe, is this possible? And is it possible to add items to this dialog based on parsing the output window?
Comment 12 David Simonek 2010-03-23 16:29:04 UTC
I think so - there is maven.api,problem, which defines ProblemReport and ProblemReporter - sitting in maven project's lookup.
Comment 13 Petr Jiricka 2010-04-16 13:27:09 UTC
Created attachment 97528 [details]
A patch using the new approach

Dafe, thanks a lot, this helped. I am attaching a new (concept of a) patch which uses the new approach:
1. After unsuccessful compilation user clicks the link showing the detailed test output
2. The IDE parses this output and looks for the interesting error message:
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/ejb/embeddable/EJBContainer
3. When it finds this error message, it adds a warning to the Maven project node about the missing embeddable EJB container
4. When the user opens this warning dialog, she can choose to fix it, which adds the embeddable EJB container to the dependencies

However, the patch itself has a number of problems:
- There does not seem to be an SPI that would allow to add processors that parse test output. So all the functionality is hardcoded in the base maven module, which is bad. All this functionality should be in the maven.j2ee module, as the Maven module itself should not be concerned about EJB. Also, having this in maven.j2ee will be necessary if in the future we will want to make this functionality server-specific (not hardcoded for GlassFish as it is now).
- Two methods were copied (and modified) from the CPExtender class. I believe these are generally useful utility methods (getRepoURLs and checkLibrary) - could they be moved to some utility class?
- Of course, the code needs to be I18N-ed (I did not do that as this is just a prototype)

Dafe, any ideas on the first two problems?
Also, I must say I don't quite understand what is the exact lifecycle of the ProblemReport. I noticed that it disappears when I modify and save the .pom (or when I accept the resolution in the warning dialog), correct? Is that the behavior? If so, then that is IMO acceptable.
Comment 14 Petr Jiricka 2010-05-06 09:26:45 UTC
Created attachment 98548 [details]
Hopefully final patch

I am attaching a new patch which polishes the previous one:
- introduces SPI for maven.j2ee to plug into the parsing of the test output, so the EJB aspect is cleanly separated and not hardcoded in the base Maven support
- moves some utility methods from CPExtender to utility classes, and polishes the API. 
- adds http://download.java.net/maven/glassfish to the list of well known repositories
- fixes also bug 185363

Dafe, can you please review? Thanks.
Comment 15 Petr Jiricka 2010-05-06 11:49:55 UTC
Created attachment 98558 [details]
Another revision of the patch - the previous one was missing one file.
Comment 16 piotrekdeb 2010-05-10 22:01:23 UTC
I faced the same problem using Netbeans 6.8 and 6.9 Beta when I try running the
ejb-embedded sample vom the sample/javaee-folder from my glassfishV3
installation. JUnit always kicked some strange exceptions 

....

o EJBContainer provider available: no provider names had been found.
javax.ejb.EJBException: No EJBContainer provider available: no provider names
had been found.
        at javax.ejb.embeddable.EJBContainer.reportError(EJBContainer.java:186)
        at
javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:121)
        at
...



but now I really think I have a workaround. As mentoined in a lot of articles
in the Internet, you MUST set the EJBContainer.MODULES also in the sample code.
Here's the change, only ONE LINE OF CODE more:

OLD VERSION (SimpleEjbTest.java):

....
try {
            Map<String, Object> p = new HashMap<String, Object>();
            p.put(EJBContainer.APP_NAME, "sample");

            EJBContainer c = EJBContainer.createEJBContainer(p);
            Context ic = c.getContext();
            System.out.println("Looking up EJB...");
            SimpleEjb ejb = (SimpleEjb)
ic.lookup("java:global/sample/SimpleEjb");

.....


NEW Version:

.....

try {
            Map<String, Object> p = new HashMap<String, Object>();
            p.put(EJBContainer.APP_NAME, "sample");

            ============================================================
            p.put(EJBContainer.MODULES, new File("build/test/classes"));
            ============================================================

            EJBContainer c = EJBContainer.createEJBContainer(p);
            Context ic = c.getContext();
            System.out.println("Looking up EJB...");
            SimpleEjb ejb = (SimpleEjb)
ic.lookup("java:global/sample/SimpleEjb");
....


I hope this helps ;)

By the way, running the Embedded EJB Container in a standalone Java-App inside
a main-method runs great, you only have to add the embedded-static-shell.jar
AND javax.ejb.jar ;)
Comment 17 piotrekdeb 2010-05-10 22:17:07 UTC
I have to correct the line should be:



p.put(EJBContainer.MODULES, new File("build/jar"));
Comment 18 David Simonek 2010-05-13 13:27:11 UTC
Uff, quite a big patch, but it looks OK to me, even with tests, great, I think it can be integrated in current state. Only one performance related comment - we should be careful to not create too much output observers, as it may slow down output.
Comment 19 Petr Jiricka 2010-05-13 15:33:24 UTC
Thanks a lot Dafe, fixed: http://hg.netbeans.org/web-main/rev/4eefc2085839

> even with tests
This is actually a refactored test that was there before.

> we should be careful to not create too much output observers
I agree.
Comment 20 Quality Engineering 2010-05-17 15:25:45 UTC
Integrated into 'main-golden', will be available in build *201005170932* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/
User: 
Log:
Comment 21 Quality Engineering 2010-05-19 06:15:11 UTC
Integrated into 'main-golden', will be available in build *201005182201* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)
Changeset: http://hg.netbeans.org/main/rev/
User: 
Log:
Comment 22 Petr Jiricka 2010-07-07 15:46:15 UTC
Filed bug 187585 as a follow-up of this for cross-server behavior.
Comment 23 Petr Jiricka 2010-07-16 18:08:30 UTC
The current workflow (loosely based on item 3 in comment #10) is the following:
1. Starting with a Java EE 6 Maven Web project with an EJB 
2. On this EJB, select Tools -> Create JUnit Tests and edit the test as needed
3. Run this test, it will fail with an error message
4. By clicking on this error message in the (textual) output tab, the SureFire test report will appear
5. At the same time, a warning badge will appear on your project
6. In the project's popup menu, choose Show and Resolve Problems...
7. The dialog will contain a hint to add a dependency on the embeddable EJB container - accept this hint
8. Rerun the test - should work now

Granted, this workflow is not very intuitive and discoverable.
Comment 24 walec51 2010-07-21 10:04:02 UTC
The same thing happens in a Java Web project in Netbeans 6.9 if you use EJB's from a pain old imported jar file