Testing Java EE 6 with Arquillian (incl. JPA, EJB, Bean Validation and CDI)

For a very long time, I heard quite a lot of people saying good things about Arquillian. Whilst I have been reading articles around its use, I couldn’t really find one that covers some of the aspects that I find important, all in a single article. Granted, I haven’t looked hard enough.

Points that I would like to cover are:

  • The use of JPA. I simply use EclipseLink here,
  • The use of in-memory database,
  • The use of CDI injection,
  • The use of EJB, say local Stateless session bean,
  • The use of JSR-303 Bean Validation,
  • The use of (embedded) glassfish for integration testing.

It took me a while to gather information to get such project up and running. I thought I dedicate this post to help out those who’s got similar requirement.

So, what are we waiting for!? Let’s start!!!

Configure pom.xml

Of course, we need to configure out project to use Arquillian, and also use EclipseLink as the persistence provider. But, bear in mind that we also decide before that we want to use an in-memory database. You might want to include your dependency here, in this pom.xml. I, however, decide to use Derby, which is available in the standard Oracle Java SDK classpath.

Anyway, so the pom.xml would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>inout</artifactId>
        <groupId>id.co.dwuysan</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>id.co.dwuysan</groupId>
    <artifactId>inout-ejb</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>ejb</packaging>

    <name>inout-ejb</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <netbeans.hint.deploy.server>gfv3ee6</netbeans.hint.deploy.server>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>1.0.0.Final</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>eclipselink</artifactId>
            <version>2.3.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>javax.persistence</artifactId>
            <version>2.0.3</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
            <version>2.3.2</version>
            <scope>provided</scope>
        </dependency>

        <!-- test -->

        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>1.0.3.Final</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.glassfish.main.extras</groupId>
            <artifactId>glassfish-embedded-all</artifactId>
            <version>3.1.2.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-glassfish-embedded-3.1</artifactId>
            <version>1.0.0.CR3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.junit</groupId>
            <artifactId>arquillian-junit-container</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <!-- environment requirement -->
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.12.4</version>
                <configuration>
                    <argLine>-XX:-UseSplitVerifier</argLine>
                    <systempropertyvariables>
                        <java.util.logging.config.file>
                        	${basedir}/src/test/resources/logging.properties
                        </java.util.logging.config.file>
                    </systempropertyvariables>
                    <systemProperties>
                        <property>
                            <name>derby.stream.error.file</name>
                            <value>target/derby.log</value>
                        </property>
                    </systemProperties>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-ejb-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <ejbVersion>3.1</ejbVersion>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.1</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>


                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>6.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>java.net</id>
            <url>http://download.java.net/maven/</url>
        </repository>
        <repository>
            <id>JBOSS_NEXUS</id>
            <url>http://repository.jboss.org/nexus/content/groups/public</url>
        </repository>
        <repository>
            <url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
            <id>eclipselink</id>
            <layout>default</layout>
            <name>Repository for library EclipseLink (JPA 2.0)</name>
        </repository>
    </repositories>
</project>

As shown in the XML above, the things we have done are:

  • Include the use of Arquillian, using embedded Glassfish
  • Include the use of EclipseLink
  • Set Derby props for later use, i.e. logging and location of the database created.
    • Create out ‘case’, i.e. our JPA, EJB, CDI

      Of course, we first start by creating a case, so that we can then later test it. I assume you are familiar with JPA, EJB, CDI. Hence, following are very quick glimpses of classes using these technology.

      JPA class, Outlet.java

      package id.co.dwuysan.inout.entity;
      
      // imports omitted
      
      @Entity
      @NamedQueries({
          @NamedQuery(
              name = Outlet.FIND_BY_NAME,
              query = "SELECT o FROM Outlet o WHERE o.name = :name")
      })
      public class Outlet implements Serializable {
          
          public static final String FIND_BY_NAME = "Outlet#FIND_BY_NAME";
          
          @Id
          @GeneratedValue(strategy = GenerationType.AUTO)
          private Long id;
          
          @Column(name = "code", length = 50, insertable = true, 
                  updatable = false, unique = true)
          @Size(message = "{dwuysan.nameSizeError}", min = 1, max = 50)
          @NotNull
          private String name;
      
          /* Accessors and mutators goes here */
      
          @Override
          public int hashCode() {
              // omitted
          }
      
          @Override
          public boolean equals(Object obj) {
              // omitted
          }
      }
      

      Persistence.xml

      <?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="inoutPU" transaction-type="JTA">
          <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
          <jta-data-source>inoutDb</jta-data-source>
          <exclude-unlisted-classes>false</exclude-unlisted-classes>
          <properties>
            <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
          </properties>
        </persistence-unit>
      </persistence>
      

      Then let’s add a producer method to supply our PersistenceContext, as well as our EJB that uses it.

      EntityManagerProducer.java

      package id.co.dwuysan.inout.util;
      
      import javax.enterprise.inject.Produces;
      import javax.persistence.EntityManager;
      import javax.persistence.PersistenceContext;
      
      public class EntityManagerProducer {
      
          @Produces
          @PersistenceContext
          private EntityManager em;
      }
      

      OutletService.java

      package id.co.dwuysan.inout.service;
      
      // imports omitted
      
      @Stateless
      @LocalBean
      public class OutletService {
      
          @Inject
          private EntityManager em;
          
          @Resource
          private Validator validator;
      
          public Outlet createOutlet(final String name) {
              final Outlet outlet = new Outlet();
              outlet.setName(name);
              final Set<ConstraintViolation<Outlet>> violations = this.validator.validate(outlet);
              if (!violations.isEmpty()) { throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations)); }
              return this.em.merge(outlet);
          }
      
          public Outlet getOutlet(final String name) {
              final Query query = this.em.createNamedQuery(Outlet.FIND_BY_NAME);
              query.setParameter("name", name);
              try {
                  return (Outlet) query.getSingleResult();
              } catch (NoResultException e) {
                  return null;
              }
          }
      }
      

      Sets beans.xml and ValidationMessages.properties
      Don’t forget to:

      • add beans.xml under src/main/resources/META-INF, and
      • add ValidationMessages.properties under src/main/resources, and
      • configure your message dwuysan.nameSizeError=error message you like here

      Configure for testing purpose

      At this point, should you deploy, it should work. HOWEVER, that’s not our goal. We would like to get it working under Arquillian, using embedded Glassfish.

      Firstly, let’s prepare configuration for embedded glassfish, using Derby database. The file is glassfish-resources.xml. In my case, I simply put this file under a new directory, mainly for separation, i.e. src/test/resources-glassfish-embedded/glassfish-resources.xml.

      glassfish-resources.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE resources PUBLIC
          "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN"
          "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
      <resources>
          <jdbc-resource pool-name="ArquillianEmbeddedDerbyPool"
                         jndi-name="jdbc/arquillian"/>
          <jdbc-connection-pool name="ArquillianEmbeddedDerbyPool"
                                res-type="javax.sql.DataSource"
                                datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
                                is-isolation-level-guaranteed="false">
              <property name="databaseName" value="target/databases/derby"/>
              <property name="createDatabase" value="create"/>
          </jdbc-connection-pool>
      </resources>
      

      It is quite self-explanatory. Just remember to configure the database to be created on target/databases/derby so that when you do mvn clean it will be cleaned.

      Next step, is to configure Arquillian to “recognise” this glassfish-resources.xml. To do this, add arquillian.xml under the src/test/resources directory.

      glassfish-resources.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <arquillian xmlns="http://jboss.org/schema/arquillian"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
          <engine>
              <property name="deploymentExportPath">target/arquillian</property>
          </engine> 
          <container default="true" qualifier="glassfish">
              <configuration>
                  <property name="resourcesXml">src/test/resources-glassfish-embedded/glassfish-resources.xml</property>
              </configuration>
          </container>
      </arquillian>
      

      The next step is to prepare our persistence.xml. We already have one, but remember we need to supply the one which is in-memory, and make use of the jdbc connection provided by our embedded Glassfish (see glassfish-resources.xml above, which provide the jdbc-resource-pool under the JNDI name jdbc/arquillian. In my case, I named this test-persistence.xml, under src/test/resources

      test-persistence.xml

      <?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="inoutPU" transaction-type="JTA">
              <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
              <jta-data-source>jdbc/arquillian</jta-data-source>
              <exclude-unlisted-classes>false</exclude-unlisted-classes>
              <shared-cache-mode>ALL</shared-cache-mode>
              <properties>
                  <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
                  <property name="javax.persistence.jdbc.url" value="jdbc:derby:target/databases/derby;create=true" />
                  <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
                  <property name="eclipselink.target-database" value="Derby"/>
                  <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
                  <property name="eclipselink.debug" value="OFF"/>
                  <property name="eclipselink.weaving" value="static"/>
                  <!--<property name="eclipselink.logging.level" value="FINEST"/>-->
                  <property name="eclipselink.logging.level.sql" value="FINE"/>
                  <property name="eclipselink.logging.parameters" value="true"/>
                  <!--<property name="eclipselink.logging.level.cache" value="FINEST"/>-->
                  <property name="eclipselink.logging.logger" value="DefaultLogger"/>
              </properties>
          </persistence-unit>
      </persistence>
      

      When everything is ready, we are now ready to write our unit test, with Arquillian. In this case, it is best to test our service EJB, since that’s where we are going to use JPA, CDI, and the Validation.

      OutletServiceTest.java

      package id.co.dwuysan.inout.service;
      
      // imports omitted
      
      @RunWith(Arquillian.class)
      public class OutletServiceTest {    
          
          @Inject
          private OutletService outletService;
      
          @Deployment
          public static JavaArchive createTestArchive() {
              return ShrinkWrap.create(JavaArchive.class)
                      .addClass(Outlet.class)
                      .addClass(OutletService.class)
                      .addClass(EntityManagerProducer.class)
                      .addAsManifestResource("test-persistence.xml",
                      	ArchivePaths.create("persistence.xml"))
                      .addAsManifestResource("META-INF/beans.xml",
                      	ArchivePaths.create("beans.xml"))
                      .addAsResource("ValidationMessages.properties");
          }
          
          
          @Test
          public void testCreateOutlet() throws Exception {
              final String outletName = "Outlet 001";
              final Outlet outlet = this.outletService.createOutlet(outletName);
              Assert.assertNotNull(outlet);
      
              // check retrieval
              final Outlet retrievedOutlet = this.outletService.getOutlet(outletName);
              Assert.assertEquals(outlet.getName(), retrievedOutlet.getName());
          }
          
          @Test(expected = ConstraintViolationException.class)
          public void testCreateOutletWithEmptyName() throws Exception {
              try {
                  final Outlet outlet = this.outletService.createOutlet("");
              } catch (EJBException e) {             
                  final ConstraintViolationException cve = (ConstraintViolationException) e.getCause();
                  
                  Assert.assertEquals("Total error message should only be one",
                  	1, cve.getConstraintViolations().size());            
                  Assert.assertEquals("Message must be correct",
                  	"Name must be provided",
                  	cve.getConstraintViolations().iterator().next().getMessage());
                  throw cve;
              }
          }
      }
      

      In the above example, the first test is testing the successful case. Given a name, a retrieval should result in an Outlet entity return providing the same name as parameter. Underneath the surface though, if we look back at the body of the OutletService.java, we are actually testing:

      • Persistence (JPA), into the underlying Derby
      • EJB injected into this test/li>
      • PersistenceContext injected via Producer method (CDI)
      • Testing no validation violated
      • Testing our NamedQuery

      The second test is aimed to test that the message is interpolated correctly. Referring to what mentioned previously, for my error message, I have put the following entry in my ValidationMessages.properties:

      dwuysan.nameSizeError=Name must be provided
      

      So, we need to test that the message from Bean Validation in Outlet is interpolated correctly.

      Please pay attention to the second test. Notice that firstly, we are catching EJBException. That is because any runtime exception thrown inside an EJB will be wrapped into EJBException, hence the need to extract it via #getCause().

      So, there you go. You can now add more services and start your Arquillian test. Happy coding 🙂

      Future investigation

      Many Java EE application of course requires authentication and authorisation, which is generally done via JAAS. For example, using my simple example above, supposed that the service is to be modified to retrieve the outlets the current user has access to, then of course we need to get the current user’s identity. Generally, this is done via EJBContext.getCallerPrincipal(). I wonder how we can do this using Arquillian and embedded Glassfish.

      Advertisements

    JAAS-secured JAX-RS end point

    With the advent of RESTFUL (JAX-RS) as the “preferred” way to create web service end points, for a long time I have always wondered how people implement security mechanism around it.

    At the end of the day, I presume the underlying implementation of JAX-RS is servlet, and therefore its security might also be around what is already provided by the container, i.e. JAAS.

    This post will cover my findings on how to step-by-step implement FORM-based security using JDBC realm, JAX-RS and how to test it using cURL, on Glassfish 3.

    Setting up JDBC-realm

    Firstly, since we are using JDBC-realm, let’s assume that we have created a JDBC connection to the underlying database under the JNDI jdbc/test.

    The next step is to create a new realm. You can do this by going to server-config > Security > Realms and add a new realm. Select the realm type com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm, and then populate the mandatory fields.

    • Start by giving your new realm a name.
    • For JAAS Context, put jdbcRealm
    • Populate JNDI name, preferably starts with "jndi/"

    Next, do pay attention to the rest of the fields. It seems that Glassfish expects to see two tables. The first table is to contain a list of users, with usernames as their unique identifier. The second table is to list the groups that each user belongs to. Username is to be the foreign-key link between the two tables. (The next section should give you better idea on how the tables should look, they are after all very simple).

    Once these tables are created, we can then populate the mandatory fields accordingly.

    Populate database for testing purpose

    The next step is to populate the table for testing purpose. Let’s just assume that we will be testing using username hpotter and password test. For the password however, note that Glassfish digest by default is SHA-256, as illustrated by the following screen shot.

    sha-256

    Hence, you need to encode the password test before insert. You can use encoder by technipixel, which will give you the String 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08.

    The next step is to write some INSERT statements:

    INSERT INTO person (id, password, username) VALUES (1, '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', 'hpotter');
    COMMIT;
    INSERT INTO person_role (username, user_group) VALUES ('hpotter', 'User');
    INSERT INTO person_role (username, user_group) VALUES ('hpotter', 'Admin');
    COMMIT;
    

    Let’s move on to the next step.

    Securing web application using JAAS

    Quite a number of tutorials out there on JAAS with FORM authentication method. However, I thought I put it here again, with the hope that some might find it simpler.

    web.xml

    Make the following modification to your web.xml

        <welcome-file-list>
            <welcome-file>/index.jsp</welcome-file><!-- 1 -->
        </welcome-file-list>
        <security-constraint><!-- 2 -->
            <display-name>TestConstraint</display-name>
            <web-resource-collection>
                <web-resource-name>TestResource</web-resource-name>
                <description/>
                <url-pattern>/*</url-pattern>
            </web-resource-collection>
            <auth-constraint>
                <description/>
                <role-name>User</role-name>
                <role-name>Admin</role-name>
            </auth-constraint>
        </security-constraint>
        <login-config><!-- 3 -->
            <auth-method>FORM</auth-method>
            <realm-name>testRealm</realm-name>
            <form-login-config>
                <form-login-page>/login.html</form-login-page>
                <form-error-page>/error.html</form-error-page>
            </form-login-config>
        </login-config>
        <security-role><!-- 4 -->
            <description/>
            <role-name>User</role-name>
        </security-role>
        <security-role><!-- 5 -->
            <description/>
            <role-name>Admin</role-name>
        </security-role>
    

    Let’s go through them one-by-one.

    1. This is the file to be displayed on successful login. You can also use this file as redirection. For example, suppose you have a file called index.xhtml (a JSF page), you can use response.sendRedirect("index.jsf");
    2. This is the actual constraint, i.e. how you secure the application. This section is basically there to secure all access to the application, denoted by /* url pattern, and only allow access of user with the role of User and Admin.
    3. This part denotes that what we are using is FORM authentication method (which I’ll explain in more detail in the next section). Important part is to ensure the correct name of the security realm used, which in this case, testRealm, the same realm name we have given when setting it up via Glassfish admin page. The other part is to set the page containing j_security_check that the application will automatically redirect to in the event of request access has not been authenticated yet.
    4. The known role
    5. Same as previous section.

    glassfish-web.xml

    We also need to configure glassfish-web.xml so that the container knows about the mapping between groups from the database and the role recognised by the application.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
    <glassfish-web-app error-url="">
        <security-role-mapping>
            <role-name>Admin</role-name>
            <group-name>Admin</group-name>
        </security-role-mapping>
        <security-role-mapping>
            <role-name>User</role-name>
            <group-name>User</group-name>
        </security-role-mapping>
        <class-loader delegate="true"/>
        <jsp-config>
            <property name="keepgenerated" value="true">
                <description>Keep a copy of the generated servlet class' java code.</description>
            </property>
        </jsp-config>
    </glassfish-web-app>
    

    N.B. If you use Netbeans, this file might be generated for you.

    Login page: login.html

    If we refer again to web.xml above, notice that the login page is pointing to login.html. For FORM authentication method, as per specification, we need to have a form with j_security_check, with j_username and j_password (Oracle 2013).

    <!DOCTYPE html>
    <html>
        <body>
    
            <form action="j_security_check" method="post">
                <p>
                    <strong>Username</strong>
                    <input type="text" name="j_username" size="25" />
                </p>
                <p>
                    <strong>Password</strong>
                    <input type="password" size="15" name="j_password" />
                </p>
                <p>
                    <input type="submit" value="Submit" />
                    <input type="reset" value="Reset" />
                </p>
            </form>
        </body>
    </html>
    

    With all of these completed, we can start up Glassfish, deploy our application and test it using any browser. Upon accessing the application, users should be directed to login.html to login. Remember to use hpotter as username and test as password. On successful login, the user should be redirected to index.jsp (which in turn redirects user to index.jsf, or anything the index.jsp is redirecting to, according to your requirement).

    Create a RESTFUL end point

    The next step of course is to create a RESTFUL end point, which is very simple. One of the posts I have written here might also be useful.

    To start, assuming we have the following application path.

    package com.dwuysan;
    
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    
    /**
     * @author denywuy
     */
    @ApplicationPath(value = "resources")
    public class ApplicationConfig extends Application {
    }
    

    Let us assume that we have the following simple RESTFUL service.

    package com.dwuysan;
    
    import com.dwuysan.entity.Outlet;
    import com.dwuysan.service.OutletService;
    import javax.annotation.ManagedBean;
    import javax.annotation.security.RolesAllowed;
    import javax.inject.Inject;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    
    @Path(value = "generic")
    @RolesAllowed(value = "User")
    @ManagedBean
    public class GenericResource {
    
        @Inject
        private OutletService outletService;
    
        @GET
        @Path("{id}")
        public Outlet get(@PathParam(value = "id") final long id) {
            return this.outletService.getOutlet(id);
        }
    }
    

    Note that we have secured this service with javax.annotation.security.RolesAllowed annotation.

    Testing secured RESTFUL service using curl

    Given the RESTFUL service we created above, we should be able to test it using CURL with the following command:

    curl -X GET -H "Accept:application/json" -H "Content-Type:application/json" http://localhost:8080/testApp/resources/generic/101
    

    The above command translate to the following: hit the URL above using GET, with the headers Accept:application/json and Content-Type:application/json (cURL 2013)

    Since we have secured our application, invocation as above will not work. The user will be redirected to login.html. Hence, our aim now is to first login. With cURL, we can submit parameters to login, i.e. username and password, and then obtain the cookie. To do that, we can use this command:

    curl -b cookies.txt -c cookies.txt -d "j_username=hpotter&j_password=test" http://localhost:8080/testApp/j_security_check
    

    This command is submitting username and password, to the j_security_check (remember our login.html that we have created previously), and storing the cookies obtain under the file cookies.txt.

    If you open your cookies.txt, you may see the following:

    # Netscape HTTP Cookie File
    # http://curl.haxx.se/docs/http-cookies.html
    # This file was generated by libcurl! Edit at your own risk.
    
    #HttpOnly_localhost     FALSE   /testApp     FALSE   0       JSESSIONID      245a317ab91fbb28244403346770
    

    N.B. You might receive Document Moved response. This means login has been successful. Otherwise, you would get the raw html of the error.html again.

    Once we have been authenticated successfully, we can use the cookies we have obtained from the login to invoke the RESTFUL service.

    curl -X GET -H "Accept:application/json" -H "Content-Type:application/json" -b cookies.txt -c cookies.txt http://localhost:8080/testApp/resources/generic/101
    

    References:

    BalusC, 2012, ‘Does JSF support form based security’, accessed 12 February 2013.

    Oracle, 2013, ‘Securing Web Application’, accessed 12 February 2013.

    Wolff, N, 2005, ‘How do you handle authentication via cookie with CURL?’, accessed 12 February 2013.