0.13.3 10-Apr-2014

ReXSL, Quick Start Guide

To get better understanding of key components let’s spend 5 minutes and create a project manually, step by step (you can also use rexsl-maven-archetype, which will create a different and more advanced project structure in one go). Start with a new Maven project with the following directory structure:

/foo
  pom.xml
  /src
    /main
      /java
        /foo
          Home.java
          FrontEnd.java
      /webapp
        /WEB-INF
          web.xml
        /xsl
          layout.xsl
          Home.xsl
    /test
      /java
        /foo
          HomeTest.java
      /rexsl
        /xhtml
          index.groovy
        /xml
          index.xml
        /scripts
          RendersHomePage.groovy
        /xsd
          foo.Home.xsd

Maven pom.xml should look like this:

<project>
  [...]
  <dependencies>
    <dependency>
      <!-- JAX-RS API -->
      <groupId>javax.ws.rs</groupId>
      <artifactId>jsr311-api</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <!-- JAXB 2.0 API -->
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.0</version>
    </dependency>
    <dependency>
      <!-- ReXSL runtime library -->
      <groupId>com.rexsl</groupId>
      <artifactId>rexsl-core</artifactId>
      <version>0.13.3</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <!-- ReXSL test harness -->
      <groupId>com.rexsl</groupId>
      <artifactId>rexsl-test</artifactId>
      <version>0.13.3</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <!-- Matchers for JUnit from http://code.google.com/p/hamcrest/ -->
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>1.3</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <!-- Unit testing framework from http://www.junit.org/ -->
      <groupId>junit</groupId>
      <artifactId>junit-dep</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>com.rexsl</groupId>
        <artifactId>rexsl-maven-plugin</artifactId>
        <version>0.13.3</version>
        <executions>
          <execution>
            <goals>
              <goal>check</goal>
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

web.xml should be simple and include one servlet and one filter from ReXSL (it’s Servlet API 3.0):

<web-app version="3.0"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/css/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/xsl/*</url-pattern>
  </servlet-mapping>
  <filter>
    <filter-name>XsltFilter</filter-name>
    <filter-class>com.rexsl.core.XsltFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>XsltFilter</filter-name>
    <servlet-name>RestfulServlet</servlet-name>
  </filter-mapping>
  <servlet>
    <servlet-name>RestfulServlet</servlet-name>
    <servlet-class>com.rexsl.core.RestfulServlet</servlet-class>
    <init-param>
      <param-name>com.rexsl.PACKAGES</param-name>
      <param-value>foo</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>RestfulServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
  <mime-mapping>
    <extension>xsl</extension>
    <mime-type>text/xsl</mime-type>
  </mime-mapping>
</web-app>

Now let’s create JAX-RS front end (FrontEnd.java):

package foo;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/")
public class FrontEnd {
  @GET
  @Produces(MediaType.APPLICATION_XML)
  public Home home() {
    return new Home();
  }
}

And JAXB-annotated data class Home.java:

package foo;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "page")
@XmlAccessorType(XmlAccessType.NONE)
public class Home {
  @XmlElement
  public String getText() {
    return "Hello, world!";
  }
}

Now, let’s unit-test our data class in src/test/java/foo/HomeTest.java:

package foo;
import com.rexsl.test.JaxbConverter;
import com.rexsl.test.XhtmlMatchers;
import org.junit.Test;
public class HomeTest {
  @Test
  public void testXmlContents() {
    Home home = new Home();
    Assert.assertThat(
      JaxbConverter.the(home),
      XhtmlMatchers.hasXPath("/page/text[contains(.,'world')]")
    );
  }
}

And now, let’s create a test XML document (src/test/rexsl/xml/index.xml):

<?xml version="1.0"?>
<?xml-stylesheet href='/xsl/Home.xsl' type='text/xsl' ?>
<page>
  <text>let's say hello!</text>
</page>

This document will be XSL-transformed by rexsl-maven-plugin:check Maven goal and validated by src/test/rexsl/xhtml/index.groovy:

import com.rexsl.test.XhtmlMatchers
import org.hamcrest.Matchers
import org.junit.Assert
Assert.assertThat(rexsl.document, Matchers.containsString('say hello'))
Assert.assertThat(
    rexsl.document,
    XhtmlMatchers.hasXPath("//xhtml:div[contains(.,'say hello')]")
)

And now the most interesting part, a test script in src/test/rexsl/scripts/RendersHomePage.groovy (rexsl.home is provided by rexsl-maven-plugin executor):

package foo
import com.jcabi.http.request.JdkRequest
import com.jcabi.http.response.RestResponse
import com.jcabi.http.response.XmlResponse
import javax.ws.rs.core.HttpHeaders
import javax.ws.rs.core.MediaType
new JdkRequest(rexsl.home)
  .header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML)
  .header(HttpHeaders.USER_AGENT, 'Safari')
  .fetch()
  .as(RestResponse.class)
  .assertStatus(HttpURLConnection.HTTP_OK)
  .as(XmlResponse.class)
  .assertXPath("//xhtml:div[contains(.,'world')]")
new JdkRequest(rexsl.home)
  .uri().path('/strange-address').back()
  .fetch()
  .as(RestResponse.class)
  .assertStatus(HttpURLConnection.HTTP_NOT_FOUND)

Now let’s create src/main/webapp/xsl/layout.xsl:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:xhtml="http://www.w3.org/1999/xhtml"
  version="2.0" exclude-result-prefixes="xs xsl xhtml">
  <xsl:output method="xhtml"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" />
  <xsl:template match="/">
    <html xml:lang="en">
      <body>
        <div id="content">
          <xsl:call-template name="content" />
        </div>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

And main page XSL at src/main/webapp/xsl/Home.xsl:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:xhtml="http://www.w3.org/1999/xhtml"
  version="2.0" exclude-result-prefixes="xs xsl xhtml">
  <xsl:output method="xhtml"/>
  <xsl:include href="/xsl/layout.xsl"/>
  <xsl:template name="content">
    <xsl:value-of select="/page/text" />
  </xsl:template>
</xsl:stylesheet>

The last step is to create src/test/rexsl/xsd/foo.Home.xsd in order to validate XML output on fly:

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="page">
    <xs:complexType>
      <xs:all>
        <xs:element name="text" type="xs:string" minOccurs="1" maxOccurs="1" />
      </xs:all>
    </xs:complexType>
  </xs:element>
</xs:schema>

That’s it. All you have to do now is to run mvn package and deploy your WAR to production server.

To test the system on your local machine run mvn package rexsl:run -Drexsl.port=9099 and open http://localhost:9099 with your favorite browser.

What’s especially cool is that you can can make changes to your webapp/* files and they will become available on-fly in web (without restart of the application). Just refresh the page in browser.

built by maven