<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rich Internet Applications (RIA) &#187; Java</title>
	<atom:link href="http://www.canoo.com/blog/tag/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.canoo.com/blog</link>
	<description></description>
	<lastBuildDate>Wed, 18 Jan 2012 14:30:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>JavaOne 2011 Thursday and wrap-up</title>
		<link>http://www.canoo.com/blog/2011/10/07/javaone-2011-thursday-and-wrap-up/</link>
		<comments>http://www.canoo.com/blog/2011/10/07/javaone-2011-thursday-and-wrap-up/#comments</comments>
		<pubDate>Fri, 07 Oct 2011 05:15:10 +0000</pubDate>
		<dc:creator>Dierk</dc:creator>
				<category><![CDATA[Grails]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaFX]]></category>
		<category><![CDATA[JavaOne]]></category>
		<category><![CDATA[Swing]]></category>
		<category><![CDATA[canoo]]></category>
		<category><![CDATA[conference]]></category>
		<category><![CDATA[Dierk König]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2291</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/10/07/javaone-2011-thursday-and-wrap-up/";</script>Opinions expressed in the post are solely my own and not necessarily those of my employer. Thursday started with the Community Keynote. Well, it actually started with a 25 minutes IBM presentation about their cloud story. This had obviously nothing to do with the topic of the event and later speakers pointed this out rather [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/10/07/javaone-2011-thursday-and-wrap-up/";</script><div id="_mcePaste"><em>Opinions expressed in the post are solely my own and not necessarily those of my employer.</em></div>
<div><em><br />
</em></div>
<div>Thursday started with the Community Keynote. Well, it actually started with a 25 minutes IBM presentation about their cloud story. This had obviously nothing to do with the topic of the event and later speakers pointed this out rather frankly. At least it was interesting to hear that there is a job title like &#8220;Cloud Architect&#8221;.</div>
<div id="_mcePaste">The real part of the Community Keynote started with a <em>quiet moment to honor Steve Jobs</em>.</div>
<div id="_mcePaste">Later on, various winners of the Duke choice award and JUG luminaries cared for a lighter mood again, presented their work and asked the audience for participation in their local JUGs and in the advancement of Java via the OpendJDK. The JavaPosse appeared on stage and presented a funny show.</div>
<div id="_mcePaste">It was also announced that many of the JavaOne talks will be available on parleys.com, which provide by far the best experience when it comes to viewing live-captured talks.</div>
<div id="_mcePaste">Afterwards I attended the ZeroTurnaround (JRebel) talk on classloader issues. The rather big room (~300 ppl) was packed and left the impression that many Java developers share a common pain around classloaders. It was a good talk, covering the basics and typical pifalls. The only surprise for me was *how* easily you can end up with a classloader leak.</div>
<div id="_mcePaste">In order to improve my fathering skills, I went into Ken Sipe&#8217;s talk on &#8220;Rocking the Gradle&#8221;, where I met Adam Bien. Ken is a great presenter. However, convincing the crowd is a challenge especially as many Maven users seem to suffer from the Stockholm syndrome.</div>
<div id="_mcePaste">Then onto &#8220;Visualization of Geomaps and Topic Maps with JavaFX 2.0&#8243;, which had some interesting visuals captured <a href="http://www.lodgon.com/lodgon/NEWS/Artikelen/2010/9/22_Our_CTO_presented_a_JavaOne_session_on_JavaFX.html">here</a>.</div>
<div>For me JavaOne 2011 finished with Jim Clarke and Dean Iverson on GroovyFX, where they made some really good points suggesting that Groovy is the best language to drive the JavaFX 2.0 API.</div>
<div>As a side note, James Weaver introduced me to Jim Clarke by pointing out &#8220;He is from *<strong>Canoo</strong>*&#8221;. Then the discussion went into how well-known Canoo is in the community and that all employees must be true geniuses to achieve so much with so few people <img src='http://www.canoo.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </div>
<div>Fazit: Still, JavaOne is nowhere near where it was before the Oracle acquisition both in terms of size and in terms of being an unparalleled community experience. Distribution all over various hotels just doesn&#8217;t feel right. However, meeting friends has been and still remains the most important part of JavaOne and the conference still delivers on that account.</div>
<div id="_mcePaste">Important topics were new Java versions, JavaEE (+cloud), and Java for the Desktop with 50+ talks on JavaFX. Whenever the audience was asked about which alternative languages they use, Groovy was the clear winner. It appears that in the mainstream, Groovy has become the default choice for dynamic programming on the JVM.</div>
<div id="_mcePaste">The topic of concurrent programming was in my eyes underrepresented. Guillaume and myself had simple usage of GPars in our demos but for such a big and increasingly important topic the coverage should be much more extensive.</div>
<div>Finally, some visual impressions.</div>
<div>Good-bye SF</div>
<div>Dierk Koenig</div>
<p><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-01.jpg"><img class="alignnone size-medium wp-image-2292" title="j1-01" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-01-225x300.jpg" alt="" width="225" height="300" /></a><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-02.jpg"><img class="alignnone size-medium wp-image-2293" title="j1-02" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-02-300x225.jpg" alt="" width="300" height="225" /></a><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-03.jpg"><img class="alignnone size-medium wp-image-2294" title="j1-03" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-03-300x225.jpg" alt="" width="300" height="225" /></a><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-04.jpg"><img class="alignnone size-medium wp-image-2295" title="j1-04" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-04-225x300.jpg" alt="" width="225" height="300" /></a><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-17.jpg"><img class="alignnone size-medium wp-image-2296" title="j1-17" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-17-300x225.jpg" alt="" width="300" height="225" /></a><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-25.jpg"><img class="alignnone size-medium wp-image-2297" title="j1-25" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-25-300x225.jpg" alt="" width="300" height="225" /></a><a href="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-27.jpg"><img class="alignnone size-medium wp-image-2298" title="j1-27" src="http://www.canoo.com/blog/wp-content/uploads/2011/10/j1-27-300x225.jpg" alt="" width="300" height="225" /></a></p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/10/07/javaone-2011-thursday-and-wrap-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaOne 2011 Wednesday</title>
		<link>http://www.canoo.com/blog/2011/10/06/javaone-2011-wednesday/</link>
		<comments>http://www.canoo.com/blog/2011/10/06/javaone-2011-wednesday/#comments</comments>
		<pubDate>Thu, 06 Oct 2011 17:11:44 +0000</pubDate>
		<dc:creator>Dierk</dc:creator>
				<category><![CDATA[Grails]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaOne]]></category>
		<category><![CDATA[Swing]]></category>
		<category><![CDATA[Dierk König]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2286</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/10/06/javaone-2011-wednesday/";</script>Opinions expressed in this post are totally my own and not necessarily that of my employer. Wednesday started with the infamous &#8220;scriptbowl&#8221;, a competition between various scripting languages. This year the contenters were JRuby, Groovy, Scala, and Clojure. I wondered whether Scala considers itself a scripting language but obviously they either do or just seek [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/10/06/javaone-2011-wednesday/";</script><p><em>Opinions expressed in this post are totally my own and not necessarily that of my employer.</em></p>
<p>Wednesday started with the infamous &#8220;scriptbowl&#8221;, a competition between various scripting languages. This year the contenters were JRuby, Groovy, Scala, and Clojure. I wondered whether Scala considers itself a scripting language but obviously they either do or just seek the opportunity to be on stage.</p>
<p>To keep a long story short: <strong>Groovy has won this event for the third time in a row</strong>! This year the race was tied with Scala. Guillaume presented Groovy in the typical Groovy-idomatic style and explained every single line of his concurrent visual analyzer for Google+ postings. Dick Wall presented only non-idomatic Scala code. I interpret this as: to make Scala appealing you have to make it look like Groovy. Furthermore, he presented Kojo, which is a great interactive learning environment written in Play/Scala. In contrast to all other presentations, this was not specifically created for the scriptbowl, nor was it written by the presenter, nor was it clear how much effort went into it, nor did the audience see a single line the implementation code. How much this skewed the comparison, I leave to everybody&#8217;s judgement. The show was good, though.</p>
<p>I felt a bit sorry for Clojure. It is a great language and deserves a presentation that is more visually appealing to convince the crowd.</p>
<p>Afterwards, I attended a hands-on lab for &#8220;rapid enterprise development with netbeans&#8221;, which was essentially creating a Swing app for database CRUD actions. If I remember correctly, I did the exact same task 1997 with JBuilder. It left me with the feeling of &#8220;Yes, it works&#8221; but it is not less complex than it was 13 years ago.</p>
<p>Early afternoon Gerrit Grunwald (better known as @hansolo_) presented his work on simplified custom components for Swing. Given that he speaks about an activity that is both utterly important and highly underadvertised he would really deserve speaking at the center stage.</p>
<p>Graeme Rocher&#8217;s great session about Grails, polyglot datastores (hibernate, jpa, redis, mongodb, &#8230;), and the cloud was overshadowed by the news that Steve Jobs has died. Accidentally, the demo application was about showing a BBC News stream, which displayed this information live on stage. Both the presenter and the audience were equally touched.</p>
<p>The day officially ended with a big event at treasure island. I decided to not go there, though, and meet the former Canooey Denis Antonioli in Berkely where we had a great evening.</p>
<p>Dierk Koenig</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/10/06/javaone-2011-wednesday/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JavaOne 2011 Tuesday</title>
		<link>http://www.canoo.com/blog/2011/10/05/javaone-2011-tuesday/</link>
		<comments>http://www.canoo.com/blog/2011/10/05/javaone-2011-tuesday/#comments</comments>
		<pubDate>Wed, 05 Oct 2011 05:33:59 +0000</pubDate>
		<dc:creator>Dierk</dc:creator>
				<category><![CDATA[Groovy]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaOne]]></category>
		<category><![CDATA[conference]]></category>
		<category><![CDATA[Dierk König]]></category>
		<category><![CDATA[JavaFX]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2281</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/10/05/javaone-2011-tuesday/";</script>The Java strategy keynote started slowly with Juniper networks presenting their take on Java, which was in my eyes not really related to the topic of the keynote. It then went on into the Java roadmap with the announcement that new Java versions should come every two years, which sounded to me like an excuse [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/10/05/javaone-2011-tuesday/";</script><div id="_mcePaste">The Java strategy keynote started slowly with Juniper networks presenting their</div>
<div id="_mcePaste">take on Java, which was in my eyes not really related to the topic of the keynote.</div>
<div id="_mcePaste">It then went on into the Java roadmap with the announcement that new Java versions</div>
<div id="_mcePaste">should come every two years, which sounded to me like an excuse for Java 8 being</div>
<div id="_mcePaste">deferred until &#8220;Summer 2013&#8243;.</div>
<div id="_mcePaste">The real surprise was a demonstration of JavaFX running various devices like</div>
<div id="_mcePaste">tablets and smartphones running Windows, Android, and even iOS! It appeard to</div>
<div id="_mcePaste">be experimental but the sheer possibility makes a difference.</div>
<div id="_mcePaste">In addition, JavaFX will be fully open-source such that everybody is free to</div>
<div id="_mcePaste">port it to his platform of choice.</div>
<div id="_mcePaste">Over lunch, the &#8220;Java Desktop Community&#8221; assembled in a nearby restaurant.</div>
<div id="_mcePaste">That was an awesome opportunity for meeting the Swing and JavaFX luminaries just like in the years before.</div>
<div id="_mcePaste">In the early afternoon, I headed for the talk about custom JavaFX components</div>
<div id="_mcePaste">presented by Jonathan Giles and Jasper Potts. It appears customizing any</div>
<div id="_mcePaste">control is mainly done via CSS. In other words, there is no typesafe API.</div>
<div id="_mcePaste">I would rather prefer to use CSS only for &#8220;skinning&#8221; and keeping an API for</div>
<div id="_mcePaste">source-code integration.</div>
<div id="_mcePaste">It also came out that the current JavaFX version doesn&#8217;t contain e.g. a</div>
<div id="_mcePaste">ComboBox. This came as a surprise since I would expect this as being part</div>
<div id="_mcePaste">of the standard widget set. I curious what else is missing.</div>
<div id="_mcePaste">There also is a distinction between public and private APIs that didn&#8217;t</div>
<div id="_mcePaste">make immediate sense to me &#8211; other than the private parts are not yet</div>
<div id="_mcePaste">finished.</div>
<div id="_mcePaste">The afternoon JavaPosse BOF was rather disappointing. They re-told the</div>
<div id="_mcePaste">story of this morning&#8217;s keynote. Who needs that?</div>
<div id="_mcePaste">Visiting the pavillion was nice even though it was just as small as</div>
<div id="_mcePaste">last year. Anyway, I ran into a number of friends and dropped by the</div>
<div id="_mcePaste">gradleware booth. They liked my animated Gradle logo, that I implemented</div>
<div id="_mcePaste">with the Groovy-based FXG interpreter.</div>
<div id="_mcePaste">The SpringSource friends were just shutting down the booth and invited</div>
<div id="_mcePaste">me to dinner: http://t.co/LfxhjIH8 . Thanks a lot!</div>
<p>Finally, late in the evening I joined Dan Sline&#8217;s talk on WebServices in the Groovy space. The major take-away for me was a repercussion of the well-known advice: &#8220;keep it simple&#8221;.</p>
<p>Throughout the day, a lot of people approached me to tell how much they liked my talks yesterday. That was a really nice experience. Last year I had the very last talk of the conference and only this year I recognized how much of a difference the scheduling of the talks make.</p>
<p>Dierk Koenig</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/10/05/javaone-2011-tuesday/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java 7 Small Language Changes Screencast</title>
		<link>http://www.canoo.com/blog/2011/07/14/java-7-small-language-changes-screencast/</link>
		<comments>http://www.canoo.com/blog/2011/07/14/java-7-small-language-changes-screencast/#comments</comments>
		<pubDate>Thu, 14 Jul 2011 19:53:46 +0000</pubDate>
		<dc:creator>Hamlet</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Screencast]]></category>
		<category><![CDATA[hamlet]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2212</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/07/14/java-7-small-language-changes-screencast/";</script>This screencast demonstrates the small language changes that are part of Open JDK 7, which is available from the Open JDK website. It demonstrates multi-catch, try with resources, strings in switch statements, underscores in literals, and the diamond operator. If you have any issues watching the video below, then you may have better luck viewing [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/07/14/java-7-small-language-changes-screencast/";</script><p>This screencast demonstrates the <a href="http://openjdk.java.net/projects/coin/">small language changes</a> that are part of <a href="http://openjdk.java.net/">Open JDK 7</a>, which is available from the Open JDK website. It demonstrates multi-catch, try with resources, strings in switch statements, underscores in literals, and the diamond operator.</p>
<p>If you have any issues watching the video below, then you may have better luck viewing it on the <a href="http://tv.jetbrains.net/videocontent/java-7-small-language-changes">JetBrains.tv</a> site. </p>
<p><object width="400" height="300" id="_ipad" data="http://tv.jetbrains.net/flowplayer/flowplayer-3.2.7.swf" type="application/x-shockwave-flash"><param name="movie" value="http://tv.jetbrains.net/flowplayer/flowplayer-3.2.7.swf" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="flashvars" value='config={"clip":{"scaling":"orig","autoPlay":false,"autoBuffering":true,"url":"/sites/default/files/videos/converted/projectcoin.mp4"},"plugins":{"controls":{"stop":true}},"playlist":[{"scaling":"orig","autoPlay":false,"autoBuffering":true,"url":"http://tv.jetbrains.net/sites/default/files/videos/converted/projectcoin.mp4"}]}' /></object>
<p>I&#8217;ve made a lot of screencasts and blog posts over the years. If you like this, then there are many ways to see the other stuff I&#8217;ve done:&nbsp;</p>
<ul>
<li>My main blog:&nbsp;<a href="http://hamletdarcy.blogspot.com">http://hamletdarcy.blogspot.com</a></li>
<li>My other JetBrains.tv posts:&nbsp;<a href="http://tv.jetbrains.net/tags/hamlet">http://tv.jetbrains.net/tags/hamlet</a></li>
<li>IDEA&nbsp;related posts on my blog:&nbsp;<a href="http://hamletdarcy.blogspot.com/search/label/IDEA">http://hamletdarcy.blogspot.com/search/label/IDEA</a></li>
<li>My screencasts on YouTube:&nbsp;<a href="http://www.youtube.com/user/HamletDRC">http://www.youtube.com/user/HamletDRC</a></li>
<li>IDEA related Posts on my work blog:&nbsp;<a href="http://www.canoo.com/blog/tag/idea/">http://www.canoo.com/blog/tag/idea/</a></li>
<li>Or follow me on Twitter:&nbsp;<a href="http://twitter.com/hamletdrc">@HamletDRC</a></li>
</ul>
<p>The screencast was created with Ubuntu 10.04, PiTiVi, Audicity, gtk-RecordMyDesktop, IntelliJ IDEA, and LibreOffice. OS from top to bottom.</p>
<p>Thanks for watching, and leave a comment! </p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/07/14/java-7-small-language-changes-screencast/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
<enclosure url="http://tv.jetbrains.net/sites/default/files/videos/converted/projectcoin.mp4" length="10103645" type="video/mp4" />
		</item>
		<item>
		<title>Interview with Antonio Goncalves about the past, present, and future of Java EE</title>
		<link>http://www.canoo.com/blog/2011/03/31/interview-with-antonio-goncalves-about-the-past-present-and-future-of-java-ee/</link>
		<comments>http://www.canoo.com/blog/2011/03/31/interview-with-antonio-goncalves-about-the-past-present-and-future-of-java-ee/#comments</comments>
		<pubDate>Thu, 31 Mar 2011 09:58:32 +0000</pubDate>
		<dc:creator>Hamlet</dc:creator>
				<category><![CDATA[Interview]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[enterprise web apps]]></category>
		<category><![CDATA[hamlet]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2038</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/31/interview-with-antonio-goncalves-about-the-past-present-and-future-of-java-ee/";</script>I got a chance to sit down and talk to Java Champion Antonio Goncalves about the past, present, and future of Java EE. I&#8217;ve been working for the last six months in a heavy EE/SOA stack, and it&#8217;s been interesting to see the advantages and disadvantages. I definitely come from the other side of the [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/31/interview-with-antonio-goncalves-about-the-past-present-and-future-of-java-ee/";</script><p>I got a chance to sit down and talk to Java Champion <a href="http://www.antoniogoncalves.org/">Antonio Goncalves</a> about the past, present, and future of Java EE. I&#8217;ve been working for the last six months in a heavy EE/SOA stack, and it&#8217;s been interesting to see the advantages and disadvantages. I definitely come from the other side of the world where specifications aren&#8217;t seen as an inherent sign of quality, and frameworks not sanctioned by Sun/Oracle are not to be feared. It was fun to get his opinions about this stuff.</p>
<p>The full interview is on the <a href="http://jetbrains.dzone.com/articles/java-champion-antonio">JetBrains Zone at DZone</a>. We&#8217;re both JetBrains Academy Members and we&#8217;re slowly interviewing each other.</p>
<p>P.S. This is the first post I&#8217;ve ever made that mentioned Java Enterprise Edition. I suspect the next time EE is mentioned will be in another few years <img src='http://www.canoo.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/03/31/interview-with-antonio-goncalves-about-the-past-present-and-future-of-java-ee/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>IntelliJ IDEA Static Analysis: Custom Rules with Structural Search &amp; Replace</title>
		<link>http://www.canoo.com/blog/2011/03/29/intellij-idea-static-analysis-custom-rules-with-structural-search-replace/</link>
		<comments>http://www.canoo.com/blog/2011/03/29/intellij-idea-static-analysis-custom-rules-with-structural-search-replace/#comments</comments>
		<pubDate>Tue, 29 Mar 2011 09:12:20 +0000</pubDate>
		<dc:creator>Hamlet</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Screencast]]></category>
		<category><![CDATA[hamlet]]></category>
		<category><![CDATA[idea]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2035</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/29/intellij-idea-static-analysis-custom-rules-with-structural-search-replace/";</script>Well, well, I made another screencast. This time I&#8217;m taking on IntelliJ IDEA code inspections, and writing your own static code analysis rule (and quick fix!) using Structural Search &#038; Replace. Not bad for under 5 minutes. If you have any trouble viewing the video then perhaps you should watch it directly on the JetBrains [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/29/intellij-idea-static-analysis-custom-rules-with-structural-search-replace/";</script><p>Well, well, I made another screencast. This time I&#8217;m taking on IntelliJ IDEA code inspections, and writing your own static code analysis rule (and quick fix!) using Structural Search &#038; Replace. Not bad for under 5 minutes. </p>
<p>If you have any trouble viewing the video then perhaps you should watch it directly on the <a href="http://tv.jetbrains.net/videocontent/intellij-idea-static-analysis-custom-rules-with-structural-search-replace">JetBrains site</a>. And if you&#8217;re feeling generous, then <a href="http://www.dzone.com/links/intellij_idea_static_code_analysis_screencast.html">clicky clicky</a> to upvote at DZone. </p>
<p><object width="400" height="300" id="_player" name="_player" data="http://tv.jetbrains.net/sites/default/files/flowplayer3/flowplayer-3.2.2.swf" type="application/x-shockwave-flash"><param name="movie" value="http://tv.jetbrains.net/sites/default/files/flowplayer3/flowplayer-3.2.2.swf" /><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="flashvars" value='config={"clip":{"baseUrl":"http://tv.jetbrains.net","scaling":"orig","autoPlay":false,"autoBuffering":true,"url":"sites/default/files/videos/converted/ideastaticanalysis.flv"},"plugins":{"controls":{"stop":true}},"playlist":[{"baseUrl":"http://tv.jetbrains.net","scaling":"orig","autoPlay":false,"autoBuffering":true,"url":"http://tv.jetbrains.net/sites/default/files/videos/converted/ideastaticanalysis.flv"}]}' /></object></p>
<p>Check out these pages for more information on IDEA&nbsp;inspections:&nbsp;</p>
<ul>
<li>JetBrains&#8217; <a href="http://www.jetbrains.com/idea/features/code_inspection.html">Code Inspection page</a>&nbsp;</li>
<li>IDEA&#8217;s <a href="http://www.jetbrains.com/idea/webhelp/inspecting-source-code.html">Webhelp for inspections</a></li>
</ul>
<p>Read these pages to learn more about Structural Search and Replace:&nbsp;</p>
<ul>
<li>JetBrains&#8217; <a href="http://www.jetbrains.com/idea/documentation/ssr.html">Structural Search and Replace Documentation</a></li>
<li>IDEA&#8217;s <a href="http://www.jetbrains.com/idea/webhelp/structural-search-and-replace.html">Webhelp for Structural Search and Replace</a></li>
</ul>
<p>I&#8217;ve made a lot of screencasts and blog posts over the years. If you like this, then there are many ways to see the other stuff I&#8217;ve done:&nbsp;</p>
<ul>
<li>My main blog:&nbsp;<a href="http://hamletdarcy.blogspot.com">http://hamletdarcy.blogspot.com</a></li>
<li>My other JetBrains.tv posts:&nbsp;<a href="http://tv.jetbrains.net/tags/hamlet">http://tv.jetbrains.net/tags/hamlet</a></li>
<li>IDEA&nbsp;related posts on my blog:&nbsp;<a href="http://hamletdarcy.blogspot.com/search/label/IDEA">http://hamletdarcy.blogspot.com/search/label/IDEA</a></li>
<li>My screencasts on YouTube:&nbsp;<a href="http://www.youtube.com/user/HamletDRC">http://www.youtube.com/user/HamletDRC</a></li>
<li>IDEA related Posts on my work blog:&nbsp;<a href="http://www.canoo.com/blog/tag/idea/">http://www.canoo.com/blog/tag/idea/</a></li>
<li>Or follow me on Twitter:&nbsp;<a href="http://twitter.com/hamletdrc">@HamletDRC</a></li>
</ul>
<p>Phew, that&#8217;s a lot of self-promotion <img src='http://www.canoo.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Thanks for watching, and leave a comment!&nbsp;</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/03/29/intellij-idea-static-analysis-custom-rules-with-structural-search-replace/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://tv.jetbrains.net/sites/default/files/videos/converted/ideastaticanalysis.flv" length="4338395" type="video/x-flv" />
		</item>
		<item>
		<title>LEGO&#174; Java (IV): Apache Camel, Spring and ElasticSearch</title>
		<link>http://www.canoo.com/blog/2011/03/24/lego-java-iv-apache-camel-spring-and-elasticsearch/</link>
		<comments>http://www.canoo.com/blog/2011/03/24/lego-java-iv-apache-camel-spring-and-elasticsearch/#comments</comments>
		<pubDate>Thu, 24 Mar 2011 11:38:27 +0000</pubDate>
		<dc:creator>alberto</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[alberto]]></category>
		<category><![CDATA[camel]]></category>
		<category><![CDATA[eip]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=2017</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/24/lego-java-iv-apache-camel-spring-and-elasticsearch/";</script>code { font-size: 12px; overflow: auto; } If you don&#8217;t know it yet, this is the fourth part of a series about Apache Camel. The previous posts can be found here: first part, second part and third part. In this fourth article, we are going to show how to properly use the Spring framework in [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/24/lego-java-iv-apache-camel-spring-and-elasticsearch/";</script><style type="text/css">
    code {
        font-size: 12px;
        overflow: auto;
    }
</style>
<p>If you don&#8217;t know it yet, this is the fourth part of a series about <a href='http://camel.apache.org'>Apache Camel</a>. The previous posts can be found here: <a href='http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics'>first part</a>, <a href='http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/'>second part</a> and <a href='http://www.canoo.com/blog/2011/03/21/lego-java-iii-apache-camel-routing-and-testing/'>third part</a>.<br />
In this fourth article, we are going to show how to properly use the <a href='http://www.springsource.org/about'>Spring framework</a> in the sample application, while adding some extra functionality to it.</p>
<h3>Extracting RSS feeds</h3>
<p>If you remember what we have done so far, the sample application is capable of, given the URL of a web page, extract the text content of the body paragraphs. To make our application a little more powerful, let&#8217;s add some functionality to extract the contents of web pages listed in RSS feeds. To accomplish this, add a new class called &#8220;RssExtractorRoutes&#8221; with the following content:<br />
<pre><code>
public class RssExtractorRoutes extends RouteBuilder {
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;rss:http://feeds.bbci.co.uk/news/rss.xml?splitEntries=false&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.marshal().rss()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.marshal().string()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.split(xpath(&quot;//item/link/text()&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Link: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(PAGE_EXTRACTOR_EP);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
This simple route uses the Camel <a href='http://camel.apache.org/rss.html'>&#8220;rss&#8221;</a> endpoint to access the RSS feed from BBC news, transforms the content with the rss &#8220;marshaller&#8221; and then extracts the article links by mean of XPath. As last step, it routes the extracted links to our already existing &#8220;page extractor&#8221; route.</p>
<p>Because we have added a new component, we need to add a new dependency to our Maven pom file:<br />
<pre><code>
&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp; &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp; &lt;artifactId&gt;camel-rss&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp; &lt;version&gt;${camel.version}&lt;/version&gt;
&lt;/dependency&gt;
</code></pre><br />
We need to apply this new route builder to the camel context and we will also eliminate the code used to send a URL to the extractor route. For this, adjust the code of the main class like this:<br />
<pre><code>
public class Part4 {
&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DefaultCamelContext camelContext = new DefaultCamelContext();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.addRoutes(new PageExtractorRoutes());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.addRoutes(new HtmlImproverRoutes());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.addRoutes(new RssExtractorRoutes());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.start();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(100000);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.stop();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
If you run the application, you should see in the log entries that all the articles present in the feed are routed to the extractor and afterward, the extracted paragraphs get lost in the space.<br />
Let&#8217;s do something useful with them and store them in a search index. For that, we could use the Apache Lucene <a href='http://camel.apache.org/lucene'>&#8220;endpoint&#8221;</a> that Camel already offers, but instead of this, let&#8217;s use an <a href='http://www.elasticsearch.org/'>Elastic Search</a> node and explore how to configure and manage the life cycle of an Elastic Search instance by mean of Spring.<br />
We need a new Elastic Search &#8220;bean&#8221; class with this content:<br />
<pre><code>
public class ElasticSearchBean {
&nbsp;&nbsp;&nbsp;&nbsp;public static final int DEFAULT_MAX_RESULTS = 50;

&nbsp;&nbsp;&nbsp;&nbsp;public static final String INDEX_METHOD = &quot;index&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;public static final String SEARCH_METHOD = &quot;search&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;public static final String ID_HEADER = &quot;search_id&quot;;

&nbsp;&nbsp;&nbsp;&nbsp;private final Client fClient;
&nbsp;&nbsp;&nbsp;&nbsp;private final Node fNode;
&nbsp;&nbsp;&nbsp;&nbsp;private final String fIndex;
&nbsp;&nbsp;&nbsp;&nbsp;private final String fType;
&nbsp;&nbsp;&nbsp;&nbsp;private final String fField;
&nbsp;&nbsp;&nbsp;&nbsp;private int fMaxResults = DEFAULT_MAX_RESULTS;

&nbsp;&nbsp;&nbsp;&nbsp;public ElasticSearchBean(String index, String type, String field) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fIndex = index;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fType = type;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fField = field;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fNode = nodeBuilder().local(true).node();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fClient = fNode.client();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getIndex() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fIndex;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getType() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fType;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String getField() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fField;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public int getMaxResults() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fMaxResults;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public void setMaxResults(int maxResults) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fMaxResults = maxResults;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public List&lt;String&gt; search(String query) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SearchResponse searchResponse = search(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getField(), query,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getMaxResults(), getIndex()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;String&gt; results = new ArrayList&lt;String&gt;();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (SearchHit hit : searchResponse.getHits()) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results.add(hit.id());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return results;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public void index(Exchange exchange) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Message in = exchange.getIn();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String id = in.getHeader(ID_HEADER, String.class);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String content = in.getBody(String.class);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;index(getIndex(), getType(), id, getField(), content);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (IOException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public void close() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fNode.close();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fClient.close();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;private IndexResponse index(String index, String type, String id, String fieldName, String fieldValue) throws IOException {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;XContentBuilder item = jsonBuilder()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.startObject()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.field(fieldName, fieldValue)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.endObject();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fClient.prepareIndex(index, type, id)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setSource(item)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.execute()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.actionGet();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;private SearchResponse search(String fieldName, String query, int maxResults, String... indexes) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fClient.prepareSearch(indexes)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setSearchType(SearchType.DEFAULT)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setQuery(termQuery(fieldName, query))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setFrom(0).setSize(maxResults).setExplain(true)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.execute()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.actionGet();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
This class is a little bit more complicated than what we have done until now, but what it basically does is creating an elastic search &#8220;local&#8221; node, offering a method to index a content under a certain structure and another method to search for a text within the previously indexed contents. Some aspects of the bean are also configurable: the name of the index, the type of the content, the index field where the content will be stored and the number of returned search results.</p>
<p>To use it in the RSS extractor, adjust the code like this:<br />
<pre><code>
public class RssExtractorRoutes extends RouteBuilder {
&nbsp;&nbsp;&nbsp;&nbsp;private static final String ARTICLES_INDEX = &quot;articles&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;private static final String ARTICLE_CONTENT_FIELD = &quot;content&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;private static final String ARTICLE_TYPE = &quot;article&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;private static final ElasticSearchBean ELASTIC_SEARCH_BEAN = new ElasticSearchBean(ARTICLES_INDEX, ARTICLE_TYPE, ARTICLE_CONTENT_FIELD);

&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;rss:http://feeds.bbci.co.uk/news/rss.xml?splitEntries=false&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.marshal().rss()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.marshal().string()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.split(xpath(&quot;//item/link/text()&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setHeader(ID_HEADER, body())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Link: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(PAGE_EXTRACTOR_EP)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.bean(ELASTIC_SEARCH_BEAN, INDEX_METHOD);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
And finally, add the Eleasctic Search dependency to the Maven pom file:<br />
<pre><code>
&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.elasticsearch&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;0.15.2&lt;/version&gt;
&lt;/dependency&gt;
</code></pre><br />
If you paid attention to the changes in the RSS extractor route builder, you should have noticed that now we have created an static instance and configured it in an static way. This is not completely bad but it has some disadvantages and one problem:</p>
<ul>
<li>If we would like to test this route, we cannot supply a different implementation of the bean.</li>
<li>In case that we want to change the name of the index, the name of the field or any other configurable value, we have to do it in the code and recompile the application.</li>
<li>When the bean is created, the Elastic Serach node is correctly initialized but, when is the &#8220;close&#8221; method called to shutdown the instance properly? This is, of course, the problem I mentioned.</li>
</ul>
<p>To correct these issues, we can take advantage of the Spring framework and the excellent Spring support that Camel offers.</p>
<p>First of all, let&#8217;s include the Spring dependency in our Maven pom file:<br />
<pre><code>
&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-spring&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&lt;/dependency&gt;
</code></pre><br />
Now, let&#8217;s create a file named &#8220;camel-context.xml&#8221; under &#8220;resources/META-INF/spring&#8221; with this content:<br />
<pre><code>
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xsi:schemaLocation=&quot; http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;packageScan&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;package&gt;com.canoo.camel&lt;/package&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/packageScan&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/camelContext&gt;
&lt;/beans&gt;
</code></pre><br />
And as last step, adpat the &#8220;main&#8221; class like this:<br />
<pre><code>
import org.apache.camel.spring.Main;

public class Part4 {
&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Main main = new Main();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main.enableHangupSupport();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;main.start();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
If you run the application now, the only difference that you should notice is that the application does not stop after 100 seconds.<br />
By using the &#8220;Main&#8221; class that the Camel Spring support offers, we are starting the Spring container and looking for Spring configuration files that will be automatically loaded and used to configure the Spring container. Such files should have the extension &#8220;.xml&#8221; and be placed under the &#8220;META-INF/spring&#8221; package.<br />
The &#8220;enableHangupSupport&#8221; call instructs the application to listen to &#8220;ctrl-c&#8221; key strokes and, before terminating, to stop properly the Spring container.</p>
<p>Now that we have configured Spring, let&#8217;s move the Elastic Search bean into the Spring beans file (&#8220;camel-context.xml&#8221;):<br />
<pre><code>
&lt;bean class=&quot;com.canoo.camel.beans.ElasticSearchBean&quot; id=&quot;elasticSearchBean&quot; scope=&quot;singleton&quot; destroy-method=&quot;close&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg value=&quot;articles&quot;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg value=&quot;article&quot;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg value=&quot;content&quot;/&gt;
&lt;/bean&gt;
</code></pre><br />
And adjust the Rss extractor route like this:<br />
<pre><code>
@Override
public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;rss:http://feeds.bbci.co.uk/news/rss.xml?splitEntries=false&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.marshal().rss()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.marshal().string()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.split(xpath(&quot;//item/link/text()&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setHeader(ID_HEADER, body())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Link: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(PAGE_EXTRACTOR_EP)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.beanRef(&quot;elasticSearchBean&quot;, INDEX_METHOD);
}
</code></pre><br />
What we have done with these changes is: letting Spring instantiate and configure an unique instance of our bean (a singleton) and instructing Spring to invoke the &#8220;close&#8221; method whenever the container is destroyed (what happens when the user presses &#8220;ctrl-c&#8221; on the shell).<br />
Because now the bean will be searched by name in the Camel bean registry (the Spring container in our case), in order to substitute the bean implementation with another one, it would be enough to use a different spring configuration file or to overwrite the bean definition by loading a second beans file with a new definition of the same bean. This is a convenient thing if, for example, we want to use mocks for testing or want to have different search services.<br />
To avoid having to edit the spring beans file in order to change the Elastic Search bean configuration values, we can use a spring &#8220;PropertyPlaceHolderConfigurer&#8221;  and create a properties file in the classpath (we will store it within the application and under the &#8220;resources&#8221; directory):<br />
<pre><code>
&lt;bean class=&quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name=&quot;location&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;elasticsearch.properties&lt;/value&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;
&lt;/bean&gt;

&lt;bean class=&quot;com.canoo.camel.beans.ElasticSearchBean&quot; id=&quot;elasticSearchBean&quot; scope=&quot;singleton&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;destroy-method=&quot;close&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg value=&quot;${index_name}&quot;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg value=&quot;${content_type}&quot;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;constructor-arg value=&quot;${index_field}&quot;/&gt;
&lt;/bean&gt;
</code></pre><br />
With this configuration change, Spring searches in the top-level package for a properties file with the name &#8220;elasticsearch.properties&#8221; and resolves the &#8220;${}&#8221; values against it, giving us more flexibility to alter these values without affecting the application.</p>
<p>To end this article doing something useful with our indexed contents, let&#8217;s create a search web service that allows us executing a search query and displays a web page with the matching article links.</p>
<p>For this, create a new route builder:<br />
<pre><code>
public class SearchServiceRoutes extends RouteBuilder {
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;jetty://http://0.0.0.0:8080/search&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setBody(header(&quot;query&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(&quot;direct:search&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.bean(HtmlFormatterBean.class, AS_SEARCH_RESULTS_PAGE);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;direct:search&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Searching: &#039;${body}&#039;.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.beanRef(&quot;elasticSearchBean&quot;, SEARCH_METHOD)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.bean(HtmlFormatterBean.class, AS_LINKS)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Found results: &#039;${body}&#039;.&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
Notice that now, it is not necessary to add explicitly the new route builders to the camel context due to the fact that Spring will instantiate the context and look for route builders under the package &#8220;com.canoo.camel&#8221; (as configured in &#8220;camel-context.xml&#8221;).</p>
<p>We need also to extend the functionality of the class &#8220;HtmlFormatterBean&#8221; as follows:<br />
<pre><code>
public class HtmlFormatterBean {
&nbsp;&nbsp;&nbsp;&nbsp;public static final String AS_EXTRACTED_RESULTS_PAGE = &quot;asExtractedResultsPage&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;public static final String AS_SEARCH_RESULTS_PAGE = &quot;asSearchResultsPage&quot;;
&nbsp;&nbsp;&nbsp;&nbsp;public static final String AS_LINKS = &quot;asLinks&quot;;

&nbsp;&nbsp;&nbsp;&nbsp;public String asExtractedResultsPage(List&lt;String&gt; contents) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return asPage(contents, &quot;Extracted contents:&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public String asSearchResultsPage(List&lt;String&gt; contents) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return asPage(contents, &quot;Search results:&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;private String asPage(List&lt;String&gt; contents, String title) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer stringBuffer = new StringBuffer();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(String.format(&quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;%s&lt;/h1&gt;&lt;ul&gt;&quot;, title));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (String content : contents) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(&quot;&lt;li&gt;&quot;).append(content).append(&quot;&lt;/li&gt;&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(&quot;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return stringBuffer.toString();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public List&lt;String&gt; asLinks(List&lt;String&gt; urls) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;String&gt; result = new ArrayList&lt;String&gt;();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (String url : urls) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.add(String.format(&quot;&lt;a href=&#039;%1$s&#039;&gt;%1$s&lt;/a&gt;&quot;, url));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return result;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
Please notice that, because now this bean has more than only one method, you will also need to specify the method to call in every bean reference within the application.</p>
<p>To test our new search service, start the application and point to the URL: <a href='http://localhost:8080/search?query=news'>http://localhost:8080/search?query=news</a>. If some of the indexed articles contains the word &#8220;news&#8221;, you should see a list with their links.</p>
<p>The camel ride ends here. I hope that you enjoyed reading the articles as much as I enjoyed writing them. I also hope that it has helped you in getting to know how Camel works and that you can found some use cases where to apply this slightly different way of building integration applications.</p>
<p>The code of this fourth part is <a href='http://www.canoo.com/blog/wp-content/uploads/2011/03/Part4.zip'>here</a>, to execute the application just unzip the file, change to the directory where the pom file is and type &#8216;mvn compile exec:java -Dexec.mainClass=&#8221;com.canoo.camel.Part4&#8243;&#8216; in the console.</p>
<p>Hope to see you soon in another post!</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/03/24/lego-java-iv-apache-camel-spring-and-elasticsearch/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>LEGO&#174; Java (III): Apache Camel Routing and Testing</title>
		<link>http://www.canoo.com/blog/2011/03/21/lego-java-iii-apache-camel-routing-and-testing/</link>
		<comments>http://www.canoo.com/blog/2011/03/21/lego-java-iii-apache-camel-routing-and-testing/#comments</comments>
		<pubDate>Mon, 21 Mar 2011 13:29:50 +0000</pubDate>
		<dc:creator>alberto</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[alberto]]></category>
		<category><![CDATA[camel]]></category>
		<category><![CDATA[eip]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=1969</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/21/lego-java-iii-apache-camel-routing-and-testing/";</script>code { font-size: 12px; overflow: auto; } In the first part of this series we saw the basics of Apache Camel routing and contexts, and in the second part we learned about error handling and web services. Today, in the third part, we will add more functionality to our Camel extractor application and we will [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/21/lego-java-iii-apache-camel-routing-and-testing/";</script><style type="text/css">
    code {
        font-size: 12px;
        overflow: auto;
    }
</style>
<p>In the <a href='http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics'>first part</a> of this series we saw the basics of Apache Camel routing and contexts, and in the  <a href='http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/'>second part</a> we learned about error handling and web services. Today, in the third part, we will add more functionality to our Camel extractor application and we will do it in a modular way. We will also have a look at how to effectively test our functionality.</p>
<h3>Improving our HTML cleaner</h3>
<p>In a perfect world, our application would never need any bug fixing or improvements. But because we know that the world is not perfect, it happens often that we discover limitations in our applications and we must make some improvements to correct them. If you try extracting a URL from DZone (&#8220;<a href='http://www.dzone.com'>http://www.dzone.com</a>&#8220;) with the application, then you will see an exception in the console indicating that <a href='http://ccil.org/~cowan/XML/tagsoup/'>&#8220;TagSoup&#8221;</a> cannot fix a problem related with the DOM structure (TagSoup is used by the &#8220;TidyMarkup&#8221; marshaller to clean our HTML) . We could configure &#8220;TagSoup&#8221; differently, but instead, let&#8217;s assume that we have to use another tool to do the work. In this case, we are going to use <a href='http://htmlcleaner.sourceforge.net/'>&#8220;HtmlCleaner&#8221;</a> and we&#8217;ll integrate it into our application using a Java &#8220;bean&#8221;.</p>
<p>Add a new Java class to your project containing this code:<br />
<pre><code>
public class HtmlCleanerBean {
&nbsp;&nbsp;&nbsp;&nbsp;public Document cleanHtml(String html) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CleanerProperties properties = new CleanerProperties();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;properties.setNamespacesAware(false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HtmlCleaner cleaner = new HtmlCleaner(properties);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TagNode node = cleaner.clean(html);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return new DomSerializer(properties).createDOM(node);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (ParserConfigurationException e) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException(e);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre></p>
<p>This class uses &#8220;HtmlCleaner&#8221; and therefore requires the following Maven dependency:<br />
<pre><code>
&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;net.sourceforge.htmlcleaner&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;htmlcleaner&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;2.2&lt;/version&gt;
&lt;/dependency&gt;
</code></pre></p>
<p>We could add more steps to our extractor route, but it is starting to become a little verbose and unclear. So instead, let&#8217;s better add a new route for our improved html functionality:<br />
<pre><code>
public class HtmlImproverRoutes extends RouteBuilder {
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;direct:html_improver&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.onException(CamelException.class)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.continued(true)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Tidying failed, trying with Cleaning.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.bean(HtmlCleanerBean.class)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.unmarshal().tidyMarkup();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre></p>
<p>And replace the tidy markup marshalling with a detour through the new created route:<br />
<pre><code>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;direct:page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.streamCaching()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.onException(HttpOperationFailedException.class)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.onWhen(bean(HttpErrorHelperBean.class).isEqualTo(true))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Fetching URL failed: &#039;${exception.message}&#039;, trying with relocation: &#039;${exception.redirectLocation}&#039;.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.handled(true)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setBody(simple(&quot;${exception.redirectLocation}&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(&quot;direct:page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setHeader(Exchange.HTTP_URI, body())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setBody(constant(null))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Extracting content from: &#039;${header.&quot; + Exchange.HTTP_URI + &quot;}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.pipeline(&quot;http:extractor&quot;, &quot;direct:html_improver&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Html from: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.split(xpath(&quot;//body//p/text()&quot;), new SplitterAggregationStrategy(&quot;(?s).*[A-Za-z0-9].*&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Text chunk: &#039;${body}&#039;.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end();
</code></pre></p>
<p>To get this route created in the Camel context, adjust the main method in the &#8220;main&#8221; class like this:<br />
<pre><code>
public static void main(String[] args) throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;DefaultCamelContext camelContext = new DefaultCamelContext();
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.addRoutes(new PageExtractorRoutes());
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.addRoutes(new HtmlImproverRoutes());
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.start();

&nbsp;&nbsp;&nbsp;&nbsp;ProducerTemplate template = camelContext.createProducerTemplate();
&nbsp;&nbsp;&nbsp;&nbsp;String result = template.requestBody(&quot;direct:page_extractor&quot;, &quot;http://www.dzone.com&quot;, String.class);
&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf(&quot;Extracted: &#039;%s&#039;.\n&quot;, result);

&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(100000);
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.stop();
}
</code></pre><br />
As you can see, in the new &#8220;HtmlImproverRoutes&#8221; class, we have applied an &#8220;onException&#8221; clause to detect when &#8220;TagSoup&#8221; html parsing fails. The difference this time is that we no longer use &#8220;handled&#8221; to end the processing of the message (as we were in the previous case, where we redelivered the message to the same endpoint). This time, we use &#8220;continued&#8221; to instruct Camel to send the message through the error handling steps and then continue processing from where the error occurred.</p>
<p>We have also performed some changes in the page extractor route. First, we have activated the &#8220;streamcaching&#8221; to avoid exceptions when the inputstream returned by the &#8220;http&#8221; extractor is consumed several times. Also, after setting the URL to extract in the &#8220;CamelHttpUri&#8221; header, we are now resetting the body of the message sent to the &#8220;http&#8221; endpoint. If not done, the body causes some problems when accessing certain web servers because the endpoint uses the HTTP POST method (used when the body is not empty) instead of the more suitable GET. The last change is to route the result of the &#8220;http&#8221; endpoint to the newly created route. To enforce a little bit more the sequential character of this process, we have applied the &#8220;pipeline&#8221; enterprise integration pattern instead of the default &#8220;to&#8221; construct of the Java DSL.</p>
<p>If you run the application now, you should see how it detects the parsing error and continues the processing of the content using &#8220;HtmlCleaner&#8221; instead of &#8220;TagSoup&#8221;.<br />
This functionality (the &#8220;html improver&#8221;) has been moved from the initial route builder to an specific one, what contributes a to better modularization and re-usability of the code in Apache Camel. Because the endpoints are named with string constants and now the strings: &#8220;direct:page_extractor&#8221; and &#8220;direct:html_improver&#8221; appear in different files, it is usually a good practice to extract these values as constants in the route builders. This, makes more difficult to get the code broken in case of a change in the endpoint name or type, and it plays the role of an interface.</p>
<h3>Testing the routes</h3>
<p>Apache Camel offers <a href='http://camel.apache.org/mock.html'>mock testing</a> support for testing the routes in an effective way. But, while in most of the examples out there, a mock endpoint is added explicitly to the route, let&#8217;s see how to do it in a programmatic way to avoid altering the route that we want to test.</p>
<p>First, add these two dependencies to your Maven pom file:<br />
<pre><code>
...
&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-test&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;junit&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;junit&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;4.8.1&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
...
</code></pre></p>
<p>Second, add this class with some run-time support for mock testing:<br />
<pre><code>
public class CamelMockTestSupport extends CamelTestSupport {
&nbsp;&nbsp;&nbsp;&nbsp;private MockEndpoint fMockEndpoint;

&nbsp;&nbsp;&nbsp;&nbsp;public MockEndpoint getMockEndpoint() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (null == fMockEndpoint) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fMockEndpoint = new MockEndpoint(&quot;mock:_mock_&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return fMockEndpoint;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;protected void sendBodyToMock(String endpoint, final Object body) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exchange exchange = template.request(endpoint, new Processor() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void process(Exchange exchange) throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exchange.getIn().setBody(body);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exchange.getIn().copyFrom(exchange.getOut());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;template.send(getMockEndpoint(), exchange);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre></p>
<p>Third, create a test based on this approach:<br />
<pre><code>
...
import static com.canoo.camel.routes.HtmlImproverRoutes.HTML_IMPROVER_EP;

public class HtmlImproverRoutesTest extends CamelMockTestSupport {
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;protected RouteBuilder createRouteBuilder() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return new HtmlImproverRoutes();
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;@Test
&nbsp;&nbsp;&nbsp;&nbsp;public void testHtmlImproverRoute_withBadHtml() throws InterruptedException {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MockEndpoint mock = getMockEndpoint();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mock.expectedMessageCount(1);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mock.expectedBodiesReceived(&quot;&lt;html xmlns:html=\&quot;http://www.w3.org/1999/xhtml\&quot;&gt;\n&quot; +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;&lt;body&gt;\n&quot; +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;&lt;h1&gt;Bad Html&lt;/h1&gt;\n&quot; +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;&lt;/body&gt;\n&quot; +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;&lt;/html&gt;\n&quot;);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sendBodyToMock(HTML_IMPROVER_EP, &quot;&lt;html&gt;&lt;h1&gt;Bad Html&lt;/html&gt;&quot;);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mock.assertIsSatisfied();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
If you run this test, you should see a nice green bar (or the like) indicating that the test passed. Please, note the static import for &#8220;HTML_IMPROVER_EP&#8221;, which contains the name of the endpoint, extracted as a constant (as explained before).<br />
As of Apache Camel 2.7, some advice functionality has been added to mock testing making possible introduce the mock endpoints in an aspect oriented way.</p>
<p>As summary of this third part, we have shown how to organize the project routes using several route builders and how you can use mocks to test and, therefore, improve the quality and stability of your Apache Camel project.</p>
<p>In the next Camel &#8220;ride&#8221;, we will introduce the Spring framework and unveil what to do with the extracted data. Hope to see you there!</p>
<p><a href='http://www.canoo.com/blog/wp-content/uploads/2011/03/Part3.zip'>Source code</a> of this third part for download. To run the application, just expand the file, change to the folder where the pom file is and execute: &#8216;mvn compile exec:java -Dexec.mainClass=&#8221;com.canoo.camel.Part3&#8243;&#8216;</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/03/21/lego-java-iii-apache-camel-routing-and-testing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>LEGO&#174; Java (II): Apache Camel Error Handling, Java Beans and Web Services</title>
		<link>http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/</link>
		<comments>http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/#comments</comments>
		<pubDate>Wed, 16 Mar 2011 11:27:20 +0000</pubDate>
		<dc:creator>alberto</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[alberto]]></category>
		<category><![CDATA[camel]]></category>
		<category><![CDATA[eip]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=1939</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/";</script>code { font-size: 12px; overflow: auto; } The first part of this series showed you how to start a Camel context, write a simple route, and then stop the context. You&#8217;re starting to see how to do some things in the &#8220;Camel way&#8221;. Let&#8217;s go for a second Camel ride and see how far we [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/";</script><style type="text/css">
    code {
        font-size: 12px;
        overflow: auto;
    }
</style>
<p>The <a href='http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics'>first part</a> of this series showed you how to start a Camel context, write a simple route, and then stop the context. You&#8217;re starting to see how to do some things in the &#8220;Camel way&#8221;. Let&#8217;s go for a second Camel ride and see how far we can go.</p>
<h3>Error Handling in Camel</h3>
<p>As promised, we&#8217;re going to add some error handling to our route and see how to deal with the usual problems. To see what I mean, go to the main class from the first article and substitute the URL of the W3C site (&#8220;http://www.w3.org&#8221;) with this alternate version: &#8220;http://www.w3c.org&#8221;. Run the application once again and now you should see the application failing to retrieve the page with a &#8220;HttpOperationFailedException&#8221; error. The problem is that the new URL is a redirect to the old one, but our route doesn&#8217;t know how to deal with this. </p>
<p>If you have a look at the &#8220;http&#8221; endpoint <a href='http://camel.apache.org/http.html'>documentation</a>, you will notice how flexible and configurable it is. You&#8217;ll also see that some HTTP response codes are thrown as exceptions by default. In plain Java, you can instruct a connection to follow redirections in a transparent way. With the &#8220;http&#8221; configuration options, we could probably do the same by configuring the &#8220;HttpClient&#8221; and modifying the &#8220;http&#8221; endpoint it uses internally. Instead of this, let&#8217;s learn some error handling strategies in Camel.</p>
<p>We want some sepcial logic for when an &#8220;HttpOperationFailedException&#8221; is thrown: we should grab the redirection location present in the exception and retry the request. For this, modify the code of the extractor route in the following way:<br />
<pre><code>
@Override
public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;direct:page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.onException(HttpOperationFailedException.class)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Fetching URL failed: &#039;${exception.message}&#039;, trying with relocation: &#039;${exception.redirectLocation}&#039;.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.handled(true)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setBody(simple(&quot;${exception.redirectLocation}&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(&quot;direct:page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setHeader(Exchange.HTTP_URI, body())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Extracting content from: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(&quot;http:extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.unmarshal().tidyMarkup()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Html from: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.split(xpath(&quot;//body//p/text()&quot;), new SplitterAggregationStrategy(&quot;(?s).*[A-Za-z0-9].*&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Text chunk: &#039;${body}&#039;.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end();
}
</code></pre><br />
If you re-run the application now, there is no exception in the console and a new log entry indicates the URL failed and fetching was retried using the redirection location.</p>
<p>What we introduced at the beginning of the route is an &#8220;onException&#8221; clause that, in case of an exception of the given type (or types) being thrown within this route, executes the steps contained in the clause. Please, notice the &#8220;end&#8221; method call separating what should be done in case of such an exception from the normal route steps.</p>
<p>In the error handling code, the first action we take is to log some information about what is about to happen. Then, we mark that the exception has already been handled and, after setting in the body the relocation URL, we send the message back to the endpoint to be retried.</p>
<p>The &#8220;onException&#8221; clause has a lot of options and the topic &#8220;error handling&#8221; is very well described in the chapter 5 from <a href='http://www.manning.com/ibsen/'>Camel in Action</a>.</p>
<h3>Fine grained logic with Java beans</h3>
<p>If you need functionality that you cannot express with the Camel Java DSL, then using Java or any other <a href='http://camel.apache.org/languages.html'>languages supported in Camel</a> is pretty easy. One option is to use the &#8220;process&#8221; method from the Java DSL. This method accepts a &#8220;Processor&#8221; parameter in through which you have access to the complete message (&#8220;Exchange&#8221; in Camel). While this seems pretty easy and practical, you will be tempted to pollute your routes with anonymous Java classes containing logic. You should also notice that, following this approach, you need to do almost the same mapping every time to access the body of the &#8220;in&#8221; exchange and set the result back into it. Another drawback of anonymous classes is that you cannot easily test the logic on them and, if you want to test them, you must create top-level classes that are both more complicated than simple Java beans and coupled to the Camel API through the &#8220;Exchange&#8221; class.</p>
<p>A better option is to use plain Java classes (&#8220;beans&#8221;) with the least possible logic, and let Camel do the default mapping using the signature of the methods invoked. If mapping the body of the exchange is not enough, you can use <a href='http://camel.apache.org/parameter-binding-annotations.html'>annotations</a> to get access to some other parts of the exchange or even the whole exchange in your bean methods (this time relying on the Camel API because of the specific annotations).</p>
<p>To see the problem that we want to solve with this option, try now the application with the following URL: &#8220;http://www.w3.org/notfound&#8221;. It does not exist and you will notice that Camel retries the operation with an empty URL obtained from the redirect location property in the &#8220;HttpOperationFailedException&#8221; exception. The application fails with a &#8220;UnknowHostException&#8221; produced by the now empty URL.<br />
Using the &#8220;bean&#8221; option, let&#8217;s see how to distinguish between any HTTP response code and a HTTP redirection code and avoid retrying the fetch unnecessarily. We can read this information by inspecting the &#8220;HttpOperationFailedException&#8221; properties. For this, create a class named &#8220;HttpErrorHelperBean&#8221; with the following content:<br />
<pre><code>
public class HttpErrorHelperBean {
&nbsp;&nbsp;&nbsp;&nbsp;public boolean isRedirectionError(Exception exception) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (exception instanceof HttpOperationFailedException) &amp;&amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(((HttpOperationFailedException) exception).getRedirectLocation() != null);
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
And modify the route introducing an &#8220;onWhen&#8221; clause after the &#8220;onException&#8221; in the following way:<br />
<pre><code>
...
&nbsp;&nbsp;&nbsp;&nbsp;.onException(HttpOperationFailedException.class)
&nbsp;&nbsp;&nbsp;&nbsp;.onWhen(bean(HttpErrorHelperBean.class).isEqualTo(true))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Fetching URL failed: &#039;${exception.message}&#039;, trying with relocation: &#039;${exception.redirectLocation}&#039;.&quot;)
...
</code></pre><br />
If you try once again the non-existing URL, you will see that now the thrown exception is the initial &#8220;HttpOperationFailedException&#8221; with a response code 404. This exception does not match the condition in our bean and therefore the error handling has not been applied.</p>
<p>Did you also notice something strange in the &#8220;bean&#8221; method construction? We indicated no method name or bean instance, but Camel seems to have instantiated the class and called the method we expected. What Camel does is try to find an instance of the passed class in the <a href='http://camel.apache.org/registry.html'>&#8220;registry&#8221;</a> or instantiate the class if it could not find an instance (for this, the class needs to have a default no arguments constructor). For guessing which method to call, Camel applies a matching algorithm based on the method signature and the message payload type. If you want to know how it exactly works, you can find a detailed description in the chapter 4.4 of <a href='http://www.manning.com/ibsen/'>Camel in Action</a>.</p>
<h3>Exposing our extractor service as a Web service</h3>
<p>Now let&#8217;s expose our logic as a web service. But first let me clarify: the kind of Web service we expose here is a plain and simple HTTP&#038;HTML service (not a REST or a SOAP based Web Service).</p>
<p>To implement a web service, create a class named &#8220;HtmlFormatterBean&#8221; with the following code:<br />
<pre><code>
public class HtmlFormatterBean {
&nbsp;&nbsp;&nbsp;&nbsp;public String asHtmlPage(List&lt;String&gt; contents) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringBuffer stringBuffer = new StringBuffer();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(&quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;Extracted contents:&lt;/h1&gt;&lt;ul&gt;&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (String content : contents) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(&quot;&lt;li&gt;&quot;).append(content).append(&quot;&lt;/li&gt;&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stringBuffer.append(&quot;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return stringBuffer.toString();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre></p>
<p>Add a new dependency in the Maven pom file:<br />
<pre><code>
...
&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-jetty&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependency&gt;
...
</code></pre></p>
<p>And modify the route builder adding this second route within the &#8220;configure&#8221; method:<br />
<pre><code>
from(&quot;jetty://http://0.0.0.0:8080/page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setBody(header(&quot;extractUrl&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(&quot;direct:page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.bean(HtmlFormatterBean.class);
</code></pre><br />
Now, if you run the application and access the URL: <a href='http://localhost:8080/page_extractor?extractUrl=http://www.w3c.org'>&#8220;http://localhost:8080/page_extractor?extractUrl=http://www.w3c.org&#8221;</a> from your web browser, then you should see a minimal HTML page containing a list with the extracted contents (you may need to increase the 10 seconds wait time before the context shuts down).</p>
<p>Wow! It was a short route but it had a big impact! By using the &#8220;jetty&#8221; endpoint in this way, Camel creates an embedded instance of the Jetty web server and starts listening to all the local IP interfaces, publishing the endpoint under the &#8220;page_extractor&#8221; web application context (&#8220;http://0.0.0.0:8080/page_extractor&#8221;). When you access this URL from a web browser adding the parameter &#8220;extractUrl=http://www.w3c.org&#8221;, the route creates a new message populating its headers with the different URL parameters and their values, and routes the message to the next step which happens to be the page extractor.</p>
<p>To send the request to our page extractor, we have to first set the value of the message body to the URL that we want to extract (which has been saved as a message header under the name of the HTTP parameter: &#8220;extractUrl&#8221;). To accomplish this, use the &#8220;setBody&#8221; method and, after this, the modified message is routed to the page extractor. When it comes back from the extractor, its body contains a list with the text chunks. The last step is to translate this payload into HTML, which is returned as a response to the HTTP request received in the &#8220;jetty&#8221; endpoint. If you do not like return HTML then instead of using a Java class you could also use the <a href='http://camel.apache.org/velocity.html'>&#8220;velocity&#8221;</a> Camel component.</p>
<p>This is the end of the second part of this series. In the next one, we introduce new functionality in a modular way and show how to test with the Camel testing framework.</p>
<p>I hope that you enjoyed the reading and that you come back for a new Camel ride in the next part.</p>
<p><strong>Update:</strong> added <a href='http://www.canoo.com/blog/wp-content/uploads/2011/03/Part2.zip'>source code</a> for download. To run the application, just expand the file, change to the folder where the pom file is and execute: &#8216;mvn compile exec:java -Dexec.mainClass=&#8221;com.canoo.camel.Part2&#8243;&#8216;</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>LEGO&#174; Java: Apache Camel Context and Route Basics</title>
		<link>http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics/</link>
		<comments>http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics/#comments</comments>
		<pubDate>Mon, 14 Mar 2011 17:25:33 +0000</pubDate>
		<dc:creator>alberto</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[alberto]]></category>
		<category><![CDATA[camel]]></category>
		<category><![CDATA[eip]]></category>

		<guid isPermaLink="false">http://www.canoo.com/blog/?p=1922</guid>
		<description><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics/";</script>code { font-size: 12px; overflow: auto; } If you are a fan of &#8220;Enterprise Integration Patterns&#8221; or you work in data integration projects, you have probably heard about Apache Camel. If not, this is a good moment to discover it. And what has Apache Camel to do with LEGOs? LEGOs offer a fixed set of [...]]]></description>
			<content:encoded><![CDATA[<script type="text/javascript">dzone_url = "http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics/";</script><style type="text/css">
    code {
        font-size: 12px;
        overflow: auto;
    }
</style>
<p>If you are a fan of <a href='http://www.eaipatterns.com/'>&#8220;Enterprise Integration Patterns&#8221;</a> or you work in data integration projects, you have probably heard about <a href='http://camel.apache.org/'>Apache Camel</a>. If not, this is a good moment to discover it.<br />
And what has Apache Camel to do with LEGOs? LEGOs offer a fixed set of blocks that you can combine to build some working gadgets. In that sense, Camel offers you Java implementations of the EIPs that you can mix and match to get some functionality done, without having to construct the blocks yourself. If you are open and eager to learn new ways of building Java applications, let&#8217;s start with some practical Camel examples.</p>
<h3>The Minimal Camel Application</h3>
<p>There are several different ways of deploying Camel: as plain Java application, as Spring-enabled application, as Java Web application, as OSGi module, etc. Let&#8217;s begin with a plain Java application and make our life easier by using Maven to grab the dependencies (the default option for Camel).</p>
<p>You will need a new Maven-based project and a &#8220;main&#8221; Java class:<br />
<pre><code>
public class Part1 {
&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DefaultCamelContext camelContext = new DefaultCamelContext();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.start();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(10000);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;camelContext.stop();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
The main method in the &#8220;main&#8221; class creates a default implementation of a Camel context. It then starts the context, waits 10 seconds and stops it before ending the application. This is an abrupt way to stop the context. For little prototypes, stopping the context improperly can have no side effects, don&#8217;t do it this way when deploying a productive application. If you want to learn more about this topic, I would recommend you to read the chapter 13.3 of <a href='http://www.manning.com/ibsen/'>&#8220;Camel in Action&#8221;</a>. There, the author explains what is a &#8220;Graceful shutdown&#8221;, how to do it properly and why.</p>
<p>The Maven pom file with the minimal dependencies required looks like this:<br />
<pre><code>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;com.canoo.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;part1&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;1.0&lt;/version&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependencies&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-core&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependencies&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&lt;properties&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;camel.version&gt;2.6.0&lt;/camel.version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/properties&gt;
&lt;/project&gt;
</code></pre><br />
If you run the application, you should observe some messages in the console output indicating that the context has been started and stopped.<br />
So far, so good, but, until now, our Camel application does nothing because we have not created any routes. We could now add a the &#8220;Hello World&#8221; route, but I am sure that reading the documentation would give you already an idea of how to do it, so let&#8217;s think about a more useful example to implement using Camel. We will construct an information retrieval application capable of processing information found in web pages.</p>
<h3>First feature: the &#8220;web page extractor&#8221;</h3>
<p>We are going to create a first route capable of downloading the page at a URL and transforming the contents into plain text. Basically, what we want do is grab the HTML, fetch all the paragraph elements in the page body, extract the text they contain and clean them by filtering all pure non-alphanumeric strings.<br />
For that, we need a new class extending &#8220;RouteBuilder&#8221; that can be applied to the Camel context and that creates the route (or routes). The &#8220;PageExtractorRoutes&#8221; class would look like this:<br />
<pre><code>
public class PageExtractorRoutes extends RouteBuilder {
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public void configure() throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;from(&quot;direct:page_extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.setHeader(Exchange.HTTP_URI, body())
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Extracting content from: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.to(&quot;http:extractor&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.unmarshal().tidyMarkup()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Html from: &#039;${body}&#039;&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.split(xpath(&quot;//body//p/text()&quot;), new SplitterAggregationStrategy(&quot;(?s).*[A-Za-z0-9].*&quot;))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.log(&quot;Text chunk: &#039;${body}&#039;.&quot;)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end();
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
This PageExtractorRoutes class extends RouteBuilder and overrides the &#8220;configure&#8221; method. This class will be applied later to the Camel context, creating the route in the context. As you can see in the route steps, this route will &#8220;listen&#8221; in an endpoint called &#8220;page_extractor&#8221; and pass the message received through to the different steps composing the route. You may also noticed that the endpoint is from type &#8220;direct&#8221;. This type is the simplest one in Camel and that it is equivalent to a Java synchronous method invocation.</p>
<p>Suppose that in the body of the message we receive the URL of a web page. We want to send this URL to an endpoint of type &#8220;http&#8221; that will access the page and grab its content passing the body of the message to the next step. The &#8220;http&#8221; endpoint can be set with the value of URL to access or, in case that the header &#8220;Exchange.HTTP_URI&#8221; is present in the message, it will use this value as the URL to grab. To put the URL in the expected header, we have used &#8220;setHeader&#8221; copying the body value. When using a dynamic URL set in the header, the name of the &#8220;http&#8221; endpoint is irrelevant (in this case we used &#8220;extractor&#8221;).</p>
<p>Once the &#8220;http&#8221; endpoint has fetched the content, we have to take care of some details before we can use XPath to select the text of the body paragraphs:</p>
<ul>
<li>Some endpoint types in Camel use streaming to avoid loading big pieces of data in memory. This means that the payload of the message is of type &#8220;InputStream&#8221; and, by default, can only be consumed once. This is the case with the &#8220;http&#8221; endpoint. If, as in our case, we know that the fetched web content is not going to be big, then we can convert it to a string that is consumable as many times as necessary (you can use &#8220;convertBodyTo(&#8216;String.class&#8217;)&#8221; from the Java DSL) or we can activate the streaming cache using the method &#8220;streamCaching()&#8221; from the Java DSL at the beginning of the route.</li>
<li>Because XPath needs well formed XML documents, we need to assure that the HTML content fulfills this condition. By using the &#8220;TidyMarkup&#8221; marshaller we achieve that and automatically our input stream gets converted to a DOM node that is a perfect fit for the XPath expression in the next step. Tidied markup can also be consumed multiple times.</li>
</ul>
<p>If everything works as expected, we can now split the DOM document into text chunks. Therefore, we use a splitter (&#8220;split&#8221; Java DSL method) that takes as first parameter an expression and as second an &#8220;AggregationStrategy&#8221; (this is only one of the many forms of the split method). The expression is responsible for returning the pieces that the split method controls, and the aggregation strategy has in this case two roles: to merge the split pieces in the required way and to filter the unwanted pieces (non-alphanumeric text chunks in our case). The &#8220;.end()&#8221; method call belongs to the splitter and is used to indicate where the pieces should be joined, returning finally a unique message whose body is the aggregation of the previously split pieces. </p>
<p>Ideally, we would separate these two concerns by using the Java DSL method &#8220;filter&#8221;. But it seems the splitter is unable to detect when to finish the split process if you dropped some pieces with the filter, which we do. As a workaround for this, do all the steps together in the aggregation strategy. Note that the XPath expression is applied implicitly to the message body.</p>
<p>The &#8220;SplitterAggregationStrategy&#8221; class looks like this:<br />
<pre><code>
public class SplitterAggregationStrategy implements AggregationStrategy {
&nbsp;&nbsp;&nbsp;&nbsp;private final String fFilter;

&nbsp;&nbsp;&nbsp;&nbsp;public SplitterAggregationStrategy(String filter) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFilter = filter;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;String&gt; result = null == oldExchange ? result = new ArrayList&lt;String&gt;() : oldExchange.getIn().getBody(List.class);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Node node = newExchange.getIn().getBody(Node.class);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String content = node.getNodeValue().trim();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (content.matches(fFilter)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.add(content.trim());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;newExchange.getIn().setBody(result);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return newExchange;
&nbsp;&nbsp;&nbsp;&nbsp;}
}
</code></pre><br />
To use this route we need to modify our main method of the &#8220;Part1&#8243; class in the following way:<br />
<pre><code>
public static void main(String[] args) throws Exception {
&nbsp;&nbsp;&nbsp;&nbsp;final DefaultCamelContext camelContext = new DefaultCamelContext();
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.addRoutes(new PageExtractorRoutes());
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.start();

&nbsp;&nbsp;&nbsp;&nbsp;ProducerTemplate template = camelContext.createProducerTemplate();
&nbsp;&nbsp;&nbsp;&nbsp;String result = template.requestBody(&quot;direct:page_extractor&quot;, &quot;http://www.w3.org&quot;, String.class);
&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf(&quot;Extracted: &#039;%s&#039;.\n&quot;, result);

&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(10000);
&nbsp;&nbsp;&nbsp;&nbsp;camelContext.stop();
}
</code></pre><br />
Before starting the context, we now apply our route builder to it to get the route added. To send a URL to our extractor and get the result, we need to create a &#8220;ProducerTemplate&#8221; and invoke one of the many available methods on it. In our case, we send only a value using the body, do a synchronous call and get a string result that is be printed to the console. If you inspect the methods in the &#8220;ProducerTemplate&#8221; class, you notice many different variations available. These correspond to the variations of the before mentioned call characteristics.<br />
The last detail to notice in the route is that we added some logging code using the Java DSL &#8220;log&#8221; method.</p>
<p>Last but not least, we need to complete the dependencies in our Maven pom file like this:<br />
<pre><code>
...
&lt;dependencies&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-core&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-http&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;dependency&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;artifactId&gt;camel-tagsoup&lt;/artifactId&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;version&gt;${camel.version}&lt;/version&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/dependency&gt;
&lt;/dependencies&gt;
...
</code></pre></p>
<p>To make this post short enough, we will end here and continue in a second part of a series. In the next part, we will introduce the following topics and features:</p>
<ul>
<li>Using error handling to control HTTP redirections and Java Beans for fine-grained logic.</li>
<li>Exposing our extractor functionality as a Web service.</li>
</ul>
<p>If you enjoyed the reading,  I hope to see you in the <a href='http://www.canoo.com/blog/2011/03/16/lego-java-ii-apache-camel-error-handling-java-beans-and-web-services'>second part</a>.</p>
<p>Note: if you are interested in learning more Camel Enterprise Integration Patterns, maybe you should have a look at <a href='http://refcardz.dzone.com/refcardz/apache-camel-update'>DZone Refcard: The Top Twelve Integration Patterns for Apache Camel</a>.</p>
<p><strong>Update:</strong> added <a href='http://www.canoo.com/blog/wp-content/uploads/2011/03/Part1.zip'>source code</a> for download. To run the application, just expand the file, change to the folder where the pom file is and execute: &#8216;mvn compile exec:java -Dexec.mainClass=&#8221;com.canoo.camel.Part1&#8243;&#8216;</p>
<script>var dzone_style="2";</script><script language="javascript" src="http://widgets.dzone.com/widgets/zoneit.js"></script>]]></content:encoded>
			<wfw:commentRss>http://www.canoo.com/blog/2011/03/14/lego-java-apache-camel-context-and-route-basics/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

