<?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>JTeam Blog &#187; Hippo</title>
	<atom:link href="http://blog.jteam.nl/tag/hippo/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.jteam.nl</link>
	<description>Keep updated on what we&#039;re doing!</description>
	<lastBuildDate>Fri, 23 Jul 2010 07:32:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>JTeam en rijksoverheid.nl</title>
		<link>http://blog.jteam.nl/2010/04/05/jteam-en-rijksoverheid-nl/</link>
		<comments>http://blog.jteam.nl/2010/04/05/jteam-en-rijksoverheid-nl/#comments</comments>
		<pubDate>Mon, 05 Apr 2010 14:47:56 +0000</pubDate>
		<dc:creator>Jettro Coenradie</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[CMS]]></category>
		<category><![CDATA[Hippo]]></category>
		<category><![CDATA[rijksoverheid]]></category>

		<guid isPermaLink="false">http://blog.jteam.nl/?p=2044</guid>
		<description><![CDATA[www.rijksoverheid.nl is live. Hoelang geleden het idee achter één rijksbrede website is ontstaan weet ik niet. Hoelang ik nu bij het project betrokken ben weet ik wel. Het is nu ongeveer anderhalf jaar geleden dat ik ben begonnen als Software Architect bij het project Overheid Nieuws Stijl. Het is een ambitieus project om 16 websites [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.rijksoverheid.nl">www.rijksoverheid.nl</a> is live. Hoelang geleden het idee achter één rijksbrede website is ontstaan weet ik niet. Hoelang ik nu bij het project betrokken ben weet ik wel.</p>
<div style="text-align:center;"><img src="http://blog.jteam.nl/wp-content/uploads/2010/04/Screen-shot-2010-04-04-at-08.52.08.png" alt="Screen shot 2010-04-04 at 08.52.08.png" border="0" width="450" height="281" /></div>
<p>Het is nu ongeveer anderhalf jaar geleden dat ik ben begonnen als Software Architect bij het project <strong>Overheid Nieuws Stijl</strong>. Het is een ambitieus project om 16 websites samen te laten komen in één rijksbrede website <a href="http://www.rijksoverheid.nl">www.rijksoverheid.nl</a>. Met de eerste live gang zijn er 7 websites over: postbus 51, regering.nl, minaz.nl, minez.nl, minocw.nl, minvws.nl en jeugengezin.nl. Uiteraard moest de meeste content van de websites ook op de nieuwe website terug te vinden zijn.</p>
<p>Vanuit de overheid is er voor gekozen om zoveel mogelijk Open Source software te gaan gebruiken. Ook voor het project Overheid Nieuwe Stijl (ONS) is er gekozen voor meerdere Open Source oplossingen. Naast het gebruik van Open Source software worden zaken als Open Standaarden en ook Open Data belangrijk gevonden. Vooral <strong>Open Data</strong> wordt door iedereen als zeer positief ervaren. De <a href="http://www.rijksoverheid.nl/copyright">licentie voor alle content</a> is Creative Commons.</p>
<p>Mocht je meer informatie willen hebben over dit enorme project, <a href="http://www.communicatieplein.nl/Onderwerpen/Corporate_communicatie/Project_ONS">dan kun je deze hier vinden</a>.</p>
<p>In deze blogpost wil ik me vooral concentreren op de betrokkenheid van JTeam bij dit ambitieuze project voor de rijksoverheid.</p>
<p><span id="more-2044"></span><br />
<h2>Proof-Of-Concept (Poc)</h2>
<p>Voor mij startte het project ergens in September 2009. Samen met twee mensen vanuit de overheid een iemand van Hippo zouden wij het Hippo cms op de pijnbank leggen tijdens een Proof-Of-Concept (PoC). Voor deze PoC was er al veel onderzoek gedaan naar content management systemen, daar is Hippo uitgekomen als de beste keuze voor de eisen van de nieuwe rijksoverheid website. Al tijdens de PoC bleek dat Hippo graag wilde helpen om een succes te maken van dit project. Ze hebben vanuit Hippo tijdens de PoC en uiteraard ook tijdens de rest van het project veel features opgeleverd die wij als rijksoverheid nodig hadden. Tijdens de PoC heb ik geholpen de oplossing te realiseren. Waar nodig ben ik kritisch richting Hippo geweest. Heb soms zelfs om verbeteringen gevraagd. Dit heb ik en uiteraard ook mijn collega&#8217;s tijdens het project vaker gedaan. Zo hebben we bugs gerapporteerd, soms ook wat patches gemaakt om de uiteindelijke oplossingen beter te maken.</p>
<p>Een klein onderdeel van de PoC was het importeren van content en beschikbaar stellen van data. Door gebruik te maken van spring-ws en spring integration en een eigen gemaakte hippo connector bleek dit heel goed mogelijk.</p>
<p>Met hard werken is de PoC uiteindelijk geslaagd en konden de plannen voor het echte project worden gemaakt.</p>
<h2>De aanbesteding</h2>
<p>In afwachting van de aanbesteding is het project team verder gegaan met het maken van plannen en voorbereiden van het project. Nadat JTeam bij een van de 6 partijen hoorde is het team uitgebreid en inmiddels zitten Roberto, Rob en Tom ook in het team. Daarmee hebben we als JTeam een behoorlijke bijdrage geleverd aan de uiteindelijke oplossing.</p>
<h2>Open Source</h2>
<p>Zoals eerder al gezegd is er de voorkeur om zoveel mogelijk met Open Source te werken. Het is bekend dat we gebruik maken van <a href="http://www.onehippo.com/en/products/cms">Hippo</a> als content management systeem. Hippo werkt mee in het project, de samenwerking met Hippo heb ik als zeer prettig ervaren. We houden elkaar scherp en vullen elkaar duidelijk aan qua kennis.</p>
<p>Naast hippo zijn er ook nog andere Open Source technologiën waar we gebruik van maken. De belangrijkste die ik nog wel wil noemen zijn twee spring framework projecten. We hebben <a href="http://static.springsource.org/spring-ws/sites/1.5/">spring webservices</a> gebruikt voor het accepteren van nieuwe content en content updates. Het afhandelen van nieuwe content is een complex stuk dat we uiteindelijk oplossen met <a href="http://www.springsource.org/spring-integration">spring integration</a>. Deze technologiën zijn onmisbaar in het hele content migratie stuk. Daarnaast maken we ook gebruik van de RSS mogelijkheden die het spring framework out-of-the-box ondersteund.</p>
<h2>Data migratie</h2>
<p>De data van de websites wordt middels een commerciële tool geïmporteerd. Kapow van <a href="http://kapowtech.com/">kapowtech</a> is een gespecialiseerde tool voor het lezen van bestaande websites. Zij spreken vervolgens een door ons gerealiseerde webservice aan om de content in Hippo te krijgen. Dit heeft zich tot nu toe bewezen als zeer efficiënt.</p>
<h2>Web richtlijnen</h2>
<p>Vanuit het ministerie van Volksgezondheid, Welzijn en Sport zijn een aantal mensen binnen gekomen die veel doen met de web richtlijnen. Twee van hen zijn nauw betrokken bij bij het tot stand komen en dus ook naleven van de <a href="http://www.webrichtlijnen.nl/">web richtlijnen</a>. Ik moet eerlijk toegeven dat ik me hier nooit zoveel mee bezig heb gehouden. Natuurlijk kende ik de basis wel, maar ik heb al best wel aardig wat geleerd van deze mannen. Ik heb begrepen dat we nu al nagenoeg voldoen aan de nieuwe versie van de web richtlijnen die momenteel wordt vastgelegd.</p>
<h2>Hosting en infrastructuur</h2>
<p>Zonder een goede infrastructuur is het onmogelijk om een goede site op te leveren. We maken al lang gebruik van een specialist op het gebied van infrastructuur. Hij komt van het bedrijf <a href="http://www.prolocation.nl/">prolocation</a>. Samen met een collega heeft hij mij echt weten te verbazen. De kennis die deze twee gasten hebben is enorm. Heb veel van hen opgestoken.</p>
<p>De hosting ga ik het niet te veel over hebben. Hier is het nodige om te doen. Weet wel dat we er erg veel energie in hebben moeten stoppen om de infrastructuur zo te krijgen dat het voor ons project werkt zoals het zou moeten werken.</p>
<h2>Open Data</h2>
<p>Ik heb al eerder gezegd dat Open Data erg belangrijk is voor het project. In de huidige versie hebben we nog niet zo heel veel gedaan aan het beschikbaar stellen van data. Het gebruik van Creative Commons is wellicht het belangrijkste. Wel is er gestart met het beschikbaar stellen van rss feeds. Uiteraard zullen hier in de toekomst meer features worden opgeleverd.</p>
<p>Persoonlijk moet ik er nog wel eens aan wennen. Ik was dan ook nogal skeptisch over het initiatief van een andere partij om toch meer feeds aan te bieden. Vanuit het project was men het hier echter helemaal niet mee eens en vond dit juist een mooi initiatief.</p>
<p><a href="http://rijksoverheid.jijendeoverheid.nl/">http://rijksoverheid.jijendeoverheid.nl/</a></p>
<h2>De mensen</h2>
<p>Als laatste wil ik toch nog even in gaan op de mensen die in het project zitten. Ik vind echt dat we een enorm goed team hebben weten te creëren met mensen die overal vandaan kwamen. Ambtenaren en externen gaan samen volledig voor een resultaat dat er mag zijn. </p>
<h2>Het resultaat</h2>
<p>Super snelle website en een gaaf project.</p>
<p>Wat we binnen dit project voor elkaar krijgen kan alleen met een team, en dat is precies wat we met zijn allen zijn geworden. Ik ben enorm trots op dat ik mee heb mogen werken met dit project en ik denk dat ik nog wel even mee blij werken om ook de volgende fases mee te kunnen maken.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jteam.nl/2010/04/05/jteam-en-rijksoverheid-nl/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Migrating content with Spring Integration &#8211; A real life example</title>
		<link>http://blog.jteam.nl/2010/01/13/migrating-content-with-spring-integration-a-real-life-example/</link>
		<comments>http://blog.jteam.nl/2010/01/13/migrating-content-with-spring-integration-a-real-life-example/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 07:38:30 +0000</pubDate>
		<dc:creator>Roberto van der Linden</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[CMS]]></category>
		<category><![CDATA[Hippo]]></category>
		<category><![CDATA[Kapow]]></category>
		<category><![CDATA[Migration]]></category>
		<category><![CDATA[Spring Framework]]></category>
		<category><![CDATA[Spring Integration]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://blog.jteam.nl/?p=1621</guid>
		<description><![CDATA[In one of the projects we need to migrate content from multiple websites into Hippo CMS. One of the interesting parts of this migration is that one of the websites will constantly provide us with updates of the content. Therefore it makes the migration a continuous process. In this post I will explain how we [...]]]></description>
			<content:encoded><![CDATA[<p>In one of the projects we need to migrate content from multiple websites into <a href="http://www.onehippo.org/cms7/" target="_blank">Hippo CMS</a>. One of the interesting parts of this migration is that one of the websites will constantly provide us with updates of the content. Therefore it makes the migration a continuous process.</p>
<p>In this post I will explain how we use Spring Integration to migrate content, handle errors, measure performance and deal with the fact that content could contain references to other content that is not imported yet.</p>
<p><span id="more-1621"></span></p>
<h2>Short Spring Integration introduction</h2>
<p>As I cannot explain Spring Integration in a few sentences, I will just focus on the elements that I use in the example. To get a complete overview of the elements I recommend you to read the reference manual.</p>
<ul>
<li><strong>Message</strong> – Generic container for data. It contains the payload which can be any object.</li>
<li><strong>Message Channel</strong> – Used for transporting the data between the different elements.</li>
<li><strong>ChannelAdapter</strong> &#8211; Message Endpoint that enables connecting a single sender or receiver to a channel.</li>
<li><strong>Transformer</strong> &#8211; Enables the loose-coupling of Message Producers and Message Consumers by transforming a message to another type.</li>
<li><strong>Service activator</strong> &#8211; The endpoint type for connecting any Spring-managed Object to an input channel so that it may play the role of a service.</li>
<li><strong>Router</strong> &#8211; Provides a simple way to connect a router to an input channel.</li>
<li><strong>Splitter</strong> &#8211; Partition a message in several parts, and send the resulting messages to be processed independently.</li>
</ul>
<h2>Getting the content</h2>
<p>Before you can start importing the content, you of course need to scrape the content from the different websites. In our case this is done by the guys from <a href="http://kapowtech.com/" target="_blank">Kapow</a>. They provide us with a lot content that is preformatted in XML files so that our import tool can handle the incoming requests. The content is distinguished by the type of document. So for example we could have news, address, a brochure and many more. The different types of documents contain data like images, videos, PDF files and of course text. Each XML file contains an element that represent a unique id for that file. This is used to identify any existing documents in the CMS.</p>
<h2>Connecting to the repository</h2>
<p>Hippo CMS is created on top of the <a href="http://jackrabbit.apache.org/" target="_blank">Apache Jackrabbit</a> repository. We connect directly to the repository using the Hippo Repository Connector that is provided in the Hippo Site Toolkit. This enables us to interact with the repository and provides us the possibility to perform actions like storing, editing and finding nodes.</p>
<p>My colleague Jettro Coenradie has written a <a href="http://www.gridshore.nl/2009/01/16/connecting-to-hippo-ecm-using-a-springframework-connector/" target="_blank">blog post</a> that goes more in-depth about connecting to the Hippo Repository.</p>
<h2>Process overview</h2>
<p><a href="http://blog.jteam.nl/wp-content/uploads/2010/01/Springintegration1.jpg"><img style="display: inline; border-width: 0px;" title="Spring integration" src="http://blog.jteam.nl/wp-content/uploads/2010/01/Springintegration_thumb1.jpg" border="0" alt="Spring integration" width="503" height="619" /></a></p>
<h3>Starting point</h3>
<p>As you can see in the image we provide two ways for Kapow to deliver the content. One is via web services and the other one is directly via the file system (as Kapow runs its tool on the same server as the importer). This has few reasons:</p>
<ol>
<li>Web services provides more flexibility for other applications to connect with the importer tool.</li>
<li>Using the web service will prevent the problem that Spring Integration tries to read the file before it is completely written to the file system.</li>
<li>When the web service is not available, Kapow is still able to write the XML files directly to the file system.</li>
</ol>
<h3>Webservice</h3>
<p>Spring Integration provides us with a inbound web service gateway that send a message to a channel that is received from a Web Service invocation.</p>
<p>As you can see in the process overview image, messages that are received from the web service are enriched with the filename and then written to the file system. We have to do this in order to create an XML file (using the transformer) which obviously needs a filename. With the file outbound gateway the XML file is placed in the directory that is scanned for new files.</p>
<p>To make this all work we have to configure in the Spring WS servlet the endpoint mapping that know which request needs to map to our gateway that is configured in our integration context.</p>
<p>When the process is finished with writing to the file system and therefore correctly received by the importer we use a service activator that returns a ResponseMessage with the text “OK”. This response message is received by Kapow.</p>
<p>The endpoint configuration in the web service servlet:</p>
<pre class="brush: xml;">
    &lt;bean id=&quot;endpointMapping&quot; class=&quot;org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping&quot;&gt;
        &lt;property name=&quot;mappings&quot;&gt;
            &lt;props&gt;
                &lt;prop key=&quot;{http://www.jteam.nl/pons/webservice}AddressRequest&quot;&gt;webserviceGateway&lt;/prop&gt;
                &lt;prop key=&quot;{http://www.jteam.nl/pons/webservice}NewsRequest&quot;&gt;webserviceGateway&lt;/prop&gt;
            &lt;/props&gt;
        &lt;/property&gt;
    &lt;/bean&gt;
</pre>
<p>The integration configuration:</p>
<pre class="brush: xml;">
&lt;si-ws:inbound-gateway id=&quot;webserviceGateway&quot; marshaller=&quot;unmarshaller&quot; request-channel=&quot;transformedIncomingRequestBuffer&quot; /&gt;

&lt;si:channel id=&quot;transformedIncomingRequestBuffer&quot; /&gt;

&lt;si:transformer ref=&quot;headerEnricher&quot; output-channel=&quot;enrichedUnmarchalledInputFile&quot; input-channel=&quot;transformedIncomingRequestBuffer&quot; /&gt;
&lt;si:channel id=&quot;enrichedUnmarchalledInputFile&quot; /&gt;

&lt;si-xml:marshalling-transformer marshaller=&quot;unmarshaller&quot; output-channel=&quot;unmarchalledInputFile&quot; input-channel=&quot;enrichedUnmarchalledInputFile&quot; result-transformer=&quot;toStringTransformer&quot; /&gt;
&lt;si:channel id=&quot;unmarchalledInputFile&quot; /&gt;

&lt;file:outbound-gateway request-channel=&quot;unmarchalledInputFile&quot; reply-channel=&quot;handledFile&quot; delete-source-files=&quot;false&quot; directory=&quot;$si{file.import.path}&quot; /&gt;

&lt;si:channel id=&quot;handledFile&quot; /&gt;

&lt;si:service-activator ref=&quot;tempHandler&quot; input-channel=&quot;handledFile&quot; /&gt;

&lt;bean id=&quot;headerEnricher&quot; class=&quot;nl.jteam.importer.HeaderEnricher&quot; /&gt;
&lt;bean id=&quot;tempHandler&quot; class=&quot;nl.jteam.importer.ResponseMessageHandler&quot; /&gt;
</pre>
<h3>Transform file</h3>
<p>All the XML files are written to the file system in a given directory. With the inbound-channel-adapter element the files are read from the given directory. You can provide a filter that only accepts the types based on the known suffix. In our case the only files we want to read are XML files. The poller is used to configure how the maximum messages we want per poll and on what interval it needs to scan the directory for new files.</p>
<p>The file-to-string-transformer element transforms, as the name already suggests, a file to a string. This string is then used by the unmarshalling-transformer as input for the unmarshalling process to create JAXB objects.</p>
<pre class="brush: xml;">
&lt;file:inbound-channel-adapter id=&quot;fileAdapter&quot; directory=&quot;$si{file.import.path}&quot; filter=&quot;patternMatchingFileListFilter&quot; channel=&quot;fileInputChannel&quot;&gt;
    &lt;si:poller max-messages-per-poll=&quot;$si{file.poller.maxperpoll}&quot;&gt;
        &lt;si:interval-trigger time-unit=&quot;MILLISECONDS&quot; interval=&quot;$si{file.poller.interval}&quot; /&gt;:         &lt;si:interval-trigger time-unit=&quot;MILLISECONDS&quot; interval=&quot;$si{file.poller.interval}&quot; /&gt;
    &lt;/si:poller&gt;
&lt;/file:inbound-channel-adapter&gt;
&lt;si:channel id=&quot;fileInputChannel&quot; datatype=&quot;java.io.File&quot; /&gt;

&lt;file:file-to-string-transformer output-channel=&quot;xmlInputChannel&quot; input-channel=&quot;fileInputChannel&quot; charset=&quot;UTF-8&quot; delete-files=&quot;true&quot; /&gt;
&lt;si:channel id=&quot;xmlInputChannel&quot; /&gt;

&lt;si-xml:unmarshalling-transformer id=&quot;defaultUnmarshaller&quot; output-channel=&quot;docTypeChannel&quot; input-channel=&quot;xmlInputChannel&quot; unmarshaller=&quot;unmarshaller&quot; /&gt;

&lt;bean id=&quot;patternMatchingFileListFilter&quot; class=&quot;org.springframework.integration.file.PatternMatchingFileListFilter&quot;&gt;
    &lt;constructor-arg value=&quot;.*\.xml&quot; /&gt;
&lt;/bean&gt;

&lt;bean id=&quot;unmarshaller&quot; class=&quot;org.springframework.oxm.jaxb.Jaxb2Marshaller&quot;&gt;
    &lt;property name=&quot;marshallerProperties&quot;&gt;
        &lt;map value-type=&quot;java.lang.Boolean&quot; key-type=&quot;java.lang.String&quot;&gt;
            &lt;entry key=&quot;jaxb.formatted.output&quot; value=&quot;true&quot; /&gt;
        &lt;/map&gt;
    &lt;/property&gt;
    &lt;property name=&quot;contextPaths&quot;&gt;
        &lt;list&gt;
            &lt;value&gt;nl.jteam.importer.jaxb.news&lt;/value&gt;
            &lt;value&gt;nl.jteam.importer.jaxb.address&lt;/value&gt;
        &lt;/list&gt;
    &lt;/property&gt;
    &lt;property name=&quot;schemas&quot;&gt;
        &lt;list&gt;
            &lt;value&gt;classpath:/xsd/news.xsd&lt;/value&gt;
            &lt;value&gt;classpath:/xsd/address.xsd&lt;/value&gt;
        &lt;/list&gt;
    &lt;/property&gt;
&lt;/bean&gt;
</pre>
<h3>Document router</h3>
<p>To identify which handler class needs to handle the document that is being imported, we created a router. This router gets the name from the payload and returns this value. This value than can be used to determine the channel to put the message on. So when the name of the payload is <em>News </em>it will use the channel with id <em>News</em> to put the message on.</p>
<p>The configuration:</p>
<pre class="brush: xml;">
&lt;si:channel id=&quot;docTypeChannel&quot; /&gt;

&lt;si:router ref=&quot;docTypeRouter&quot; input-channel=&quot;docTypeChannel&quot; method=&quot;resolveObjectTypeChannel&quot; /&gt;
&lt;si:channel id=&quot;News&quot; /&gt;
&lt;si:channel id=&quot;Address&quot; /&gt;

&lt;si:service-activator ref=&quot;newsMessageHandler&quot; output-channel=&quot;newContentItemNotification&quot; input-channel=&quot;News&quot; method=&quot;handleMessage&quot; /&gt;
&lt;si:service-activator ref=&quot;addressMessageHandler&quot; output-channel=&quot;newContentItemNotification&quot; input-channel=&quot;Address&quot; method=&quot;handleMessage&quot; /&gt;

&lt;bean class=&quot;nl.jteam.importer.DocumentTypeMessageRouter&quot; name=&quot;docTypeRouter&quot; /&gt;
</pre>
<p>The implementation of the router class:</p>
<pre class="brush: java;">
public class DocumentTypeMessageRouter {

    public String resolveObjectTypeChannel(Message message) {
        return message.getPayload().getClass().getSimpleName();
    }
}
</pre>
<h3>Handlers</h3>
<p>Each document type has his own handler class. This class is responsible for handling all the properties that are relevant for that document.</p>
<p>A document in Hippo CMS is a JCR node in the repository. The handler will create this node if it not already exists. If it does exist it will take that node. Because the content from the XML is the correct content, all properties and child nodes will be removed. So any change that is made to the content via the CMS in that document will be gone. Because we keep the node, references in the repository by other nodes will be intact, but all properties will be recreated by the handler.</p>
<p>For each property the handler will add this to the node or calls another handler that handles that specific property such as an image handler or audio handler class.</p>
<p><strong>Missing links</strong></p>
<p>It could be that a document has references to another document that is not imported yet. This means that the link cannot be created yet. To solve this problem we store for each link that cannot be created a missing link node in the repository. This node contains the information that is necessary to create the link when that document is imported. I will explain later on how the missing links are used when updating content.</p>
<p>When the handler is finished with creating a document in Hippo CMS, it will execute a query that finds all the documents that contain a reference to the imported document or in other words, it will find all missing links that now can be resolved.</p>
<h3>Referred documents splitter</h3>
<p>The outcome of the query that looks for references to the imported document could return multiple documents of different types. For example, we have imported an address document and that document is referenced by a news item and a press release item. These two documents will each be represented in a ReferredDefinition object. These ReferredDefinition objects contain the information of the missing link node plus the unique repository id (uuid) of the currently imported document.</p>
<p>Spring Integration provides us a splitter that has the capability to partition the message in several messages. The implementation of our splitter receives a list of ReferredDefinition objects. Each message (ReferredDefinition object) will be send to a router that routes the ReferredDefinition object to his appropriate channel.</p>
<p>The configuration:</p>
<pre class="brush: xml;">
&lt;si:channel id=&quot;newContentItemNotification&quot; /&gt;

&lt;si:splitter ref=&quot;referredBySplitter&quot; output-channel=&quot;referedBy&quot; input-channel=&quot;newContentItemNotification&quot; method=&quot;splitReferredDefinitions&quot; /&gt;

&lt;bean class=&quot;nl.jteam.importer.ReferredByMessageSplitter&quot; name=&quot;referredBySplitter&quot; /&gt;
</pre>
<p>The referred by message splitter class:</p>
<pre class="brush: java;">
public class ReferredByMessageSplitter {

    public List&lt;referreddefinition&gt; splitReferredDefinitions(List&lt;referreddefinition&gt; referedDefinitions) {
        return referredDefinitions;
    }
}
</pre>
<h3>Referred by router</h3>
<p>The splitter provides the router with a single ReferredDefinition item. The ReferredDefinition object contains information about the document type that needs to be updated. The implementation of the router is quite simple. It gets the type of the document that needs to be updated and adds the string “Update” to the document type. This combination gives us the name of the channel that is used to put the message on.</p>
<p>The configuration:</p>
<pre class="brush: xml;">
&lt;si:channel id=&quot;referredBy&quot; /&gt;
&lt;si:router ref=&quot;referredByRouter&quot; input-channel=&quot;referredBy&quot; method=&quot;resolveItemChannel&quot; /&gt;

&lt;si:channel id=&quot;NewsUpdate&quot; /&gt;
&lt;si:channel id=&quot;PressReleaseUpdate&quot; /&gt;

&lt;bean class=&quot;nl.jteam.importer.ReferredByMessageRouter&quot; name=&quot;referredByRouter&quot; /&gt;
</pre>
<p>The referred by message router class:</p>
<pre class="brush: java;">
public class ReferredByMessageRouter {

    public String resolveItemChannel(ReferredDefinition referredDefinition) {
        return referredDefinition.getPrimaryTypeReferredBy() + &quot;Update&quot;;
    }
}
</pre>
<h3><span style="color: #ff0000;"> </span></h3>
<h3>Update handlers</h3>
<p>Each document type has just like the normal handlers his own update handler. The update handlers are used for updating content that has a reference to the imported document. The handler receives in ReferredDefinition object which contains the information of the document that contains the reference.</p>
<p><a href="http://blog.jteam.nl/wp-content/uploads/2010/01/update1.jpg"><img style="display: inline; border-width: 0px;" title="update" src="http://blog.jteam.nl/wp-content/uploads/2010/01/update_thumb1.jpg" border="0" alt="update" width="353" height="205" /></a></p>
<p>So if we have for example two documents (A and B). B is a news item that contains a field addres and A is an address document. Document B is already imported and has a reference to A. Document B links to document A, but because A was not imported yet, a missing link node for A exists. Now document A is going to be imported, so document B can be updated with a working link. The content handler of A finds the missing link and creates a ReferredDefinition object with the information of the missing link. The update handler finds document B based on his unique id that is also stored in the ReferredDefinition object.</p>
<p>Now that document B is found, the update handler can add the correct property (which is specified in the ReferredDefinition object) to document B with the link to document A. When the link is created, it is no longer necessary to keep the missing link node and will therefore be removed.</p>
<h3>Error handling</h3>
<p>When importing content errors could occur. We distinguish between two types of errors:</p>
<ol>
<li>Critical errors &#8211; Something is wrong with the XML structure or with the connection to the repository.</li>
<li>Non critical errors – Errors like incorrect content or links that cannot be created.</li>
</ol>
<p>When a critical error occurs it means that we cannot import that document at this moment. So we have defined an error channel that is used to put the payload on and transform it back to an XML file. That file is than written to a different  folder, so that it will not be picked up again by Spring Integration.</p>
<p>With non critical errors it is only necessary to notify a system admin that something went wrong. So in order to do this, we introduced an error collector. This error collector is nothing more than object that contains a list of errors and a little information about the document. This collector is passed through the whole process of importing a document and all errors that occur will be collected. At the end of that process the errors will be mailed to the system admin. Currently this is done after each document, but this will probably change in the future to use some sort of batch mailing.</p>
<pre class="brush: xml;">
&lt;si:channel id=&quot;errorChannel&quot; /&gt;

&lt;si:chain input-channel=&quot;errorChannel&quot;&gt;
    &lt;si:service-activator ref=&quot;errorMessageHandler&quot; method=&quot;handleMessage&quot; /&gt;
    &lt;si:router ref=&quot;errorMessageRouter&quot; method=&quot;routeByPayloadType&quot; /&gt;
&lt;/si:chain&gt;

&lt;si:channel id=&quot;errorOutputChannel&quot; /&gt;
&lt;si-xml:marshalling-transformer id=&quot;defaultMarshaller&quot; marshaller=&quot;unmarshaller&quot; output-channel=&quot;xmlOutputChannel&quot; input-channel=&quot;errorOutputChannel&quot; result-transformer=&quot;toStringTransformer&quot; /&gt;

&lt;bean id=&quot;toStringTransformer&quot; class=&quot;org.springframework.integration.xml.transformer.ResultToStringTransformer&quot; /&gt;

&lt;si:channel id=&quot;xmlOutputChannel&quot; /&gt;

&lt;file:outbound-channel-adapter directory=&quot;$si{file.unhandled.path}&quot; channel=&quot;xmlOutputChannel&quot; /&gt;

&lt;bean id=&quot;errorMessageHandler&quot; class=&quot;nl.jteam.importer.errorhandling.ErrorHandler&quot;&gt;
    &lt;property name=&quot;errorMailer&quot; ref=&quot;errorMailer&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;

&lt;bean id=&quot;errorMessageRouter&quot; class=&quot;nl.jteam.importer.errorhandling.ErrorMessageRouter&quot;&gt;
    &lt;property name=&quot;objectChannel&quot; ref=&quot;errorOutputChannel&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;xmlChannel&quot; ref=&quot;xmlOutputChannel&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;
</pre>
<h3>Performance</h3>
<p>With the use of an aspect we send a message to the <em>performanceMonitor</em> channel. When you declare a MessageChannel you can call the method send(..) to send your message to the channel. In our case we create a new StringMessage to send the performance message. The channel is used by the service-activator. The service-activator uses a poller to throttle inbound messages.</p>
<p>The service-activator passes the message to the Log4jMonitorPersister, where it logs the message.</p>
<p>The configuration:</p>
<pre class="brush: xml;">
&lt;si:channel id=&quot;performanceMonitor&quot;&gt;
    &lt;si:queue /&gt;
&lt;/si:channel&gt;

&lt;si:service-activator ref=&quot;monitorPersister&quot; input-channel=&quot;performanceMonitor&quot; method=&quot;persist&quot;&gt;
    &lt;si:poller max-messages-per-poll=&quot;100&quot;&gt;
        &lt;si:interval-trigger time-unit=&quot;MILLISECONDS&quot; interval=&quot;10000&quot; /&gt;
    &lt;/si:poller&gt;
&lt;/si:service-activator&gt;

&lt;bean id=&quot;monitorPersister&quot; class=&quot;nl.jteam.importer.monitoring.Log4jMonitorPersister&quot; /&gt;

&lt;aop:aspectj-autoproxy /&gt;

&lt;bean id=&quot;monitoringHandlersAdvice&quot; class=&quot;nl.jteam.importer.monitoring.MonitoringHandlersAdvice&quot;&gt;
    &lt;property name=&quot;performanceMonitor&quot; ref=&quot;performanceMonitor&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;
</pre>
<p>The MonitoringHandlersAdvice class:</p>
<pre class="brush: java;">
@Aspect
public class MonitoringHandlersAdvice {

    private MessageChannel channel;

    @Around(&quot;execution(* nl.jteam.importer.handler.*Handler.handleMessage(..)) &amp;&amp; args(message)&quot;)
    public Object monitorB(ProceedingJoinPoint proceedingJoinPoint, Message message)
            throws Throwable {
        String fileName = (String) message.getHeaders().get(&quot;springintegration_file_name&quot;);
        Object payload = message.getPayload();

        StopWatch clock = new StopWatch();
        DateTime start = new DateTime();

        try {
            clock.start(proceedingJoinPoint.toShortString());
            return proceedingJoinPoint.proceed(new Object[]{message});
        } finally {
            clock.stop();
            StringBuilder outMessageBuilder = new StringBuilder();
            outMessageBuilder.append(clock.getTotalTimeMillis()).append(&quot;;&quot;)
                    .append(payload.getClass().getSimpleName()).append(&quot;;&quot;)
                    .append(fileName).append(&quot;;&quot;)
                    .append(start.toString(&quot;yyyy-MM-dd HH:mm:ss&quot;));
            channel.send(new StringMessage(outMessageBuilder.toString()));
        }
    }

    @Required
    public void setPerformanceMonitor(MessageChannel performanceMonitor) {
        channel = performanceMonitor;
    }
}
</pre>
<p>The Log4jMonitorPersister class:</p>
<pre class="brush: java;">
public class Log4jMonitorPersister implements MonitorPersister {
    private final static Logger log = LoggerFactory.getLogger(MonitorPersister.class);

    @Override
    public void persist(String monitorEvent) {
        log.info(monitorEvent);
    }
}
</pre>
<h2>Conclusion</h2>
<p>I have showed you how we use Spring Integration to migrate XML content to Hippo CMS. As you could see the configuration is relatively simple and when we want to import a new content type, all we have to do is create a handler and wire up the configuration. As always SpringSource provides good documentation that makes is easy work with Spring Integration.</p>
<h2>References</h2>
<p style="text-align: left;"><strong>Spring Integration: <a title="http://static.springsource.org/spring-integration/reference/htmlsingle/spring-integration-reference.html" href="http://static.springsource.org/spring-integration/reference/htmlsingle/spring-integration-reference.html">http://static.springsource.org/spring-integration/reference/htmlsingle/spring-integration-reference.html</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jteam.nl/2010/01/13/migrating-content-with-spring-integration-a-real-life-example/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Some thoughts about the Dutch Government&#8230;.</title>
		<link>http://blog.jteam.nl/2009/03/30/some-thoughts-about-the-dutch-government/</link>
		<comments>http://blog.jteam.nl/2009/03/30/some-thoughts-about-the-dutch-government/#comments</comments>
		<pubDate>Mon, 30 Mar 2009 15:28:39 +0000</pubDate>
		<dc:creator>Leonard Wolters</dc:creator>
				<category><![CDATA[Business]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Agile]]></category>
		<category><![CDATA[Governmental]]></category>
		<category><![CDATA[Hippo]]></category>
		<category><![CDATA[Open Source]]></category>

		<guid isPermaLink="false">http://blog.jteam.nl/?p=25</guid>
		<description><![CDATA[As some of you might already noticed, recently a lot of exposure has been granted to the news that the Dutch Government has decided on using Open Source technologies for their new communication platform. Not only Dutch news sites like nu.nl &#38; webwereld announced this news, but even the traditional newspapers and &#8220;het Financiele Dagblad&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>As some of you might already noticed, recently a lot of exposure has been granted to the news that the Dutch Government has decided on using Open Source technologies for their new communication platform. Not only Dutch news sites like nu.nl &amp; webwereld announced this news, but even the traditional newspapers and &#8220;het Financiele Dagblad&#8221; thought this was something to proudly announce to the their readers.</p>
<p>Well, being true Open Source Believers from the early days, JTeam is very pleased that the world out there is finally starting to adopt the Open Source Model. More and more companies understand  the value of Open Source technologies (for example iLocal) and are convinced enough to build their business on top of it. And as for now, even the Dutch Government is starting to change. Well that&#8217;s a positive thing.</p>
<p>It shouldn&#8217;t come as a surprise that JTeam was (and still is) involved in the decision. We empowered the Minestry of General Affairs  (Ministerie van Algemene Zaken) and its personnel for about a couple of months and helped them to setup a good development process as well as taught its employees everything they need to know in order to execute this project by themselves. This is also something that pleasantly surprises me.  Instead of outsourcing IT projects and lightling candles every day hoping that their development partners will successfully deliver, they&#8217;ve become more mature and decided to give a try themselves. Having this knowledge &amp; experience in house will eventually result in better quality and less costs. Why? Because governmental projects are dynamic as well and also require an agile project management methodoly combined with XP development skills. It is questionable if outsourcing partners can deliver the same quality and communication skills as their internal department does. But let&#8217;s see how this project (and all related side projects) will eventually end.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.jteam.nl/2009/03/30/some-thoughts-about-the-dutch-government/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
