2015年7月9日星期四

Fuse_003:Fuse 快速上手之三:camel-errorhandler

环境:JBoss Fuse 6.2.0

1. 学习重点
(1)如何定义和使用 bean 处理消息
(2)如何定义 default error handler
(3)如何定义 exception-specific error handler

2. errors.xml 
(1)设计图

(2)源码
<?xml version="1.0" encoding="UTF-8"?>
<!--
    JBoss, Home of Professional Open Source
    Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual
    contributors by the @authors tag. See the copyright.txt in the
    distribution for a full listing of individual contributors.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<!--
   This is the OSGi Blueprint XML file defining the Camel context and routes.  Because the file is in the
   OSGI-INF/blueprint directory inside our JAR, it will be automatically activated as soon as the artifact is being installed.

   The root element for any OSGi Blueprint file is 'blueprint' - you also see the namespace definitions for both the Blueprint
   and the Camel namespaces.
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
             http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
             http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">


    <!--
      We are using the OSGi Blueprint XML syntax to define a bean that we use in our Camel route to validate
      the order date. It has a method that intermittently throws runtime exceptions.
    -->
    <bean id="myOrderService" class="io.fabric8.quickstarts.errors.OrderService"/>

    <!--
      Here, we define the dead-letter channel configuration we want to use.  We want to retry delivering a failed exchange
      twice and we also want to use exponential backoff between retries (so first retry after 1 second, second retry another
      2 seconds later).  After a total of 3 failed deliveries (1 initial delivery plus our 2 redeliveries), the message will
      be sent to the configured dead letter uri (direct:deadletter).
    -->
    <bean id="myDeadLetterErrorHandler" class="org.apache.camel.builder.DeadLetterChannelBuilder">
        <property name="deadLetterUri" value="direct:deadletter"/>
        <property name="redeliveryPolicy">
            <bean class="org.apache.camel.processor.RedeliveryPolicy">
                <property name="maximumRedeliveries" value="2"/>
                <property name="useExponentialBackOff" value="true"/>
            </bean>
        </property>
    </bean>

    <!--
      The namespace for the camelContext element in Blueprint is 'http://camel.apache.org/schema/blueprint'.  Additionally,
      we can also define namespace prefixes we want to use in the XPath expressions in our CBR here.

      While it is not required to assign id's to the <camelContext/> and <route/> elements, it is usually a good idea
      to set those for runtime management purposes (logging, JMX MBeans, ...)

      We also configure the default error handler for this Camel context by setting the errorHandlerRef attribute to the
      'myDeadLetterErrorHandler' bean defined earlier.  Any exceptions that are not being handled by a more specific
      mechanism (e.g. an <onException/> or <doTry/>) will be handled by this default error handler.
    -->
    <camelContext xmlns="http://camel.apache.org/schema/blueprint"
                  id="errors-example-context"
                  errorHandlerRef="myDeadLetterErrorHandler">

        <!--
          Using <onException/>, we can define recovery scenarios for specific exceptions.  In our case, if order date validation
          fails (OrderValidationException is thrown, we want to move the file to the work/errors/validation folder.

          We don't specify a redelivery policy and mark the exception handled to ensure processing will not be retried for this exception.
        -->
        <onException>
            <exception>io.fabric8.quickstarts.errors.OrderValidationException</exception>
            <handled>
                <constant>true</constant>
            </handled>
            <log message="Validation failed for ${file:name} - moving the file to work/errors/validation"/>
            <to uri="file:work/errors/validation"/>
        </onException>

        <!--
          This is the main Camel route.  Files dropped in work/errors/input directory will be processed by two methods in our order service bean:
          - the first method will validate the order date - it will throw an OrderValidationException whenever the order date is a Sunday
            these exceptions will be handled by the <onException/> definition
          - the second method will just randomly throw a RuntimeException 2 out of 3 times
            our default error handler strategy configured on the <camelContext/> will retry the exchange 3 times and afterwards send it to the dead letter channel
        -->
        <route id="mainRoute">
            <from uri="file:work/errors/input"/>
            <log message="Processing ${file:name}"/>
            <to uri="bean:myOrderService?method=validateOrderDate"/>
            <to uri="bean:myOrderService?method=randomlyThrowRuntimeException"/>
            <to uri="file:work/errors/done"/>
            <log message="Done processing ${file:name}"/>
        </route>

        <!--
          This route starts with the direct:deadletter endpoint we used in the 'myDeadLetterErrorHandler' bean definition,
          so any exchanges that have failed delivery 3 times will be sent to this route.  The route itself logs a human-friendly
          error message and afterwards stores the failed message in the work/errors/deadletter folder.
        -->
        <route id="dlcRoute">
            <from uri="direct:deadletter"/>
            <log message="File ${file:name} was moved to the dead letter channel"/>
            <to uri="file:work/errors/deadletter"/>
        </route>

    </camelContext>

</blueprint>

3. OrderService.java

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014, Red Hat, Inc. and/or its affiliates, and individual
 * contributors by the @authors tag. See the copyright.txt in the
 * distribution for a full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.fabric8.quickstarts.errors;

import org.apache.camel.Exchange;
import org.apache.camel.Header;
import org.apache.camel.language.NamespacePrefix;
import org.apache.camel.language.XPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Random;

/**
 * An order service implementation that provides one method to do validation and another method that just randomly throws
 * Exceptions to be able to test error handling in our Camel route.
 */
public class OrderService {

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderService.class);
    private static final Random RANDOM = new Random();

    /**
     * Validate the order date - orders should only be place from Monday to Saturday.
     *
     * This method can be used as a plain Java method, but when it is used inside a Camel route, the @XPath annotation will kick
     * in, evaluating the XPath expression and using the result as the method parameter. In this case, it will fetch the order
     * date from the order XML message.
     *
     * @param date the order date
     * @throws OrderValidationException when the order date is a Sunday
     */
    public void validateOrderDate(
        @XPath(value = "/order:order/order:date",
            namespaces = @NamespacePrefix(prefix = "order", uri = "http://fabric8.com/examples/order/v7")) String date) throws OrderValidationException {
        final Calendar calendar = new GregorianCalendar();
        try {
            calendar.setTime(DATE_FORMAT.parse(date));
            if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                LOGGER.warn("Order validation failure: order date " + date + " should not be a Sunday");
                throw new OrderValidationException("Order date should not be a Sunday: " + date);
            }
        } catch (ParseException e) {
            throw new OrderValidationException("Invalid order date: " + date);
        }
    }

    /**
     * This method throws a runtime exception 2 out of 3 times. This is completely useless in real life, but in this example we
     * use this to demonstrate Camel's error handling capabilities.
     *
     * In order to be able to log which file is being processed when throwing the exception, we the Camel @Header annotation to
     * extract the file name from the message.
     *
     * @param name the file name
     */
    public void randomlyThrowRuntimeException(@Header(Exchange.FILE_NAME) String name) {
        if (RANDOM.nextInt(3) > 0) {
            LOGGER.warn("An unexpected runtime exception occurred while processing " + name);
            throw new RuntimeException("Something else went wrong while handling this message");
        }
    }
}

4. 编译、部署、卸载
 (1)cd /Users/maping/Redhat/fuse/jboss-fuse-6.2.0.redhat-133/quickstarts/beginner/camel-errorhandler
(2)mvn clean install
(4)./fuse
(5)osgi:install -s mvn:org.jboss.quickstarts.fuse/beginner-camel-errorhandler/6.2.0.redhat-133
(6)osgi:list
(7)cp /Users/maping/Redhat/fuse/jboss-fuse-6.2.0.redhat-133/quickstarts/beginner/camel-errorhandler/src/main/fabric8/data/* /Users/maping/Redhat/fuse/jboss-fuse-6.2.0.redhat-133/work/errors/input
(8)cd /Users/maping/Redhat/fuse/jboss-fuse-6.2.0.redhat-133/work/errors/
(9)osgi:uninstall <id>

1 条评论:

brightwoods 说...

带有示例代码的java编程语言
用java语言摆动按钮