This article is a translation of an original article I published a while ago about how to integrate Apache Camel with Apache Wicket.
In this article I propose that we have a look on a very simple usage of the lightweight integration framework Apache Camel with one of the most popular Java Web framework : Apache Wicket. The article’s source code is available at the end of this article, ready to be compiled, so that you can test freely from home. If you’d like to reuse this article or its source code, please contact me before since it is not free for distribution. My email is displayed on the upper left logo.
What is Apache Wicket ?
Wicket is an open-source java framework that unlike a framework such as Struts 1, is component oriented. It lets you define your pages using plain Java, and then integrate them in static HTML source code with light tags, so that everything is displayed with a light application server such as Apache Tomcat for instance.
When I was a projet leader at Reuters last year, my job was to be the person in charge for a proprietary framework, Karma, which is very close to Wicket. Thus it wasn’t very hard to jump into the Wicket world. Component oriented frameworkd such as Wicket eases the development and is more close to what we’ve been doing for decade with Swing.
In this article we will see how to create your first Wicket page, a custom component, and integrate Spring with Apache Camel to load our configuration. The most interesting concept about Wicket is that the event model is very close to Swing. You basically implement listeners and based on the Observer-Observable pattern, the Wicket engine notifies your logic depending on the user action on the web page. With the support of Ajax, it’s a very interesting solution to learn.
What is Apache Camel ?
Apache Camel is an open-source framework based on Spring Core framework. It’s an implementation of the populars EIP (Enterprise Integration Patterns) that offers either XML for configuration (like Mule) or a nice DSL based on Java, which I personaly prefer. We will use the Java DSL in this article.
Apache Camel is a good add-on for Mule. For me, Mule is a lightweight integration framework, not only an ESB. Camel is a toolbox that you can also integrate with Mule. In this architecture, Mule will be in charge for I/O and the Threading model. Camel would be in charge of routing, transform and mediation tasks. Here we will use only Camel, since my solution is pretty simple.
Content of the demonstration
I started to work on this article in november, with the aim to present this work at Devoxx during my quickie on ESB at Devoxx 2008. I quickly realized that it wouldn’t fit in the amount of time allocated for a quickie. I posted the french version in january and now you’re reading the english translation.
… and why didn’t you take Mule/Spring Integration/Apache ServiceMix/OpenESB/AnyOtherIntegrationFramework ?
I really appreciate the hard work behind the other Integration framework but for the purpose of this article, Camel is enough. It would be very interesting to publish the same article based on Spring Integration for instance. Let me know if you plan to write such article so that we can reference it here !
The sample application
Let’s start with what you will see once completed. Remember : the source code is available at the end of this article.
Unzip, open a xterm and simply type « mvn jetty:run ». Open a web browser and here is what you’ll see:
As a customer I put my name in a form, and a litle message to ask why my great PS3 wasn’t delivered for christmans.
I click the submit button and a nice « Thanks » message is displayed. So far, so good.
What’s happening in the background ?
When I click the Send button, Wicket generates a message for Apache Camel. It transforms this message and store-it to a file, to demonstrate how to save a message on the local filesystem. I don’t say this is what you should do. I just want to demonstrate some of Camel’s features. So please don’t blame me for what the application is doing, we’re more interested by HOW it is doing it.
We have another async process in Camel that polls this folder. For any incoming file, it reads it, transforms it using Velocity and then generate an email. We will use an outgoing silo so that Apache Camel send to an SMTP server the email.
For my article I use MockMail, it’s a fake SMTP server that’s really easy to use, especially for JUnit test.
The administrator view
The admin page is a wicket page that reads the inbox with POP3 and display a list of customer’s requests to the web site administrator. We will see how it is simple to implement our own Wicket component to iterate a collection of Messages.
First steps with Wicket, « Bonjour le monde »/ »Hello world »
Download the source code and open InnoteriaWicketApplication java class with your favorite IDE (mine is IDEA IntelliJ). This class is our Wicket main application entry point. I use Mock JavaMail to declare a MailBox.
The class ReportIncidentEndpoint is one of my Apache Camel component. It’s the gate to the Camel world here for Wicket.
public class InnoteriaWicketApplication extends WebApplication { private ApplicationContext ctx; private ReportIncidentEndpoint endpoint; private Mailbox mailbox; // mock javamail private static ISpringContextLocator CTX_LOCATOR = new ISpringContextLocator() { public ApplicationContext getSpringContext() { return InnoteriaWicketApplication.get().ctx; } }; public InnoteriaWicketApplication() { } public Class getHomePage() { return HomePage.class; } public static InnoteriaWicketApplication get() { return (InnoteriaWicketApplication) Application.get(); } @Override protected void init() { ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Address address= null; try { address = new InternetAddress("incident@innoteria.fr"); } catch (AddressException e) { e.printStackTrace(); } mailbox = new Mailbox(address); } /** * This trick is used to decorate a specified template class with a SpringBeanLocator * so that I can use Spring to load my Apache Camel configuration. * @param clazz will be the endpoint to decorate * @param <T> template * @return a decorated class. */ private <T> T createProxy(Class<T> clazz){ return (T) LazyInitProxyFactory.createProxy(clazz, new SpringBeanLocator(clazz, CTX_LOCATOR)); } public ReportIncidentEndpoint getReportIncidentEndpoint(){ if(endpoint==null){ endpoint=createProxy(ReportIncidentEndpoint.class); } return endpoint; } }
It is now time to write your first page, the HomePage class. It extends the WebPage class. We see that a FeedbackPanel component has been declared with 2 text fields. We also added a Form. The onSubmit method will be trigger when the user submits the form from the web page. We will see later how an instance of MyReportIncident is used to hold details later.
In a few lines, as you can see it’s pretty simple to implement a Form and to trigger an action when the user clicks a button.
public class HomePage extends WebPage { private static final long serialVersionUID = 1L; public HomePage(final PageParameters parameters) { // un de mes composants FeedbackPanel feedbackPanel = new FeedbackPanel("feedback"); add(feedbackPanel); final TextField yourName = new TextField("yourname", new Model()); final TextField yourMessage = new TextField("yourmessage", new Model()); yourMessage.setRequired(true) ; yourName.setRequired(true); Form form = new Form("formtest") { protected void onSubmit() { ReportIncidentEndpoint service = InnoteriaWicketApplication.get().getReportIncidentEndpoint(); service.reportIncident( new MyReportIncident( yourName.getModelObject().toString(), yourMessage.getModelObject().toString() )); info("Thanks!"); } }; form.add(yourName); form.add(yourMessage); // Add a form with an onSubmit implementation that sets a message add(form); /* java code */ add(new BookmarkablePageLink("link", CheckMailbox.class)); } }
Let’s see on the HTML side how does it work. If you open HomePage.html we see:
......
Wicket relies on the ID declared in the HTML to create a proxy between the HTML view and the Java side. Wicket really lets you play and design the HTML without the need to use complex components syntax. The HTML code is clean and you can even preview it with IDEA IntelliJ to validate that the layout is correct.
Enough for my short introduction to Wicket, I let you have a look at the code while I prepare the next step.
Browse the source code, try to compile and deploy with maven 2 (mvn jetty:run) and when you’re ok to continue, come back here.
The Apache Camel side
Our entry point in Camel is ReportIncidentEndpoint. The reportIncident method is notified by Wicket when the user clicks the send button in the user interface. It publishes to an in-memory Queue handled by Apache Camel. ReportIncidentEndpoint is a Spring Bean which gets injected into InnoteriaWicketApplication.
/** * @author Nicolas Martignole http://touilleur-express.fr */ public class ReportIncidentEndpoint implements Serializable { private CamelContext context; private static final long serialVersionUID=2233L; public ReportIncidentEndpoint() throws Exception { // create the context context = new DefaultCamelContext(); // append the routes to the context context.addRoutes(new ReportIncidentRoutes()); // at the end start the camel context context.start(); } /** * Callback method executed from the HomePage when the user * clicks on submit. * @param message is a message created by Wicket * @return a status message that is shown on HomePage. */ public String reportIncident(MyReportIncident message) { // create the producer template to use for sending messages ProducerTemplate producer = context.createProducerTemplate(); // Send the message to an in-memory queue and return // See GenerateEmailStoreToFilePollFolderAndSendFile // it will read the message from direct:start in-memory queue Object mailBody = producer.sendBody("direct:start", message); System.out.println("Body:" + mailBody); return "OK"; } }
Let’s see now how we integrate our application with Apache Camel. I stated explicitly 2 roads to show you one of Camel’s principle. Open ReportIncidentRoutes in your editor. This class extends RouteBuider, a Camel super class that lets you define the logical path within your application. Please note that XML is also an option.
Note that it is also possible to use XML to define your route, but here I find elegant the java definition of your path, especially when your architecture will not change any further.
The first road:
– reads the in-memory queue « direct:start »
– calls a Velocity template to generate a message
– saves set the file on the file system.
Please note that it would be very simple to replace the « save file on disk » operation with a « generate and send JMS Message » to a Queue. For the purpose of this article it’s simplier to use an in-memory queue or the file system.
public class ReportIncidentRoutes extends RouteBuilder { // Any message posted on direct:start will be // handled by velocity template, then persisted on the file system public void configure() throws Exception { from("direct:start") .to("velocity:MailBody.vm") .setHeader(FileComponent.HEADER_FILE_NAME, "errorEmail.txt") .to("file:///./target/demoCamel?append=false"); ...
Let’s add another Camel path/road. The second road :
– polls the folder for any incoming file
– adds an email subject to the message
– connects to a real SMTP server and publish the message
Do you remember that we created a MockMail Mailbox in InnoteriaWicketApplication ? Once this application is deployed, MockMail starts to listen for incoming SMTP requests on port 25. Try a telnet localhost 25, you should see that someone is listening ! So our application will really send an email. You can adapt the source code and use the GMail SMTPs server if you want to complete this article.
Here, the user name is nicolas. We will use it later to retrieve the list of email with POP3.
from("file:///./target/demoCamel") .setHeader("subject", constant("New incident reported")) .to("smtp://nicolas@localhost?password=secret&to=incident@innoteria.fr");
Apache Camel is a framework that can simplify your source code. I picked another sample to demonstrate one of its nice feature : a simple rules engine that would be enough in most use cases :
// sample 1 if the header key foo is equals to bar then forward to b queue
from("queue:a").filter(header("foo").isEqualTo("bar")).to("queue:b");
// sample 2
from("queue:c").choice()
.when(header("foo").isEqualTo("bar")).to("queue:d")
.when(header("foo").isEqualTo("cheese")).to("queue:e")
.otherwise().to("queue:errorQueue");
Apache Camel is an interesting solution. It aims to reduce the amount of technical code in your application, usually required to read a file, generate an email and send it to a SMTP server. The idea behind this framework is to ease integration with external systems such as JMS, SSH, FTP, File, SMTP or FTP. Don’t hesitate to go on Apache Camel web site and have a look at the documentation, there’s a lot of interesting use cases.
How can I read the mailbox content and display a list of emails ?
Let’s go back to Wicket. We will see in a minut how to create your own component. The next user story is : how can I read the list of incoming customer’s request from my administrator web page ?
If you click on the « Check Incident Mailbox… » on the home page you’ll see this screen :
CheckMailbox class is a Wicket web page that fetches the list of email sent to incident AT innoteria.fr. An Iterator of Message is used by our custom component to display each message. I had to add an Adapter for Message, cause javax.mail.Message is not Serializable. Wicket requires Serializable instance. This is an usage of Iterator and Adapter pattern by the way.
public class CheckMailbox extends WebPage { private static final long serialVersionUID = 1L; public CheckMailbox(final PageParameters parameters) { Address address = null; try { address = new InternetAddress("incident@innoteria.fr"); } catch (AddressException e) { e.printStackTrace(); } Mailbox mailbox = Mailbox.get(address); // javax.mail.message subclasses are not serializable // thus we need this little trick to adapt our collection. // I don't wan use collections-utils and folks, it's too simple. InnoteriaMessageIterator imi=new InnoteriaMessageIterator(mailbox.iterator()); MessagePanel rv = new MessagePanel("repeatingPanel",imi); add(rv); } }
My first component
The class MessagePanel is a Wicket component, attached with its own subset of HTML. DisplayMessagePanel is a Panel included in MessagePanel. When I wrote my code I thought it would be useful to add some more components, but I didn’t finnish. Anyway…
I really appreciate how Wicket describes components.
public class DisplayMessagePanel extends Panel { public DisplayMessagePanel(String id, final InnoteriaMessageIterator listOfMessages) { super(id); add(new RefreshingView("repeatingView") { @Override protected Iterator getItemModels() { return new ModelIteratorAdapter(listOfMessages.iterator()) { @Override protected IModel model(Object o) { return new CompoundPropertyModel((InnoteriaMessage) o); } }; } @Override protected void populateItem(Item item) { // Those 3 properties are read directly on InnoteriaMessage POJO // See CompoundPropertyModel javadoc for more details item.add(new Label("subject")); item.add(new Label("from")); item.add(new Label("content")); } }); } }
Conclusion
I hope you appreciate, I spent a lot of time to write this article. The idea was to demonstrate Apache Wicket and Apache Camel with a real use case. It would be interesting to write the Spring Integraton or the Mule version. Not a lot of work but I have already some article to complete…
If you appreciate reading this article, please vote for it on DZone
Source code:
touilleur_wicket_camel_1.0.tar.gz
Merci pour ton article qui est extrêmement intéressant. Ton exemple est très bien fait.
Pour ma part j’ai trouvé un framework Java pour le développement rapide (Play!) qui mérite le coup d’oeil. C’est un équivalent à Ruby on Rails mais en Java. Évidemment on est très loin de Wicket ou Spring.
Merci pour d’avoir fait cet article avec Wicket, ce fabuleux framework.