Google

Jun 18, 2013

Advanced Apache Camel tutorial

This is an extension to the last two Apache Camel tutorials:
This advanced tutorial extends part-2. The first route (consumer) polls for csv files and once a file is found in the c:/temp/adv folder, a sequential route is created by placing the info to two in memory queues via the "direct" component. Firstly, direct:email consumes the exchange data and generates an email with empty body to notify user that a file has arrived. The second consummer direct:transfer receives the exchange, and transforms the document to another csv using the PersonMapper class depending on if the conversion succeeded or not, the file is  written to either converted or rejected sub folder. The key thing to make note is that how body and header information are passed back and forth between a n XML based routing in Spring and Java beans.

Step 1: The adv_file_converter.properties contains the name/value pairs

adv.in.dir=c:/temp/adv
adv.in.file.pattern=.*\.csv
adv.out.dir=c:/temp/adv
adv.converted.filename=Accepted_File_adv
adv.rejected.filename=Rejected_File_adv
adv.email.to=peter.smith@mycompany.com
adv.email.from=john.smith@mycompany.com

common.mail.host=mailhost.mycompany.net
common.mail.port=25

poll.delay=60000
initial.delay=10000

Step 2: Add the  camel-mail component to send email via smtp endpoints. Make sure that you have the following in the pom.xml.

     
    <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-core</artifactId>
   <version>2.10.4</version>
  </dependency>
  <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-spring</artifactId>
   <version>2.10.4</version>
  </dependency>
  <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-csv</artifactId>
   <version>2.10.4</version>
  </dependency>
  <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-mail</artifactId>
   <version>2.10.4</version>
  </dependency>

Step 3: The adv_file_route.xml file with the relevant routes defined.



 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

 <routeContext id="advFileRoute" xmlns="http://camel.apache.org/schema/spring">
  <route id="advFileConversion">
   <from
    uri="file://{{adv.in.dir}}?include={{adv.in.file.pattern}}&delay={{poll.delay}}&initialDelay={{initial.delay}}&move=archive/" />

   <multicast stopOnException="false">
    <to uri="direct:email" /> 
    <to uri="direct:transfer" />
   </multicast>
  </route>

  <route id="inMemory">
   <from uri="direct:email" />
   <camel:setHeader headerName="subject">
    <camel:simple>${file:name} has been received</camel:simple>
   </camel:setHeader>
      <!-- reset body to null otherwise file content will be sent via email body-->
   <camel:setBody>
    <constant></constant>
   </camel:setBody> 
   <to
    uri="smtp://{{common.mail.host}}?contentType=text/html&to={{adv.email.to}}&from={{adv.email.from}}" />
  </route>
  <route>
   <from uri="direct:transfer" />
   <unmarshal>
    <csv skipFirstLine="true" />
   </unmarshal>
   <to uri="bean:personMapper" />
   <marshal>
    <csv delimiter="," />
   </marshal>
   <convertBodyTo type="java.lang.String" />

   <choice>
    <when>
     <simple>${headers.status} == 'accepted'</simple>
     <to uri="file://{{adv.out.dir}}/converted?fileName={{adv.converted.filename}}_${headers.dest_file_suffix}" />
    </when>
    <when>
     <simple>${headers.status} == 'rejected'</simple>
     <to uri="file://{{adv.out.dir}}/rejected?fileName={{adv.rejected.filename}}_${headers.dest_file_suffix}" />
    </when>
   </choice>

  </route>
 </routeContext>

</beans>

Step 4: The PersonMapper class that transforms the input file.

 
package com.mycompany.app5;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.camel.Exchange;
import org.springframework.stereotype.Component;

@Component("personMapper")
public class PersonMapper
{
    
    private static final String HEADER_STATUS = "status";
    private static final String HEADER_DEST_FILE_SUFFIX = "dest_file_suffix";
    
    enum Status
    {
        accepted, rejected
    };
    
    public Object service(List<List<String>> data, Exchange exchange)
    {
        List<Map<String, String>> results = new ArrayList<Map<String, String>>();
        
        Map<String, String> headerLine = new LinkedHashMap<String, String>();
        headerLine.put("surname", "surname");
        headerLine.put("firstname", "firstname");
        headerLine.put("age", "age");
        
        results.add(headerLine);
        
        boolean accepted = true;
        
        for (List<String> line : data)
        {
            Map<String, String> resultLine = new LinkedHashMap<String, String>();
            resultLine.put("surname", line.get(1));
            resultLine.put("firstname", line.get(0));
            resultLine.put("age", line.get(2));
            results.add(resultLine);
            
            if (line.get(1) == null || line.get(2) == null)
            {
                accepted = false;
            }
            
        }
        
        if (accepted)
        {
            exchange.getIn().setHeader(HEADER_STATUS, Status.accepted.name());
        }
        else
        {
            exchange.getIn().setHeader(HEADER_STATUS, Status.rejected.name());
        }
        
        String srcFileName = (String) exchange.getIn().getHeader("CamelFileNameOnly");
        exchange.getIn().setHeader(HEADER_DEST_FILE_SUFFIX, srcFileName);
        
        return results;
        
    }
}

Step 5: The main class that kicks off the standalone route.

 
package com.mycompany.app5;

import org.apache.camel.spring.Main;

public class StandAloneCamelWithSpring
{
    
    private Main main;
    
    public static void main(String[] args) throws Exception
    {
        StandAloneCamelWithSpring example = new StandAloneCamelWithSpring();
        example.boot();
    }
    
    private void boot() throws Exception
    {
        // create a Main instance
        main = new Main();
        // enable hangup support so you can press ctrl + c to terminate the JVM
        main.enableHangupSupport();
        main.setApplicationContextUri("applicationContext.xml");
        
        // run until you terminate the JVM
        System.out.println("Starting Camel. Use ctrl + c to terminate the JVM.\n");
        main.run();
        
    }
    
}

Step 6: The other relevant file "applicationContext.xml"

   
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">

 <context:component-scan base-package="com.mycompany.app5" />

 <context:annotation-config />
 <context:spring-configured />

 <task:annotation-driven />

 <bean id="bridgePropertyPlaceholder"
  class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
  <property name="locations">
   <list>
    <value>classpath:*.properties</value>
   </list>
  </property>
 </bean>


 <import resource="classpath*:route.xml" />
</beans>

Step 7: Finally, the route.xml file.

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
 xmlns:camel="http://camel.apache.org/schema/spring"
 xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

 <import resource="classpath*:adv_file_route.xml" />

 <camel:errorHandler id="defaultErrorHandler" type="DefaultErrorHandler" />

 <camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring"
  errorHandlerRef="defaultErrorHandler">
  <routeContextRef ref="advFileRoute" />
 </camelContext>

</beans>

The route definition file "adv_file_route.xml" is covered in Step 3.

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home