Processor 处理器

unmodifiableMap() 方法用于返回指定映射的不可修改视图

SuppressWarnings压制警告,即去除警告 
rawtypes是说传参时也要传递带泛型的参数
2-5-2-1、Processor 处理器

Processor是Camel中的一个接口使用Processor,此接口只有一个方法:
public void process(Exchange exchange) throws Exception;

从上述方法可以看出,Processor是一个可以直接操作Camel的Exchange对象的API。它让你可以访问所有在Camel的CamelContext中传输的部分。CamelContext对象可以通过Exchange的getCamelContext方法得到。
让我们看一个例子。在骑士汽车零部件系统中,每天都会把收到的订单输出为CSV格式的文件。公司使用了一个自定义格式的订单实体,但是为了使事情变得简单,他们使用了一个file协议,此协议会根据输入的日期参数返回一个订单列表。你面对的挑战就在于:把file协议返回的数据映射为CSV格式,并写到文件中。
因为你想从一个快速原型开始,您决定使用Camel的Processor。
代码中使用Processor将一个自定义格式转换为CSV格式

public class OrderToCsvProcessor implements Processor {

    public void process(Exchange exchange) throws Exception {
        String custom = exchange.getIn().getBody(String.class);

        String id = custom.substring(0, 10);
        String customerId = custom.substring(10, 20);
        String date = custom.substring(20, 30);
        String items = custom.substring(30);
        String[] itemIds = items.split("@");

        StringBuilder csv = new StringBuilder();
        csv.append(id.trim());
        csv.append(",").append(date.trim());
        csv.append(",").append(customerId.trim());
        for (String item : itemIds) {
            csv.append(",").append(item.trim());
        }

        exchange.getIn().setBody(csv.toString());
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

首先从exchange中获取自定义格式的内容。它是String类型的,所以你传入了String参数,Exchange返回了String类型的结果。接着你从自定义格式中提取数据到本地变量。自定义格式的内容可以是任意的,但在这个例子中,它是一个定长的自定义格式。接着你通过构建一个以逗号分隔的字符串将自定义格式映射为CSV格式。最后,使用CSV格式的payload替换了自定义格式的payload。
你可以在下列路由中使用上面的OrderToCsvProcessor

public class OrderToCsvProcessorTest extends CamelTestSupport {

    @Test
    public void testOrderToCsvProcessor() throws Exception {
        // this is the inhouse format we want to transform to CSV
        String inhouse = "0000004444000001212320091208  1217@1478@2132";
        template.sendBodyAndHeader("direct:start", inhouse, "Date", "20091208");

        File file = new File("target/orders/received/report-20091208.csv");
        assertTrue("File should exist", file.exists());

        // compare the expected file content
        String body = context.getTypeConverter().convertTo(String.class, file);
        assertEquals("0000004444,20091208,0000012123,1217,1478,2132", body);
    }

    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start")
                    // format inhouse to csv using a processor
                    .process(new OrderToCsvProcessor())
                    // and save it to a file
                    .to("file://target/orders/received?fileName=report-${header.Date}.csv");
            }
        };
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2-5-2-2、使用bean进行数据转换

使用bean进行数据转换是一个很好的实践,因为这种方式允许你使用任何你需要的java代码或者java类库。Camel对这点没有强加任何限制。Camel可以调用你开发的任意bean,甚至你可以使用已经存在的bean,而不需要重写或者重新编译他们。
让我们使用 bean代替前面的那个Processor。

/**
 * A bean which translates an order in custom inhouse format
 * to a CSV format.
 */
public class OrderToCsvBean {

    public String map(String custom) {
        String id = custom.substring(0, 10);
        String customerId = custom.substring(10, 20);
        String date = custom.substring(20, 30);
        String items = custom.substring(30);
        String[] itemIds = items.split("@");

        StringBuilder csv = new StringBuilder();
        csv.append(id.trim());
        csv.append(",").append(date.trim());
        csv.append(",").append(customerId.trim());
        for (String item : itemIds) {
            csv.append(",").append(item.trim());
        }
        return csv.toString();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
2-5-2-3、使用transform方法进行数据转换

transform是可以用在路由中的一个方法,可以用来进行消息转换。利用表达式,transform()具有极大的灵活性,有时还可以节省时间。例如,假设你需要使用<br/>标记取代所有HTML格式数据中的的换行符。此时可以使用Camel内置的表达式和正则表达式的搜索和替换:

from("direct:start") 
.transform(body().regexReplaceAll("\n", "<br/>")) 
.to("mock:result"); 
  • 1
  • 2
  • 3

上述路由使用transform()方法告诉Camel:消息要使用表达式进行转换。Camel利用建造者模式从路由中的表达式创建了整个表达式。这是通过方法调用链接在一起,这是建造者模式的本质。
在这个例子中,你联合使用了body()和regexReplaceAll()表达式,表达式应该这样读:获取body,并执行一个正则表达式,使用<br/>替换所有的\n,两个表达式组成了一个联合Camel表达式。

2-5-3、 Camel 类型转换器(Converter)

Camel提供了一个内置的类型转换系统,对常用类型之间进行自动转换。这个类型转换系统使Camel的各个组件直接可以很容易的协调工作,不会出现类型转换错误。从Camel用户的角度来看,在很多地方类型转换是内置在API中的,没有侵入性。例如,有如下代码:
String custom = exchange.getIn().getBody(String.class);
getBody方法接收了你所期望返回的类型为参数。在底层,类型转换系统将返回的结果转换成了Sring类型(如果需要的话)。
在本节中,我们将看看类型转换程序的内部机制。我们将解释Camel在启动时如何扫描类路径,进行动态注册类型转换器。我们还将向您展示在Camel路由中如何使用它,以及如何建立你自己的类型转换器。

2-5-3-1、 Camel类型转换器的运行机制

理解类型转换器的运行机制,首先需要理解在Camel中类型转换器是什么。图展示TypeConverterRegistry和TypeConverters之间的关系
这里写图片描述
在Camel启动时,所有的类型转换器都会注册到TypeConverterRegistry中。在运行时,Camel使用TypeConverterRegistry的lookup方法查找一个合适的TypeConverter来使用:
TypeConverter lookup(Class<?> toType, Class<?> fromType);
使用TypeConverter的convertTo方法,Camel可以将一种类型转换为另一种类型:
<T> T convertTo(Class<T> type, Object value);

2-5-3-2、使用Camel类型转换器

如前所述,Camel类型转换器被广泛使用,而且常常自动起作用。你可能要在一个路由中使用它们转换一个特定的类型,假设你需要将一些文件路由到JMS队列,并且使用javax.jmx.TextMessage类型的消息。你可以将每一个文件转换为String类型,迫使JMS组件使用TextMessage,Camel是很容易做到的—使用convertBodyTo方法,如下

from("file://riders/inbox")
.convertBodyTo(String.class)
.to("activemq:queue:inbox");
  • 1
  • 2
  • 3

如果使用Spring XML,如下:

<route>
<from uri="file://riders/inbox"/>
<convertBodyTo type="java.lang.String"/>
<to uri="activemq:queue:inbox"/>
</route>
  • 1
  • 2
  • 3
  • 4
  • 5

你可以省略java.lang.前缀 <convertBodyTo type="String"/>.

2-5-3-3、自定义类型转换器

在Camel中编写自己的类型转换器是很容易的。你已经看到类型转换器是什么样子。假设你想写一个converter用于将一个byte[]转换为PurchaseOrder对象。那么,你需要创建一个@Converter注解的类,类中包含转换方法,代码如下

@Converter
public final class PurchaseOrderConverter {

    @Converter
    public static PurchaseOrder toPurchaseOrder(byte[] data, Exchange exchange) {
        TypeConverter converter = exchange.getContext().getTypeConverter();

        String s = converter.convertTo(String.class, data);
        if (s == null || s.length() < 30) {
            throw new IllegalArgumentException("data is invalid");
        }

        s = s.replaceAll("##START##", "");
        s = s.replaceAll("##END##", "");

        String name = s.substring(0, 10).trim();
        String s2 = s.substring(10, 20).trim();
        String s3 = s.substring(20).trim();

        BigDecimal price = new BigDecimal(s2);
        price.setScale(2);

        Integer amount = converter.convertTo(Integer.class, s3);

        PurchaseOrder order = new PurchaseOrder(name, price, amount);
        return order;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

代码中,通过Exchange获取CamelContext,进而获取上级TypeConverter,用其进行String和byte转换。
现在你所需要做的是添加服务发现文件,文件名为:TypeConverter,位于META-INF目录中。正如前面所解释的那样,这个文件包含一行内容,用于Camel扫描这个包进而发现@Converter注解类。如图所示
这里写图片描述

3、 路由

3-1、如何使用RouteBuilder

通过继承抽像类org.apache.camel.builder.RouteBuilder是最常见的一种方式。然后重载configure方法如下代码所示

public class MyRouteBuilder extends RouteBuilder {

    /**
     * Let's configure the Camel routing rules using Java code...
     */
    public void configure() {
        // here is a sample which processes the input files
        // (leaving them in place - see the 'noop' flag)
        // then performs content based routing on the message using XPath
        from("file:src/data?noop=true&recusive=true")
            .choice()
                .when(xpath("/person/city = 'London'"))
                    .to("log:uk?showall=true")
                    .to("file:target/messages/uk")
                .otherwise()
                    .to("file:target/messages/others");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

现在只需要将该对象通过CamelContext类的addRoutes方法添加

CamelContext context = new DefaultCamelContext();
context.addRoutes(new MyRouteBuilder ());
  • 1
  • 2

3-2 创建路由-Java DSL

DSL领域定义语言,用来描述特定领域的特定表达。比如画图从起点到终点;路由中的从A到B。
示例代码如下

public class OrderRouterWithFilterTest extends CamelTestSupport {

    @Override
    protected CamelContext createCamelContext() throws Exception {
        // create CamelContext
        CamelContext camelContext = super.createCamelContext();

        // connect to embedded ActiveMQ JMS broker
        ConnectionFactory connectionFactory = 
            new ActiveMQConnectionFactory("vm://localhost");
        camelContext.addComponent("jms",
            JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));

        return camelContext;
    }

    @Test
    public void testPlacingOrders() throws Exception {
        getMockEndpoint("mock:xml").expectedMessageCount(1);
        assertMockEndpointsSatisfied();
    }

    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                // load file orders from src/data into the JMS queue
                from("file:src/data?noop=true").to("jms:incomingOrders");

                // content-based router
                from("jms:incomingOrders")
                    .choice()
                        .when(header("CamelFileName").endsWith(".xml"))
                            .to("jms:xmlOrders")  
                        .when(header("CamelFileName").regex("^.*(csv|csl)$"))
                            .to("jms:csvOrders")
                        .otherwise()
                            .to("jms:badOrders");
  ...            
            }
        };
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

3-3、创建路由-Spring方式

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

  <!-- set up ActiveMQ broker -->
  <broker:broker useJmx="false" persistent="false" brokerName="localhost">
    <broker:transportConnectors>
      <broker:transportConnector name="tcp" uri="tcp://localhost:61616"/>
    </broker:transportConnectors>
  </broker:broker>

  <bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
    <property name="connectionFactory">
      <bean class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://localhost:61616"/>
      </bean>
    </property>
  </bean>

  <bean id="downloadLogger" class="camelinaction.DownloadLogger"/>

  <camelContext xmlns="http://camel.apache.org/schema/spring">
    <route>
      <from uri="file:src/data?noop=true"/>
      <process ref="downloadLogger"/>
      <to uri="jms:incomingOrders"/>
    </route>
  </camelContext>
</beans>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3-4、EIP路由模式

3-4-1 Content-based router

3-4-2 消息filters

3-4-4、RecipientList 多个接收者

消息动态的路由到一个多个结点,recipientList 会将消息复制多份发送到后面的节点,后面每个节点处理的消息不是同一份,recipientList支持在线程池里面运行。recipientList 效果如同 multicast
这里写图片描述
示例代码如下

<route autoStartup="true">  
    <from uri="direct:recipientList" />  
    <recipientList delimiter="," ignoreInvalidEndpoints="true">  
        <header>endlist</header>  
    </recipientList>  
</route>  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
ProducerTemplate pt = context.getBean("camelTemplate", ProducerTemplate.class);  

pt.send("direct:routingSlip", new Processor()  
{  
    String endList = "bean:bean1,bean:changeInfo,bean:bean2";  
    public void process(Exchange exch) throws Exception   
    {  
        exch.getIn().setHeader("endlist", endList);  
    }  
});  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

最后,结果都会路由到bean:bean1,bean:changeInfo,bean:bean2这3个节点上面

3-4-5、wireTap窃听器

4、错误处理

编写应用程序集成异构系统,如何处理意想不到的事件是一个很大的挑战。在单一系统中,由于你有完全的控制权,你可以处理这些事件并恢复它。当时通过网络集成起来的系统有额外的风险:网络连接可能断掉、远程系统可能不会及时响应,或者它可能无缘无故宕机。即使在你的本地服务器,也可能发生意外事件,如服务器的磁盘满了或服务器内存耗尽。不管哪种错误出现,您的应用程序应该准备处理它们。
在这些情况下,日志文件通常是记录意外事件的唯一证据,所以日志非常重要。Camel对日志记录和错误处理有广泛支持,确保您的应用程序可以持续运行。
在本章,你会发现Camel的错误处理是多么灵活,深入和全面,学会如何对它进行定制,以应对大多数情况下的异常。我们将讨论Camel提供开箱即用的所有错误处理程序,以及他们的最佳使用场景,所以你可以选择最适合您的应用程序的处理方式。您还将了解如何配置和掌握消息返回处理,Camel可以使用返回技术尝试从特定的错误中恢复过来。我们还可以看到异常处理策略,这些策略允许你对错误进行区分,只处理特定的错误,以及定义通用错误处理规则,实现路由级别的错误处理。最后我们看下错误处理的细粒度控制

4-1、理解错误处理

在走进Camel错误处理的世界之前,我们需要退一步,看看更普遍的错误。首先,错误一般分为两大类:可恢复的错误和不可恢复的错误。其次我们需要看看何时何地开始错误处理,因为错误的发生是有先决条件的

4-1-1-1、可恢复的错误和不可恢复的错误

当涉及到的错误,我们可以将他们分为可恢复的错误和不可恢复的错误,如下图所示
这里写图片描述
不可恢复的错误是指一个错误,无论你尝试多少次相同的操作,它仍是一个错误。在集成的项目中,这可能意味着试图访问的数据库表不存在,这将导致JDBC驱动程序抛出SQLException异常。
可恢复的错误,是一个临时错误,在接下来的尝试操作后,可能并不会再次出现这个错误。比如网络连接错误导致的java.io.IOException。在接下来的尝试操作后,网络问题可能已经解决了,您的应用程序可以继续运行。
作为一个Java开发人员在日常生活中,你可能会遇到这样的错误分类: 可恢复的错误和不可恢复的错误。一般来说,异常处理代码使用两种分类之一,如下面两个代码片段所示。
第一个代码片段演示了一个常见的错误处理方式,将所有的异常认为是不可恢复的,放弃进一步的尝试,直接向调用者返回异常

public void handleOrder(Order order) throws OrderFailedException {
try {
service.sendOrder(order);
} catch (Exception e) {
throw new OrderFailedException(e);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

下一个片段通过添加一些处理改善了这种情况,在抛出异常之前进行的尝试,尝试5次后,如果仍然失败,抛出异常

public void handleOrder(Order order) throws OrderFailedException {
boolean done = false;
int retries = 5;
while (!done) {
try {
service.sendOrder(order);
done = true;
} catch (Exception e) {
if (--retries == 0) {
throw new OrderFailedException(e);
}
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

上面的例子都缺少对错误类型的验证,即发生的错误时可恢复的还是不可恢复的?进而采取相应的措施。可恢复的情况下,你可以再试一次,不能恢复的情况下,你可以立即放弃并重新抛出异常。
在Camel中,可恢复的错误由Throwable和Exception代表,可以通过org.apache.camel.Exchange类中的下面两个方法进行存取:

void setException(Throwable cause);
Exception getException();
  • 1
  • 2

不可恢复的错误由一个消息代表,此消息有一个错误标识,此标识可以通过org.apache.camel.Exchange来存取。例如,设置”Unknown customer”作为一个错误消息,可以这么做:
Message msg = Exchange.getOut();
msg.setFault(true);
msg.setBody(“Unknown customer”);
错误标识必须使用setFault(true)方法设置。
那么,在Camel中,为什么这两种类型的错误代表不同?原因有两个:第一,Camel API的设计符合JBI(Java业务集成)规范,此规范中有个错误消息的概念。第二,Camel核心包中内置了错误处理,所以,只要Camel中抛出异常,Camel都可以捕获它,并把它设置到Exchange中作为可恢复错误,如下

try {
processor.process(exchange);
} catch (Throwable e) {
exchange.setException(e);
}
  • 1
  • 2
  • 3
  • 4
  • 5

使用这种模式允许Came捕获和处理所有异常。Camel的错误处理机制就可以决定如何处理捕获的错误— 重试,传播错误返回给调用者,或者做别的事情。Camel的终端用户可以将不可恢复的错误设置为错误消息,Camel能做出相应的反应,停止路由该消息。
现在你已经了解了可恢复和不可恢复的错误,让我们总结下他们在Camel中是如何表示的:
1、异常(Exceptions)表示为可恢复错误。
2、错误信息表示为不可恢复的错误。

5、理解camel组件

5-1、camel组件预览

参考

http://blog.csdn.net/yinwenjie/article/details/51769820


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值