3 Replies Latest reply on Apr 13, 2012 3:15 AM by dan.j.allen

    [Solder 3.1.0.Final][Seam Validation 3.1.0.Final] and [Glassfish-embedded-all 3.1.2-b16]

    blep

      Hi,

       

      I try to build a test case with Seam Validation, Arquillian and GF embedded but I'm facing a problem :

       

      Caused by: java.lang.IllegalAccessError: tried to access class org.jboss.solder.servlet.event.ImplicitServletObjectsHolder$InternalServletContextEvent from class org.jboss.solder.servlet.event.ImplicitServletObjectsHolder$Proxy$_$$_WeldClientProxy

       

      Full stack trace is available on http://pastebin.com/iWfhTqbg .

       

      It looks similar to the problem ( http://java.net/jira/browse/GLASSFISH-14808 ) mentionned in the "supported platforms" paragraph in Solder, but the affected GF release is 3.1 (fixed in b47) and I use 3.1.2.

       

      Could you confirm please the two issues are related ?

       

      If no, is there any way to make Seam Validation work with GF embedded?

       

      The code for the test case is:

       

      {code}

      @RunWith(Arquillian.class)

      public class ValidationIT {

       

       

                @Deployment

                public static Archive<?> createTestArchive() {

                          MavenDependencyResolver resolver = DependencyResolvers.use(MavenDependencyResolver.class)

                                              .configureFrom(System.getenv("M2_HOME") + "/conf/settings.xml")

                                              .loadMetadataFromPom("pom.xml");

                          WebArchive archive = ShrinkWrap

                                              .create(WebArchive.class, "test.war")

                                              .addAsLibraries(resolver.artifacts("org.jboss.seam.validation:seam-validation-api").resolveAsFiles())

                                              .addAsLibraries(resolver.artifacts("org.jboss.seam.validation:seam-validation").resolveAsFiles())

      //                                        .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")

                                              .addAsManifestResource("META-INF/beans-validation.xml","beans.xml")

                                                                  ;

                          return archive;

                }

       

       

                @AutoValidating

                public static class ValidatedService {

                          public String method(@NotNull String param) {

                                    return "yes";

                          }

                }

       

       

                @Inject

                private ValidatedService validatedService;

       

       

                @Test

                public void testValidatedService() {

                          validatedService.method(null);

                          fail();

       

       

                }

      }

      {code}

       

        • 1. Re: [Solder 3.1.0.Final][Seam Validation 3.1.0.Final] and [Glassfish-embedded-all 3.1.2-b16]
          lightguard

          No clue what's going on there, the stack trace doesn't seem to help much. Have you searched the forums? There have been other GF embedded quesitons.

          • 2. Re: [Solder 3.1.0.Final][Seam Validation 3.1.0.Final] and [Glassfish-embedded-all 3.1.2-b16]
            dan.j.allen

            I managed to get the test working, though I used a different container adapter. I'll explain the reason for the switch, then how I got the test working.

             

            The danger of embedded containers

             

            In the Arquillian project, we're all about real tests. There's a good reason for this philosophy. If you use mocks or a substitute container when testing code that uses a programming model (i.e., CDI), all you can be sure of is that you've faked out enough functionality to get the test to work. You can't be certain that your code really, truly works.

             

            Obviously, we provide adapters for embedded containers (i.e., substitute containers). The reason is, they often do a decent enough job of simultating the real environment when we are working in development that we are willing to make the trade-off for it's variations. I would expect, though, that you are running the same tests in CI using a real container...just to get that peace of mind.

             

            How do you know when not to use an embedded container?

             

            If you write a test and you are positive it should work, but instead it throws some wacky exception, immediately stop and try the same test in a real container. In your case, I would advise running this test on managed or remote GlassFish, for instance. Both of those adapters use an authentic GlassFish instance. If the test passes, then you have answered the question of this section.

             

            When the embedded container begins to act inconsistently, drop it.

             

            As with all things, there are exceptions to the rule. If the code really *should* work on the embedded container, then it may be worth exploring why it isn't. That may lead you to the discovery of a bug in the library. If debugging the library is your main concern, chase it down. If you are just trying to get a test written for your application code, stop using the embedded container (or save debugging it for a rainy day).

             

            Getting a green bar for this test

             

            The first thing I did was move ValidationService into it's own file. Then you'll see it's clear you need to package it in the ShrinkWrap archive.

             

            package org.arquillian.example;
            
            import javax.validation.constraints.NotNull; 
            import org.jboss.seam.validation.AutoValidating;
            
            @AutoValidating
            public class ValidatedService {
                public String method(@NotNull String param) {
                    return "yes";
                }
            }
            

             

            Next, I made some updates to the test. Here's what I changed:

             

            • Add beans.xml to WEB-INF (not META-INF)
            • Use try/catch to handle the expected exception (otherwise you get serialization problems w/ the exception class)
            • Moved beans-validation.xml to the src/test/resources root
            • Disabled the configureFrom() (you don't have to change this...I just put the repositories in my pom.xml to simplify things)

             

            package org.arquillian.example;
            
            import javax.inject.Inject;
            import org.hibernate.validator.method.MethodConstraintViolationException;
            import org.jboss.arquillian.container.test.api.Deployment;
            import org.jboss.arquillian.junit.Arquillian;
            import org.jboss.shrinkwrap.api.Archive;
            import org.jboss.shrinkwrap.api.ShrinkWrap;
            import org.jboss.shrinkwrap.api.spec.WebArchive;
            import org.jboss.shrinkwrap.resolver.api.DependencyResolvers;
            import org.jboss.shrinkwrap.resolver.api.maven.MavenDependencyResolver;
            import org.junit.Assert;
            import org.junit.Test;
            import org.junit.runner.RunWith;
            
            @RunWith(Arquillian.class)
            public class ValidationIT {
            
                @Deployment
                public static Archive<?> createTestArchive() {
                    MavenDependencyResolver resolver = DependencyResolvers.use(MavenDependencyResolver.class)
                            .loadMetadataFromPom("pom.xml");
                    WebArchive archive = ShrinkWrap.create(WebArchive.class, "test.war")
                            .addClasses(ValidatedService.class, ValidationIT.class)
                            .addAsLibraries(resolver.artifacts("org.jboss.seam.validation:seam-validation-api").resolveAsFiles())
                            .addAsLibraries(resolver.artifacts("org.jboss.seam.validation:seam-validation").resolveAsFiles())
                            .addAsWebInfResource("beans-validation.xml", "beans.xml");
                    return archive;
                }
            
                @Inject
                private ValidatedService validatedService;
            
                @Test
                public void testValidatedService() {
                    try {
                        validatedService.method(null);
                        Assert.fail();
                    }
                    catch (MethodConstraintViolationException e) {
                        // expected
                    }
                }
            }
            

             

            Next, I added a profile for the GlassFish remote container adapter in my pom.xml.

             

                    <profile>
                        <id>arquillian-glassfish-remote</id>
                        <dependencies>
                            <dependency>
                                <groupId>org.jboss.spec</groupId>
                                <artifactId>jboss-javaee-6.0</artifactId>
                                <version>1.0.0.Final</version>
                                <type>pom</type>
                                <scope>provided</scope>
                            </dependency>
                            <dependency>
                                <groupId>org.jboss.arquillian.container</groupId>
                                <artifactId>arquillian-glassfish-remote-3.1</artifactId>
                                <version>1.0.0.CR3</version>
                                <scope>test</scope>
                            </dependency>
                        </dependencies>
                    </profile>
            

             

            I then enabled the arquillian-glassfish-remote profile in Eclipse.

             

            Next, I downloaded GlassFish 3.1.2 and extracted it. I setup the container in Eclipse so I could start it from there. Of course you could just start it from the commandline...but then debugging is more difficult.

             

            With GlassFish running and the arquillian-glassfish-remote profile enabled:

             

            Run As > JUnit Test

            Green bar

             

            Closing thoughts

             

            Code that uses Solder works fine on GlassFish 3.1.2, but it does not play very nicely with Embedded GlassFish. It's just a hostile environment for CDI because of visibility issues that come with starting an embedded container inside an existing Java process. You end up in a minefield of class visibility and access problems.

             

            If you want the container to start automatically just like embedded, then switch to the arquillian-glassfish-managed-3.1 container. It works almost exactly the same as the remote container, except that Arquillian will start the standalone process at the beginning of the test execution and stop it at the end. In other words,

             

            managed >>>> embedded

             

            But for speed of development:

             

            remote > managed

             

            since you don't have to wait for the container to start...and you can start it in debug mode very easily and keep it there.

             

            Keep that in mind.

            • 3. Re: [Solder 3.1.0.Final][Seam Validation 3.1.0.Final] and [Glassfish-embedded-all 3.1.2-b16]
              dan.j.allen

              Btw, there is one major issue with Solder running on Embedded GlassFish.

               

              There are hacks to load special extensions for when using certain versions of Weld in GlassFish. There's only one problem with this approach. Embedded GlassFish does not allow you do see the package manifest info. It has to do with OSGi visibility rules. Therefore, the following code in TypedMessageBundleAndLoggerExtension.java throws a NullPointerException.

               

              processTypesInModule = System.getProperty("glassfish.version") != null && cdi.getImplementationTitle().contains("Weld")
                              && cdi.getImplementationVersion().equals("20110114-1644");
              

               

              I don't know what we need to do to get the Weld version in this environment. Perhaps it would be best to ask the GlassFish developers.

               

              If nothing else, this code needs to be changed to perform a null check on getImplementationTitle() and getImplementationVersion(). We shouldn't just skip this extension if this information is not available.