JVM Advent

The JVM Programming Advent Calendar

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 :

Welcome @sap to #JavaEE6 party, NetWeaver Cloud is now Java EE 6 Web Profile Certified: bit.ly/Wf8VNS
— GlassFish (@glassfish) November 16, 2012

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.

I hope you enjoyed reading this post as much as I enjoyed writing it.

Salut à tous!

Christophe

Meta: this post is part of the Java Advent Calendar and is licensed under the Creative Commons 3.0 Attribution license. If you like it, please spread the word by sharing, tweeting, FB, G+ and so on! Want to write for the blog? We are looking for contributors to fill all 24 slot and would love to have your contribution! Contact Attila Balazs to contribute!


[0] Java EE wins over spring
[1] Official Java EE compatibility page
[2] ‘SAP Netweaver Cloud is Java EE 6 Web Profile Compatible’ announcement on SAP Community Network

Author: gpanther

Next Post

Previous Post

2 Comments

  1. Arend v. Reinersdorff December 27, 2012

    Is the HelloDao interface really needed here? Couldn't you use HelloBean directly?

  2. Christophe Thiébaud January 3, 2013

    Correct, HelloDao interface isn't mandator; the implementation class can be annotated with @LocalBean. Needs EJB 3.1, I believe. cf. http://stackoverflow.com/questions/9453308/ejb3unit-demands-a-business-interface-for-localbean

Leave a Reply

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

© 2024 JVM Advent | Powered by steinhauer.software Logosteinhauer.software

Theme by Anders Norén