Red Hat

In Relation To Java EE

In Relation To Java EE

Three arguments for reified generics

Posted by    |       |    Tagged as Ceylon Java EE

Cedric recently brought up the topic of type erasure, concluding:

All in all, I am pretty happy with erasure and I’m hoping that the future versions of Java will choose to prioritize different features that are more urgently needed

Well, I suppose erasure isn't the thing I hate most about Java, but it's certainly up there. Java's system of partially reified types actually adds a surprising amount of complexity and unintuitive behavior to the type system.

From a pure language-design point of view, I think a partially reified type system is one of the worst decisions you could possibly make. Either reify all types, like C#, or reify none of them, like ML. And look, there's certain language features that simply don't play nicely with type erasure. A case in point: overloading. You can have type erasure, or you can have overloading (or, like Ceylon, you can have neither). You can't have both type erasure and overloading. No, Java is not a counter-example to this! In terms of language design, Java's approach to reification is almost impossible to justify except as a totally half-baked and misconceived workaround for simply not having time to Do It Right.

But Cedric's coming from a purely practical point of view, saying the problems don't actually bite him much when he's doing real work. Well, OK, I can see that. So here's the practical reasons why I think reified generics are needed, and why they should be added to Java if that could be done without messing up Java's type system even further.

Frameworks

Many frameworks depend upon having reified types. Type argument erasure cripples frameworks that work with generic types, and results in horrid workarounds like this one in CDI.

Typesafe narrowing

Instead of a Java-style instanceof operator, and C-style typecasts, Ceylon provides a construct for narrowing the type of a reference in a totally statically typesafe way. You just can't get ClassCastExceptions in Ceylon.

But this functionality depends upon having reified generics. Until we implement reified type arguments, we can't provide any mechanism to narrow to a parameterized type. Right now, you simply can't narrow an Object to a List<String>.

You might think that this is a problem with Ceylon, but really, the situation isn't much better in Java. The instanceof operator doesn't support types with type arguments, and casting to a type with type arguments is a totally unsafe operation! I just don't think that's acceptable behavior in a language with static typing.

Inter-language interoperability

Interoperability between statically-typed JVM languages is going to get really messy when some of the languages support reified generics and some don't. Especially since it's easy to imagine that those languages which do support reified generics won't support them in an interoperable way. This could turn out to be a real problem for the vision of a multi-language JVM.

Want to learn about JBoss AS 7? Now you can!

Posted by    |       |    Tagged as Java EE JBoss AS

If you missed our webinars on JBoss AS7 then never fear, because we've already got the recordings uploaded!

First, watch Jason introduce you to what's new and cool in JBoss AS 7, then, you can watch Dan and myself show you how to write apps for JBoss AS 7. Next up was Brian, showing you the JBoss AS 7 domain model and how to manage your subsystems, finally we have Paul and Bela showing you how to cluster JBoss AS 7.

This was a great series of tutorials, full of live demos, so check it out.

If you are looking to get started with JBoss AS 7, you might want to take a look at the 5 ways to deploy an application to JBoss AS 7 screencast I recorded yesterday.

Visualizing data structures is not easy, and I'm confident that a great deal of success of the exceptionally well received demo we presented at the JBoss World 2011 keynote originated from the nice web UIs projected on the multiple big screens. These web applications were effectively visualizing the tweets flowing, the voted hashtags highlighted in the tagcloud, and the animated Infinispan grid while the nodes were dancing on an ideal hashweel visualizing the data distribution among the nodes.

So I bet that everybody in the room got a clear picture of the fact that the data was stored in Infinispan, and by live unplugging a random server everybody could see the data reorganize itself, making it seem a simple and natural way to process huge amounts of data. Not all technical details were explained, so in this and the following post we're going to detail what you did not see: how was the data stored, how could Drools filter the data, how could all visualizations load the grid stored data, and still be developed in record time?

JPA on a Grid

All those applications were simply using JPA: Java Persistence API. Think about the name: it's clearly meant to address all persistence needs of Java applications; in fact while it's traditionally associated with JDBC databases, we have just shown it's not necessarily limited to those databases: our applications were running an early preview of the Hibernate Object/Grid Mapper: Hibernate OGM, and the simple JPA model was mapped to Infinispan, a key/value store.

Collecting requirements

The initial plan didn't include Hibernate OGM, as it was very experimental yet, it was never released nor even tagged before, but it was clear that we wanted to use Infinispan: to store and to search the tweets. Kevin Conner was the architect envisioning the technical needs, who managed to push each involved developer to do its part, and finally assembled it all into a working application in record time; so he came to Emmanuel and me with a simple list of requirements:

  • we want to showcase Infinispan
  • we want to store Tweets, many of them, in real time coming in from a live stream from Twitter
  • we need to be able to iterate them all in time order, to rollback the stream and re-process it again (as you can see in the demo recording, we had a fake cheater and want to apply stricter rules to filter out invalid tweets at a second stage, without loosing the originally collected tweets).
  • we need to know which projects are voted the most: people are going to express preferences via hashtags in their tweets
  • we want to know who's voting the most
  • it has to perform well, potentially on a big set of data

Using Lucene

So, to implement these search requirements, you have to consider that being Infinispan a key/value store, performing queries is not as natural as you would do on a database. Infinispan currently provides two main approaches: to use the Query module or to define some simple Map/Reduce tasks.

Also, consider those requirements. Using SQL, how were we going to count all tweets containing a specific hashtag, extract this count for all collected hashtags, and sort them by frequency? On a relational database, that would have been a very inefficient query which involves at least a full table scan, possibly a scan per hashtag, and it would have required a prior list of hashtags to look for. We wanted to extract the most frequently mentioned tags, we didn't really know what to look for as people were free to vote for anything.

A totally different approach is to use an inverted index: every time you save a tweet, you tokenize it, extract all terms and so keep a list of terms with pointers to the containing tweets, and store the frequency as well. That's exactly how full-text search engines like Lucene work; in addition to that Lucene is able to apply state-of-the-art optimizations, caches and filtering capabilities. Both our Infinispan Query and Hibernate Search provide nice and easy integrations with Lucene (they actually share the same engine, one geared towards Infinispan users and one to Hibernate and JPA users).

To count for who voted the most is a problem which is technically comparable to counting for term frequencies, so again Lucene would be perfect. Sorting all data on a timestamp is not a good reason to introduce Lucene, but still it's able to do that pretty well too, so Lucene would indeed solve all query needs for this application.

Hibernate OGM with Hibernate Search

So Infinispan Query could have been a good choice. But we opted for Hibernate OGM with Search as they would provide the same indexing features, but on top of that using a nice JPA interface. Also I have to admit that Hibernate OGM was initially discarded as it was lacking an HQL query parser: my fault, being late with implementing it, but in this case it was not a problem as all queries we needed were better solved using the full text queries, which are not defined via HQL.

Model

So how does our model look like? Very simple, it's a single JPA entity, enhanced with some Hibernate Search annotations.

@Indexed(index = "tweets")
@Analyzer(definition = "english")
@AnalyzerDef(name = "english",
	tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
	filters = {
		@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
		@TokenFilterDef(factory = LowerCaseFilterFactory.class),
		@TokenFilterDef(factory = StopFilterFactory.class, params = {
				@Parameter(name = "words", value = "stoplist.properties"),
				@Parameter(name = "resource_charset", value = "UTF-8"),
				@Parameter(name = "ignoreCase", value = "true")
		})
})
@Entity
public class Tweet {
	
	private String id;
	private String message = "";
	private String sender = "";
	private long timestamp = 0L;
	
	public Tweet() {}
	
	public Tweet(String message, String sender, long timestamp) {
		this.message = message;
		this.sender = sender;
		this.timestamp = timestamp;
	}

	@Id
	@GeneratedValue(generator = "uuid")
	@GenericGenerator(name = "uuid", strategy = "uuid2")
	public String getId() { return id; }
	public void setId(String id) { this.id = id; }

	@Field
	public String getMessage() { return message; }
	public void setMessage(String message) { this.message = message; }

	@Field(index=Index.UN_TOKENIZED)
	public String getSender() { return sender; }
	public void setSender(String sender) { this.sender = sender; }

	@Field
	@NumericField
	public long getTimestamp() { return timestamp; }
	public void setTimestamp(long timestamp) { this.timestamp = timestamp; }

}

Note the uuid generator for the identifier: that's currently the most efficient one to use in a distributed environment. On top of the standard @Entity, @Indexed enables the Lucene indexing engine, the @AnalyzerDef and Analyzer specifies the text cleanup we want to apply to the indexed tweets, @Field selects the property to be indexed, @NumericField makes sure the numeric sort will be performed efficiently treating the indexed value really as a number and not as an additional keyword: always remember that Lucene is focused on natural language matching.

Example queries

This is going to look like a bit verbose as I'm expanding all functions for clarity:

public List<Tweet> allTweetsSortedByTime() {

	//this is needed only once but we want to show it in this context:
	QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity( Tweet.class ).get();

	//Define a Lucene query which is going to return all tweets:
	Query query = queryBuilder.all().createQuery();

	//Make a JPA Query out of it:
	FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery( query );

	//Currently needed to have Hibernate Search work with OGM:
	fullTextQuery.initializeObjectsWith( ObjectLookupMethod.PERSISTENCE_CONTEXT, DatabaseRetrievalMethod.FIND_BY_ID );

	//Specify the desired sort:
	fullTextQuery.setSort( new Sort( new SortField( "timestamp", SortField.LONG ) ) );

	//Run the query (or alternatively open a scrollable result):
	return fullTextQuery.getResultList();
}

Download it

To see the full example, I pushed a complete Maven project to github. It includes a test of all queries and all project details needed to get it running, such as Infinispan and JGroups configurations, the persistence.xml needed to enable HibernateOGM.

Please clone it to start playing with OGM: https://github.com/Sanne/tweets-ogm

And see you on IRC, the Hibernate Search forums, or the brand new Hibernate OGM forums for any clarification.

How are entities persisted in the grid?

Emmanuel is going to blog about that soon, keep an eye on the blog! Update: blog on OGM published

JBoss AS 6 released

Posted by    |       |    Tagged as Java EE JBoss AS Seam

Congratulations to the whole AS team on the release of version 6! JBoss AS 6 is a Java EE 6 compliant application server, so it includes built-in support for many of the technologies we talk about here: CDI, JPA 2, Bean Validation, EJB 3.1, JSF 2.0, Servlets 3.0, along with many major enhancements in other areas.

Java EE 6 and CDI

Posted by    |       |    Tagged as CDI Java EE Seam

Dan did a webinar yesterday looking at Java EE 6 and CDI, showing how it can be used to improve your application development.

Check it out!

Adam Bien: Java Developer of the Year (2010)

Posted by    |       |    Tagged as CDI Java EE

I'd like extend my congratulations to fellow Java EE community member and blogger Adam Bien for being awarded Java Developer of the Year for 2010 by the editors of Oracle Magazine (image from November/December 2010 issue right). This is one award announced by Oracle this year that I can totally get behind and say is truly deserved.

If you speak with Adam even for any length of time, you immediately recognize that he strikes an ideal balance between being practical and flexible. He's quoted in the announcement as stating:

You have to be open to changes and suggestions

Java EE 6 brought a lot of changes in the platform, including JSR-299, and it's great to see Adam embrace the changes, apply them in practice and communicate (and challenge) the benefits through blog entries, books, articles, podcast interviews, presentations and status updates. The message he consistently delivers is:

Most developers are stunned to discover that Java EE 6 became even lighter than POJOs.

Can you ask for a more well-rounded champion for the platform? The best part is, I know Adam bases his support on technical merit and utility. To me, that's what makes his message carry so much weight.

Bien works with many companies as a Java architecture consultant for enterprise applications, helping organizations design and implement high-performance Java solutions and troubleshooting mission-critical problems. He's also the author of eight books and more than 100 articles on Java, architectures, and best practices.

So he works and gives back to the community. Hero.

Keep up the great work Adam and all Java EE rock stars who contribute practical experience to the community! You keep us away from Ivory Towers.

Where I've been

Posted by    |       |    Tagged as Java EE

A few folks have been asking what the hell's happened to me, and I realized that I have not posted anything here for like 6 months. Well, the truth is, apart from recently becoming a father, I've been quite busy with a really exciting new project. And yeah, my head is full of a hundred interesting things I could blog about, but, unfortunately, the new project is super-duper secret for now, so I'll have to keep a lid on it until we're ready for an alpha release (perhaps another 6 months from now).

A Hitchhikers Guide to Java EE 6 application setup, Part V

Posted by    |       |    Tagged as Java EE

In this shortish part, we'll add some interfaces to our application so external users can read the current greetings. Expanding the interfaces so greetings can be added is left as an exercise for the reader

JAX-RS

REST is hip (and now in EE 6 as JAX-RS) so let's throw it in. Add

package com.acme.greetings;

import java.util.List;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path(value = "/RESTExport")
public class RESTExporter
{

   @Inject
   GreetingServer greetingServer;

   @GET
   @Path("/greetings")
   @Produces("application/xml")
   public List<Greeting> getGreetings()
   {
      return greetingServer.getGreetings();
   }

}

That fetches the current greetings (notice the injection) from the server and presents them in XML format. To hook up the JAX-RS implementation, RESTEasy, add the following to web.xml

<context-param>
	<param-name>resteasy.scan</param-name>
	<param-value>true</param-value>
</context-param>
<context-param>
	<param-name>resteasy.servlet.mapping.prefix</param-name>
	<param-value>/resteasy</param-value>
</context-param>
<listener>
	<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<servlet>
	<servlet-name>Resteasy</servlet-name>
	<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>Resteasy</servlet-name>
	<url-pattern>/resteasy/*</url-pattern>
</servlet-mapping>

and the @XMLRootElement annotation to the top of our Greeting class

@Entity
@Audited
@XmlRootElement
public class Greeting

Your greetings should now be available from /Greetings/resteasy/RESTExport/greetings.

JAX-WS

Adding a Web Service is even more simple. Add

package com.acme.greetings;

import java.util.List;

import javax.inject.Inject;
import javax.jws.WebService;

@WebService
public class WebServiceExporter
{
   @Inject
   GreetingServer greetingServer;

   public List<Greeting> getGreetings()
   {
      return greetingServer.getGreetings();
   }

}

That does a similar job as our RESTExporter and then hook it up in web.xml

<servlet>
	<servlet-name>WSExport</servlet-name>
	<servlet-class>com.acme.greetings.WebServiceExporter</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>WSExport</servlet-name>
	<url-pattern>/WebServiceExport/*</url-pattern>
</servlet-mapping>

Hmm. Wonder if you can make it auto-register? Anyway, the WDSL should be viewable from /Greetings/WebServiceExport?wsdl

Conclusion

This was a short one. Partly because setting things up is really straightforward and don't require us to do that many workarounds. Hopefully, once Aslak finishes the Arqullian DBUnit integration (I already heard rumors on JSFUnit integration) I can be back with a more thorough article on testing all parts of the application.

A Hitchhikers Guide to Java EE 6 application setup, Part IV

Posted by    |       |    Tagged as Java EE

My wife has delivered our daughter and JBoss has delivered a snapshot of the AS 6 that can do most of the stuff we set out to do, so let's move on!

The good, the bad and our daily workarounds

So, since we're living on the edge here, head over to JBoss Hudson and grab the latest successful build (#1750 or later). Install the envers.jar in the server and change the http listener port like we did in the end of part II. Remember to update your JBOSS_HOME environment variable if you use one.

There is a slight regression with the auto-registration of the faces servlet so add this to your web.xml

<servlet>
	<servlet-name>Faces Servlet</servlet-name>
	<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>Faces Servlet</servlet-name>
	<url-pattern>/faces/*</url-pattern>
</servlet-mapping>

The ZipException is gone so clear out your faces-config.xml from the merged ICEfaces stuff and delete the icefaces directory from your local repository so you can pick up fresh copies of the original jars.

There is also another regression that has something to do with redirects and the ICEfaces PushRenderer (perhaps). If you go straight to the /Greetings context root, the PushRenderer is not that pushy, you'll have to use the full /Greetings/faces/greetings.xhtml for now.

Look mama, no EARs

We're going EJB and let's do some refactorings while we're at it. Remove the GreetingBean and create the following classes

package com.acme.greetings;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;

import javax.annotation.PostConstruct;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Stateful;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;

import org.icefaces.application.PushRenderer;

@Stateful
@ApplicationScoped
@Named
public class GreetingServer implements Serializable
{
   private static final long serialVersionUID = 1L;

   List<Greeting> greetings = new ArrayList<Greeting>();
   
   @Inject
   @Added
   Event<Greeting> greetingAddedEvent;

   @Inject
   GreetingArchiver greetingArchiver;
   
   @Inject
   GreetingEcho greetingEcho;
   
   @PostConstruct
   public void init()
   {
      greetings = greetingArchiver.loadGreetings();
   }

   @Lock(LockType.WRITE)
   public void addGreeting(Greeting greeting)
   {
      greetings.add(greeting);
      PushRenderer.render("greetings");
      greetingAddedEvent.fire(greeting);
      Future<Boolean> result = greetingEcho.echo(greeting);
   }

   @Lock(LockType.READ)
   public List<Greeting> getGreetings()
   {
      return greetings;
   }
}

and

package com.acme.greetings;

import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
import javax.enterprise.inject.Model;
import javax.inject.Inject;

import org.icefaces.application.PushRenderer;

import com.icesoft.faces.context.effects.Appear;
import com.icesoft.faces.context.effects.Effect;

@Stateful
@Model
public class GreetingClient
{
   Greeting greeting = new Greeting();

   @Inject
   GreetingServer greetingServer;

   @PostConstruct
   public void init()
   {
      PushRenderer.addCurrentSession("greetings");
   }
   
   public Effect getAppear()
   {
      return new Appear();
   }

   public Greeting getGreeting()
   {
      return greeting;
   }

   public void setGreeting(Greeting greeting)
   {
      this.greeting = greeting;
   }

   public void addGreeting()
   {
      greetingServer.addGreeting(greeting);
   }
}

The GreetingServer keeps the greetings and the client has injected the server and interacts with it (we've thrown in some annotations for concurrency control to the greeting list). You might notice we also have a new injection

@Inject
GreetingEcho greetingEcho;

which is called like

Future<Boolean> result = greetingEcho.echo(greeting);

This is an asynchronous call to GreetingEcho that looks like

package com.acme.greetings;

import java.util.concurrent.Future;

import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;

@Stateless
public class GreetingEcho
{

   @Asynchronous
   public Future<Boolean> echo(Greeting greeting)
   {
      System.out.println(String.format("Got a new greeting: %s", greeting.getText()));
      return new AsyncResult<Boolean>(true);
   }
}

This is kind of funny EE 6 construct since it really exits the @Asynchronous method immedeately, returning a handle to the task. Well, good luck trying to cancel that output before it finished. This is kind of JMS-light. And yes, it's obviously used just for show-off here.

You'll also notice that our DB bean has gotten a new look

package com.acme.greetings;

import java.util.Date;
import java.util.List;

import javax.ejb.Stateless;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;

@Stateless
public class GreetingArchiver
{
   @Inject
   @GreetingDB
   EntityManager db;

   CriteriaQuery<Greeting> loadQuery;
   ParameterExpression<Date> timestampParam;

   @Inject
   public void initQuery(@GreetingDB EntityManagerFactory emf)
   {
      CriteriaBuilder cb = emf.getCriteriaBuilder();
      timestampParam = cb.parameter(Date.class);
      loadQuery = cb.createQuery(Greeting.class);
      Root<Greeting> greeting = loadQuery.from(Greeting.class);
      loadQuery.select(greeting);
      loadQuery.where(cb.greaterThan(greeting.get(Greeting_.created), timestampParam));
   }

   public void saveGreeting(@Observes @Added Greeting greeting)
   {
      db.persist(greeting);
   }

   public List<Greeting> loadGreetings()
   {
      Date tenMinutesAgo = new Date();
      tenMinutesAgo.setTime(tenMinutesAgo.getTime() - 10 * 60 * 1000);
      return db.createQuery(loadQuery).setParameter(timestampParam, tenMinutesAgo).getResultList();
   }
}

The persistence has been much simplified thanks to CMT.

And finally the view to use them

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:ice="http://www.icesoft.com/icefaces/component"
	xmlns:ui="http://java.sun.com/jsf/facelets">
	<h:head>
		<title>
			Greetings
		</title>
	</h:head>

	<h:body>
		<h:form>
			<ice:inputText id="feedback" value="#{greetingClient.greeting.text}" effect="#{greetingClient.appear}"/>
			<h:message for="feedback" />
			<h:commandButton value="Add" action="#{greetingClient.addGreeting}" />
			<h:dataTable value="#{greetingServer.greetings}" var="greeting">
				<h:column>
					<h:outputText value="#{greeting.text}"/>
				</h:column>
			</h:dataTable>
		</h:form>
	</h:body>
</html>

JMS and JavaMail

Speaking of JMS, let's add a MDB

package com.acme.greetings;

import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

@MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destination", propertyValue = "topic/GreetingTopic"), @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic") })
public class GreetingMailer implements MessageListener
{
   @Resource(mappedName = "java:/Mail")
   Session mailSession;

   @Override
   public void onMessage(Message message)
   {
      TextMessage textMessage = (TextMessage) message;
      try
      {
         mail(textMessage.getText());
      }
      catch (Exception e)
      {
         // Forgive me, for I have sinned
         System.out.println(String.format("No mailing: %s", e.getMessage()));
      }
   }

   private void mail(String greeting) throws MessagingException
   {
      MimeMessage message = new MimeMessage(mailSession);
      message.setFrom(new InternetAddress("greetingmaster@nowhere.org"));
      message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress("you@yourplace.com"));
      message.setSubject("New greeting has arrived!");
      message.setText(greeting);
      Transport.send(message);
   }
}

This is a message driven bean that listens to the topic /topic/GreetingTopic and when a message is recieved, it grabs the default Mail session from the application server and send off a notification, configure it in the mail-service.xml in the deploy directory. You should also add the topic to the deploy/hornetq/hornetq-jms.xml like

<topic name="GreetingTopic">
   <entry name="/topic/GreetingTopic"/>
</topic>

There are probably better ways of deploying topics but I'm not writing a JBoss AS tutorial here. This MDB isn't going to see any action unless someone actually send it messages. Let's add a

package com.acme.greetings;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.enterprise.event.Observes;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

@Stateless
public class JMSDispatcher
{
   @Resource(mappedName = "/ConnectionFactory")
   ConnectionFactory connectionFactory;

   @Resource(mappedName = "/topic/GreetingTopic")
   Topic topic;

   public void broadcast(@Observes @Added Greeting greeting) throws JMSException
   {
      Session session = null;
      MessageProducer producer = null;
      Connection connection = null;
      try
      {
         connection = connectionFactory.createConnection();
         session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
         producer = session.createProducer(topic);
         TextMessage message = session.createTextMessage(greeting.getText());
         producer.send(message);
      }
      finally
      {
         if (producer != null)
         {
            producer.close();
         }
         if (session != null)
         {
            session.close();
         }
         if (connection != null)
         {
            connection.close();
         }

      }
   }
}

This bean observers the same added greeting as the GreetingArchiver and sends the greeting of to our topic. There are probably better ways of writing this but I'm not writing a JMS tutorial (either). Hmm, I forget what kind of tutorial I'm actually writing.

Something for the future

This snapshot doesn't have everyting yet, I'd like to have @Scheduled greetings so we don't get lonely. This doesn't actually work yet, but let's add it and keep upgrading our snapshots so perhaps one day we'll get lucky.

package com.acme.greetings;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.inject.Inject;

@Singleton
public class ArbiBean
{
   static final List<String> comments = new ArrayList<String>()
   {
      private static final long serialVersionUID = 1L;
      {
         add("Tried the new JRebel? I heard it can read your thoughts?");
         add("Where can I find the specs?");
         add("Shouldn't you be using RichFaces 4 instead?");
         add("How is the Seam 3 performance compared to Seam 2?");
         add("PIGBOMB!");
      }
   };

   @Inject
   GreetingServer greetingServer;

   @Schedule(second = "*/15")
   public void comment()
   {
      greetingServer.addGreeting(new Greeting(getRandomComment()));
   }

   private String getRandomComment()
   {
      return comments.get(new Random(comments.size()).nextInt());
   }
}

This one should start up and send comments to the server every 15 seconds. But like I said, not yet.

Conclusion

This ends part IV. In part V we'll add JAX-RS and JAX-WS to share our greetings with the world. If you haven't noticed it by now, developing against snapshots and alpha-releases require a fair amount of workarounds. But as a developer you can choose - do you want to sit around and wait for the final product or do you want to join the round and get first-hand knowledge on things to come (filing JIRAs as you encounter issues).

In this third part we'll be hooking up JPA 2 with the static metamodel generator, Bean Validation and Envers

JPA

Let's get persistent. When we're talking persistence, we need a persistence.xml so let's make a folder META-INF src/main/resources and create one there

<persistence 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"
	version="2.0">
	<persistence-unit name="Greetings">
		<jta-data-source>java:/DefaultDS</jta-data-source>
		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
			<property name="hibernate.show-sql" value="true" />
		</properties>
	</persistence-unit>
</persistence>

We're using the standard HSQL DefaultDS that comes with JBoss AS 6. If you want to be really hip, google around for @DataSourceDefinition which is a new kid on the block in EE 6 (haven't tried if AS 6 supports it yet, though)

Next, let's expand our model from Strings to a Greeting entity. Create a

package com.acme.greetings;

import static javax.persistence.TemporalType.TIMESTAMP;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
public class Greeting
{
   @Id
   @GeneratedValue
   int id;

   String text;
   
   @Temporal(TIMESTAMP)
   Date created = new Date();

   public int getId()
   {
      return id;
   }

   public void setId(int id)
   {
      this.id = id;
   }

   public String getText()
   {
      return text;
   }

   public void setText(String text)
   {
      this.text = text;
   }
}

change the GreetingBean to

package com.acme.greetings;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;

import org.icefaces.application.PushRenderer;


@ApplicationScoped
@Named
public class GreetingBean implements Serializable
{
   Greeting greeting = new Greeting();
   List<Greeting> greetings = new ArrayList<Greeting>();

   @Inject
   @Added
   Event<Greeting> greetingAddedEvent;

   @Inject
   GreetingArchiver greetingArchiver;
   
   @PostConstruct
   public void init()
   {
      greetings = greetingArchiver.loadGreetings();
   }

   public void addGreeting()
   {
      greetings.add(greeting);
      greetingAddedEvent.fire(greeting);
      greeting = new Greeting();
      PushRenderer.render("greetings");
   }

   public Greeting getGreeting()
   {
      return greeting;
   }

   public void setGreeting(Greeting greeting)
   {
      this.greeting = greeting;
   }

   public List<Greeting> getGreetings()
   {
      PushRenderer.addCurrentSession("greetings");
      return greetings;
   }

   public void setGreetings(List<Greeting> greetings)
   {
      this.greetings = greetings;
   }
}

We have also injected an event that is fired when comments are added:

@Inject
@Added
Event<Greeting> greetingAddedEvent;

so we need a qualifier called Added:

package com.acme.greetings;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target( { METHOD, FIELD, PARAMETER, TYPE })
public @interface Added
{
}

and greetings.xhtml to

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:ice="http://www.icesoft.com/icefaces/component"
	xmlns:ui="http://java.sun.com/jsf/facelets">
	<h:head>
		<title>
			Greetings
		</title>
	</h:head>

	<h:body>
		<h:form>
			<ice:inputText value="#{greetingBean.greeting.text}" effect="#{greetingBean.appear}"/>
			<h:commandButton value="Add" action="#{greetingBean.addGreeting}" />
			<h:dataTable value="#{greetingBean.greetings}" var="greeting">
				<h:column>
					<h:outputText value="#{greeting.text}"/>
				</h:column>
			</h:dataTable>
		</h:form>
	</h:body>
</html>

(we changed the order of the table and the input fields as it was getting annoying to have the field and button move down as we add comments)

Of course since we are firing events it would be nice if someone is actually listening. Let's create a GreetingArchvier:

package com.acme.greetings;

import java.util.List;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@ApplicationScoped
public class GreetingArchiver
{
   @PersistenceContext
   EntityManager db;

   @Inject
   UserTransaction userTransaction;

   public void saveGreeting(@Observes @Added Greeting greeting)
   {
      try
      {
         userTransaction.begin();
         db.joinTransaction();
         db.persist(greeting);
         db.flush();
         userTransaction.commit();
      }
      catch (Exception e)
      {
         e.printStackTrace();
         // The recommended way of dealing with exceptions, right?
      }
   }

   public List<Greeting> loadGreetings()
   {
      return db.createQuery("from Greeting").getResultList();
   }

}

That observes @Added Greetings and store them to the database. Notice also the loadGreetings() method that GreetingBean calls in it's @PostConstruct to populate itself with old comments. Well, with create-drop in our persistence.xml there won't be much to load but let's fix that later.

JPA 2

That's all nice but we're trying to be livin' on the edge so let's bring in JPA and typesafe queries and with those we better have some static metamodel generator, otherwise the attributes will quickly become a burden. There is Eclipse integration available (google around) but if you're doing automated maven-based builds, you're going to need this anyway. Since both Eclipse and Maven are involved in building, be prepared for some chicken-egg-project-cleaning-and-refreshing in Eclipse from time to time when adding new entities. Anyways, open up pom.xml and add some plugin repositories:

<pluginRepositories>
	<pluginRepository>
		<id>jfrog</id>
		<url>http://repo.jfrog.org/artifactory/plugins-releases/</url>
	</pluginRepository>
	<pluginRepository>
		<id>maven plugins</id>
		<url>http://maven-annotation-plugin.googlecode.com/svn/trunk/mavenrepo/</url>
	</pluginRepository>
</pluginRepositories>

The maven-compiler-plugin will need an argument to not process annotations automagically once we slap jpamodelgen on the classpath

<configuration>
	<source>1.6</source>
	<target>1.6</target>
	<compilerArgument>-proc:none</compilerArgument>
</configuration>

and the job should be taken over by our new build-plugins:

<plugin>
	<groupId>org.bsc.maven</groupId>
	<artifactId>maven-processor-plugin</artifactId>
	<version>1.3.5</version>
	<executions>
		<execution>
			<id>process</id>
			<goals>
				<goal>process</goal>
			</goals>
			<phase>generate-sources</phase>
			<configuration>
				<outputDirectory>target/metamodel</outputDirectory>
			</configuration>
		</execution>
	</executions>
	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-jpamodelgen</artifactId>
			<version>1.0.0.Final</version>
		</dependency>
	</dependencies>
</plugin>
<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>build-helper-maven-plugin</artifactId>
	<version>1.3</version>
	<executions>
		<execution>
			<id>add-source</id>
			<phase>generate-sources</phase>
			<goals>
				<goal>add-source</goal>
			</goals>
			<configuration>
				<sources>
					<source>target/metamodel</source>
				</sources>
			</configuration>
		</execution>
	</executions>
</plugin>

Dan Allen thinks this is a lot of configuration for this task, I'll have to remember to ask if he ever got his simple, elegant solution to place the artifacts in usable places ;-)

Run mvn eclipse:eclipse to have the target/metamodel added to eclipse and do the project level Maven, refresh project configuration.

Run the maven build and you should see the Greeting_ class appear in target/metamodel and in the WAR structure. Now let's bring it into use:

First we add EntityManger/EntityManagerFactory producers (the recommeded CDI way of wrapping them)

package com.acme.greetings;

import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;

public class DBFactory
{
   @Produces @GreetingDB @PersistenceContext EntityManager entityManager;
   @Produces @GreetingDB @PersistenceUnit EntityManagerFactory entityManagerFactory;
}

We also need a qualifier for that

package com.acme.greetings;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target( { METHOD, FIELD, PARAMETER, TYPE })
public @interface GreetingDB
{
}

Finally, let's modify or GreetingArchiver:

package com.acme.greetings;

import java.util.Date;
import java.util.List;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import javax.transaction.UserTransaction;

@ApplicationScoped
public class GreetingArchiver
{
   @Inject
   @GreetingDB
   EntityManager db;

   @Inject
   UserTransaction userTransaction;

   CriteriaQuery<Greeting> loadQuery;
   ParameterExpression<Date> timestampParam;

   @Inject
   public void initQuery(@GreetingDB EntityManagerFactory emf)
   {
      CriteriaBuilder cb = emf.getCriteriaBuilder();
      timestampParam = cb.parameter(Date.class);
      loadQuery = cb.createQuery(Greeting.class);
      Root<Greeting> greeting = loadQuery.from(Greeting.class);
      loadQuery.select(greeting);
      loadQuery.where(cb.greaterThan(greeting.get(Greeting_.created), timestampParam));
   }

   public void saveGreeting(@Observes @Added Greeting greeting)
   {
      try
      {
         userTransaction.begin();
         db.joinTransaction();
         db.persist(greeting);
         db.flush();
         userTransaction.commit();
      }
      catch (Exception e)
      {
         e.printStackTrace();
         // The recommended way of dealing with exceptions, right?
      }
   }

   public List<Greeting> loadGreetings()
   {
      Date tenMinutesAgo = new Date();
      tenMinutesAgo.setTime(tenMinutesAgo.getTime() - 10 * 60 * 1000);
      return db.createQuery(loadQuery).setParameter(timestampParam, tenMinutesAgo).getResultList();
   }
}

Bean Validation

Adding Bean Validation is a breeze, just stick the annotations on the entity fields in Greeting:

@Size(min = 1, max = 50)
String text;

and attach a message to the input field in greetings.xhtml:

<ice:inputText id="feedback" value="#{greetingBean.greeting.text}" effect="#{greetingBean.appear}"/>
<h:message for="feedback" />

I tried placing a @NotNull on text but it still failed on submit because the values came in as empty string (might be this is the designed behavior) so I used min = 1 instead.

Envers

If you would like to have auditing on your entities, you need to add the Envers dep to pom.xml

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-envers</artifactId>
	<version>3.5.1-Final</version>
	<scope>provided</scope>
</dependency>

I cheated a little and marked it provided as it was pulling in a lot of deps. I downloaded the envers.jar and dropped it in the server common lib with it's other hibernate buddy-jar:s. After that we can stick an annotation on the entity

@Entity
@Audited
public class Greeting

Last but not least to enjoy automatic data auditing we need to add the Envers listeners to persistence.xml

<property name="hibernate.ejb.event.post-insert"
	value="org.hibernate.ejb.event.EJB3PostInsertEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-update"
	value="org.hibernate.ejb.event.EJB3PostUpdateEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-delete"
	value="org.hibernate.ejb.event.EJB3PostDeleteEventListener,org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.pre-collection-update"
	value="org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.pre-collection-remove"
	value="org.hibernate.envers.event.AuditEventListener" />
<property name="hibernate.ejb.event.post-collection-recreate"
	value="org.hibernate.envers.event.AuditEventListener" />		

This concludes part II, next time we'll be looking at EJB:s and MDB:s in more details. And perhaps abandoning our WAR-only utopia for now. Guess I'll have to learn about the maven EJB plugin. Hints for good tutorials accepted.

PS. Have you checked the size of the WAR file after you've taken all these technologies into use? Around 320k. And of those ~85% are the ICEfaces libs (the only external deps)

back to top