Enterprise Java

Java EE 6 Web Profile. On the cloud. Easy.

Java SE is ok.

Java EE is evil.

That’s what I always used to think. Well, not anymore, now. Let me share my experience.

Some weeks ago, I started thinking about porting a legacy spring+hibernate+tomcat application to a new platform :
SAP NetWeaver Cloud. I known what you geeks out there are thinking : this post is getting worse. It starts with Java EE, not exactly a geeky thing, and now enters SAP, not exactly a geek company… Please, give me another ten minutes !

The configuration of the spring layer of my legacy application was xml-based (it was written before annotations came in the game). I was dreaded by the prospect of diving into – my own – xml horror again.

Then came this tweet :


and, some days later, this documentation. And I tried it out. And it worked. And I changed my mind about Java EE. There is a blog post by ‘Bill the Plumber’ that precisely describe what are my thoughts after this experience.

So much with the bla bla bla. Let’s start coding! If you are in a hurry, clone the complete application from https://github.com/cthiebaud/adventscloud

Before cutting and pasting like mad, let’s describe briefly what the code below is about. We will construct and deploy in the cloud (free) a tiny web application that:

1. logs in the user (sorry, you’ll need a SAP Community Network account, do not worry it’s free),

2. upon login, say ‘hello’ to the rest of the world on behalf of the user,

3. upon successive logins, instead of saying ‘hello’ again and again, merely store in a database how many ‘hellos’ were said, and

4. that’s it.

To achieve that, we’ll need one java interface, three java classes, one java server page, and a final touch of persistence.xml (for the database configuration) and web.xml (for security constraint wizardry).

For the sake of brevity, packages, imports, getters and setters are omitted from the code below. But, as just said, the complete source is available @ github

Write one Hello.java POJO (complete class here):

public class Hello {

  private Long      id;
  private String    username;
  private Integer   counter;
  private Timestamp when;
  // ... getters and setters ...
}

Fairly obvious : for each username, this POJO will store in a counter how many time the user hit the index.jsp of our app, and when was the last time.

Annotate this Hello.java POJO with JPA annotations (complete class here):

@Entity
@Table(name="T_HELLO")
@NamedQueries( {
  @NamedQuery(name = "allHellos", query = "select h from Hello h"),
  @NamedQuery(name = "helloFromUsername", query = "select h from Hello h where h.username = :username")
})
public class Hello {

  @Id
  @GeneratedValue
  private Long      id;
  @Column(name="A_USER", unique=true, nullable=false)
  private String    username;
  @Column(name="A_COUNTER", nullable=false)
  private Integer   counter;
  @Version
  @Column(name="A_TIMESTAMP", nullable=false)
  private Timestamp when;

  public Hello() {
    this.counter = 1;
  }
  // ... getters and setters ...
}

Write one HelloDao.java interface that accesses the POJO (complete interface here)

@Local
public interface HelloDao {

  List<hello> getAll();
  Hello fromUsername(String username);
  Hello save(Hello hello);
}

Write one HelloBean.java, annotated with EJB annotations, that implements the HelloDao interface (complete class here):

@Stateless
public class HelloBean implements HelloDao {

  @PersistenceContext
  private EntityManager em;

  @Override
  public List<hello> getAll() {
    @SuppressWarnings("unchecked")
    List<hello> hellos = (List<hello>)em.createNamedQuery("allHellos").getResultList();

    Collections.sort(hellos, new Comparator<hello>() {
      @Override
      public int compare(Hello o1, Hello o2) {
        return o2.getWhen().compareTo(o1.getWhen()); // latest first
      }
    });

    return hellos;
  }

  @Override
  public Hello fromUsername(String username) {
    Query query = em.createNamedQuery("helloFromUsername");
    query.setParameter("username", username);
    Hello hello = null;
    try {
      hello = (Hello)query.getSingleResult();
    } catch (NoResultException ignored) {
    }

    return hello;
  }

  @TransactionAttribute
  @Override
  public Hello save(Hello hello) {
    hello = em.merge(hello);
    return hello;
  }
}

Write one HelloFilter.java Java Servlet 3 Filter, that 1. bumps the counter upon login, and 2. exposes the HelloBean instance to the – coming soon – Java Server page (complete class here):

@WebFilter("/index.jsp")
public final class HelloFilter implements Filter {

  @EJB
  HelloDao  helloDao;

  @Override
  public void init(FilterConfig fConfig) throws ServletException {
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  try {
    request.setAttribute("helloDao", helloDao);

    String username = ((HttpServletRequest)request).getRemoteUser();
    Hello hello = helloDao.fromUsername(username);
    if (hello == null) {
      hello = new Hello();
      hello.setUsername(username);
    } else {
      hello.setCounter(hello.getCounter()+1);
    }
    hello = helloDao.save(hello);

    chain.doFilter(request, response);

  } finally {
    request.removeAttribute("helloDao");
  }
  }

  @Override
  public void destroy() {
  }
}

NB. in bold above, the magic plumbing done by our Java EE 6 Web Profile container between all these classes :

@PersistenceContext EntityManager em;
@EJB HelloDao helloDao;
@WebFilter('/index.jsp')

Write one persistence.xml JPA configuration (complete xml here)

<?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="adventscloud-persist" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <jta-data-source>jdbc/DefaultDB</jta-data-source>
    <class>net.aequologica.adventscloud.Hello</class>
    <properties>
      <property name="eclipselink.ddl-generation" value="create-or-extend-tables" />
    </properties>
  </persistence-unit>
</persistence>

Write one web.xml to trigger login when user access index.jsp and to inform the web application of the presence of a container managed database (complete xml here):

<?xml version="1.0" encoding="UTF-8"?>
<web-app
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0">

  <login-config>
    <auth-method>FORM</auth-method>
  </login-config>

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected Area</web-resource-name>
      <url-pattern>/index.jsp</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>Everyone</role-name>
    </auth-constraint>
  </security-constraint>
  <security-role>
    <description>All SAP NetWeaver Cloud users</description>
    <role-name>Everyone</role-name>
  </security-role>

  <resource-ref>
    <res-ref-name>jdbc/DefaultDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
  </resource-ref>

</web-app>

Again, NB. above in bold the further magic plumbing:

<jta-data-source>jdbc/DefaultDB</jta-data-source>
<class>net.aequologica.adventscloud.Hello</class>
<res-ref-name>jdbc/DefaultDB</res-ref-name>

Finally, write one index.jsp java server page that displays all ‘hellos’ (complete page here):

<%@ taglib prefix="c"   uri="http://java.sun.com/jstl/core_rt" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt_rt" %>
<!DOCTYPE html>
<html>
  <head>
    <title>adventscloud</title>
  </head>
  <body>

  <table>
    <tbody>
      <c:forEach var="hello" items="${requestScope.helloDao.all}" varStatus="status">
        <tr>
          <td><fmt:formatDate type="both" value="${hello.when}" /></td>
          <td>${hello.counter}</td>
          <td>hello<c:if test = "${hello.counter > 1}">(s)</c:if> from</td>
          <td>${hello.username}</td>
        </tr>
      </c:forEach>
    </tbody>
  </table>  

  </body>

</html>

We’re nearly done … two last things: 1. classpath hell, and 2. JPA 2.0 metamodel generation with javac -processor.

1. Classpath hell.

In order to compile all this stuff, you’ll need somehow to have the following jars on the classpath :

group | artifact | version
javax.persistence : persistence-api   : >= 1.0 
javax.ejb         : ejb-api           : >= 3.0 
javax.servlet     : javax.servlet-api : >= 3.0 
javax.servlet     : jstl : >= 1.2

Of course the easiest way is to declare these dependencies in a maven project like mine, but if you are maven-averse, I took the time to hyperlink to the jars above to maven central to spare you some time chasing the jars.

2. JPA 2.0 metamodel generation with javac -processor.

Finally , the build must be able to generate JPA 2.0 metamodel classes. Here I choose the eclipselink generator, as eventually eclipselink is the JPA implementation used by SAP NetWeaver Cloud. I believe that any JPA 2.0 compliant generator should do the job as well. Here also, maven do help, with the following xml fragment in the <build><plugins> section of the pom.xml:

<plugin>
  <groupId>org.bsc.maven</groupId>
  <artifactId>maven-processor-plugin</artifactId>
  <version>2.1.0</version>
  <executions>
    <execution>
      <id>process</id>
      <goals>
         <goal>process</goal>
      </goals>
      <phase>generate-sources</phase>
      <configuration>
        <processors>
          <processor>org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor</processor>
        </processors>
      </configuration>
    </execution>
  </executions>
</plugin>

Maven-averse can refer to eclipselink documentation about JPA 2.0 matamodel generation for alternative means to generate JPA 2.0 metamodel classes.

At this point, we have a adventscloud.war file that should run verbatim on any Java EE 6 Web Profile compliant container.

Among them is SAP NetWeaver Cloud. You can have a look at the application running at my lifelong-free trial instance of SAP NetWeaver Cloud. It is a bit richer than the code shown in this blog post, with a spark of twitter bootstrap bells and whistles. Follow the github ribbon in the app if you are interested by sparks.

If you’d like to get your lifelong-free trial instance of SAP NetWeaver Cloud, follow the initial steps described here.

 

Reference: Java EE 6 Web Profile. On the cloud. Easy. from our JCG partner Christophe Thiebaud at the Java Advent Calendar blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button