tomcat 7 下修改session默认cookie名JSESSIONID

 apache, tomcat, 未分类  tomcat 7 下修改session默认cookie名JSESSIONID已关闭评论
6月 052015
 

看看下面这个配置:

<Context path=”” docBase=”D:WORKDIRoschinawebapp” reloadable=”false” sessionCookiePath=”/” sessionCookieName=”OUU_SESSION”/>

原来 J2EE 应用所使用的 Session 对应的是一个名为 JSESSIONID 的 Cookie ,现在在 Tomcat 7 中你可以修改这个 Cookie 的名称、PATH了

如没有指定到项目,使用下面到配置:

<Context  sessionCookiePath=”/” sessionCookieName=”OUU_JSESSIONID”>

使用Apache Commons Configuration读取配置信息

 apache, java  使用Apache Commons Configuration读取配置信息已关闭评论
4月 302014
 

好文章一起分享:


    在项目中使用一些比较新的库总会给你带来很多快乐,在这篇文章中,我将会给你介绍一个在Java中读取配置文件的框架——Apache Commons Configuration framework. 

你会了解到

    ·从XML文件中获取数据
    ·访问环境变量
    ·连接不同类型的配置信息(基于XML的,基于环境变量的,等等)

    ·在程序改变后自动重新加载配置。

    在我们的示例中将会使用XML文件和环境变量的两种方式存储一个数据库的配置信息(开发、测试、产品,等等)。接下来你将会看到具体的内容,但是首先先配置一下Maven。

Maven设置

我们的示例程序需要在pom.xml文件中添加如下依赖:

<dependencies>
    <dependency>
        <groupId>commons-configuration</groupId>
        <artifactId>commons-configuration</artifactId>
        <version>1.8</version>
    </dependency>
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.8.0</version>
    </dependency>
    <dependency>
        <groupId>commons-jxpath</groupId>
        <artifactId>commons-jxpath</artifactId>
        <version>1.3</version>
    </dependency>
</dependencies>


简单的数据库配置

    设想我们有一个简单的数据库配置信息存储在XML文件中:

<?xmlversion="1.0"encoding="UTF-8"?>
<!-- const.xml -->
<config>
    <database>
        <url>127.0.0.1</url>
        <port>1521</port>
        <login>admin</login>
        <password>pass</password>
    </database>
</config>

    为了拿到url和port我们用如下的代码:

XMLConfiguration config =new XMLConfiguration("const.xml");
// 127.0.0.1
config.getString("database.url");
// 1521
config.getString("database.port");

    XMLConfiguration是一个Apache Commons类,他可以从指定的配置文件中加载内容,并且提供一个很好的以指定的点的方式得到存储的值。例如例子中的表达式database.port映射到xml文件中的 config/database/port节点的值“1521”。当然还有很多方式获取数据。这里有一些基本的用法:

getBoolean
getByte
getDouble
getFloat
getInt
getInteger
getList
getLong
getStringArray

    你可通过查看Apache Commons的JavaDoc查看更多的使用方式。


将如上配置扩展一步

    设想,过了一会我们想配置多个数据库,我们可以在配置文件中这样写:

<?xmlversion="1.0"encoding="UTF-8"?>
<!-- const.xml -->
<config>
    <databases>
        <database>
            <name>dev</name>
            <url>127.0.0.1</url>
            <port>1521</port>
            <login>admin</login>
            <password>pass</password>
        </database>
        <database>
            <name>production</name>
            <url>192.23.44.100</url>
            <port>1521</port>
            <login>admin</login>
            <password>not-so-easy-pass</password>
        </database>
    </databases>
</config>

    现在我们要访问url数据我们可以这样:

XMLConfiguration config =new XMLConfiguration("const.xml");

// 127.0.0.1
config.getString("databases.database(0).url");

// 192.23.44.100
config.getString("databases.database(1).url");

    你可以看到这次多了参数,0代表第一个,1代表第二个。


XPath表达式

    定点的访问方式没问题,但是只是在一些简单的情况下,对于复杂的真实的状况来讲,我们可能需要使用XPath表达式语言。这里的主要优点是,即便你使用了XML的高级查询,程序看起来仍然比较简洁易懂。

XMLConfiguration config =new XMLConfiguration("const.xml");
config.setExpressionEngine(new XPathExpressionEngine());

// 127.0.0.1
config.getString("databases/database[name = 'dev']/url");

// 192.23.44.100
config.getString("databases/database[name = 'production']/url");

    这里是上面两个XPath表达式查询的一个解释:


访问环境变量

    在Apache Commons Configuration的帮助下你可以轻松的访问到系统的环境变量。下面是访问系统中ENV_TYPE变量的方法:

EnvironmentConfiguration config =new EnvironmentConfiguration();
config.getString("ENV_TYPE");

    假设变量ENV_TYPE已经设置好了,想看上面的程序是否运行正确,你可以在控制台运行如下脚本:

echo %ENV_TYPE%        # for Windows
# or...
echo $ENV_TYPE         # for Linux/Mac OS

你将会看到ENV_TYPE的值。


联合配置

    让我们总结一下我们了解的东西,下面的getDbUrl方法做如下事情:
    ·检查系统环境变量中叫做ENV_TYPE的值。
    ·如果值是dev或者produtcion就返回相应的数据库url
    ·如果变量没有配置就抛出异常。
public String getDbUrl() throws ConfigurationException {
    EnvironmentConfiguration envConfig =new EnvironmentConfiguration();
    String env = envConfig.getString("ENV_TYPE");
    if("dev".equals(env) ||"production".equals(env)) {
        XMLConfiguration xmlConfig =new XMLConfiguration("const.xml");
        xmlConfig.setExpressionEngine(new XPathExpressionEngine());
        String xpath ="databases/database[name = '"+ env +"']/url";
        return xmlConfig.getString(xpath);
    }else{
        String msg ="ENV_TYPE environment variable is "+
                     "not properly set";
        throw new IllegalStateException(msg);
    }
}


集中你的配置

    对每个不同的需要配置的对象创建多个配置比较烦。假如我们想添加其他的基于XML的配置,我们会怎么搞?我们需要创建另一个XMLConfiguration对象,这会给管理带来很多麻烦。一个解决办法是把配置文件信息集中到一个单个XML文件中。下面是一个例子:

<?xmlversion="1.0"encoding="UTF-8"?>
<!-- config.xml -->
<configuration>
  <env/>
  <xmlfileName="const.xml"/>
</configuration>

    你需要使用DefaultConfigurationBuilder类,最终版本的getDbUrl方法看起来像这样:

public String getDbUrl()throws ConfigurationException {
    DefaultConfigurationBuilder builder =
        new DefaultConfigurationBuilder("config.xml");
    boolean load =true;
    CombinedConfiguration config = builder.getConfiguration(load);
    config.setExpressionEngine(new XPathExpressionEngine());
    String env = config.getString("ENV_TYPE");
    if("dev".equals(env) ||"production".equals(env)) {
        String xpath ="databases/database[name = '"+ env +"']/url";
        return config.getString(xpath);
    }else{
        String msg ="ENV_TYPE environment variable is "+
                     "not properly set";
        throw new IllegalStateException(msg);
    }
}


自动重新加载

    Apache Commons Configuration有很多非常酷的特性。其中一个就是当基于文件的配置变化的时候自动加载,因为我们可以设置加载策略。框架会轮询配置文件,当文件的内容发生改变时,配置对象也会刷新。你可以用程序控制:

XMLConfiguration config =new XMLConfiguration("const.xml");
ReloadingStrategy strategy =new FileChangedReloadingStrategy();
strategy.setRefreshDelay(5000);
config.setReloadingStrategy(strategy);

    或者把他写到主配置文件中:

<?xmlversion="1.0"encoding="UTF-8"?>
<!-- config.xml -->
<configuration>
  <env/>
  <xmlfileName="const.xml">
    <reloadingStrategyrefreshDelay="5000"
      config-class="org.apache.commons.configuration.reloading.FileChangedReloadingStrategy"/>
  </xml>
</configuration>

    每五秒框架都检查一下配置文件有没有改变。


最后

    我个人用来管理需要配置的代码的时候的选择是使用Apache Commons。我希望这篇文章可以让你相信这个框架可以提供一个非常有用的接口用来访问你的静态数据。最后需要说的一点,这篇文章的内容只是这个框架的功能的一部分。此框架一些其他的有趣的功能是:
    ·从不同的数据源装载配置信息,例如properties文件、ini文件、数据库等等。
    ·给配置对象增加新的属性,并把它回存到文件中。
    ·监听配置对象改变的事件。(这点可以让你在配置发生改变的时候及时作出处理)

    ·自动解析配置文件的实际路径(不管你是把它放在程序文件夹下还是一个jar中。)


转自:http://m.oschina.net/blog/57171 

使用Apache Commons Chain

 apache  使用Apache Commons Chain已关闭评论
3月 072014
 

作为程序开发人员,我们经常需要对一个实际上程序性的系统应用面向对象的方法。商业分析家和管理人员描述这样的系统时通常不使用类层次和序列图,而是使用流程图和工作流图表。但是不论如何,使用面向对象的方法解决这些问题时会带来更多的灵活性。面向对象的设计模式提供了有用的结构和行为来描述这种顺序的处理,比如模版方法(Template Method)[GoF]和责任链(Chain of Responsibility)[GoF]。 

Jakarta Commons的子项目Chain将上述两个模式组合成一个可复用的Java框架用于描述顺序的处理流程。这个在Jakarta Commons project社区中开发的框架,已经被广泛的接受并且使用于许多有趣的应用中,特别的是他被Struts和Shale应用框架作为处理HTTP请求处理的基础机制。你可以在需要定义和执行一组连续的步骤时使用Commons Chain。 

至于经典设计模式,开发者和架构师普遍使用模版方法(Template Method)造型顺序处理。模版方法(Template Method)中使用一个抽象的父类定义使用的算法:处理的步骤,具体实现交给子类。当然,父类也可以为算法所使用的方法提供一个缺省实现。 

由于模版方法(Template Method)依赖继承——子类必须继承定义了算法的父类——因此使用这个模式的软件表现出紧耦合而且缺少灵活性。又由于实现类添加自己的行为前必须扩展父类,沟每⑷嗽北幌拗朴诶嗖愦沃校佣拗屏顺绦蛏杓频牧榛钚浴ommons Chain使用配置文件定义算法,在程序运行时解析配置文件,从而很好的解决了这个问题。 

现在来看一下Commons Chain是怎样工作的,我们从一个人造的例子开始:二手车销售员的商业流程。下面是销售流程的步骤: 

  1. 得到用户信息
  2. 试车
  3. 谈判销售
  4. 安排财务
  5. 结束销售

现在假设使用模版方法(Template Method)造型这个流程。首先建立一个定义了算法的抽象类: 

Java代码  收藏代码

  1. public abstract class SellVehicleTemplate {  
  2.   
  3.     public void sellVehicle() {  
  4.         getCustomerInfo();  
  5.         testDriveVehicle();  
  6.         negotiateSale();  
  7.         arrangeFinancing();  
  8.         closeSale();  
  9.     }  
  10.   
  11.     public abstract void getCustomerInfo();  
  12.   
  13.     public abstract void testDriveVehicle();  
  14.   
  15.     public abstract void negotiateSale();  
  16.   
  17.     public abstract void arrangeFinancing();  
  18.   
  19.     public abstract void closeSale();  
  20. }  

现在来看一下怎样用Commons Chain实现这个流程。首先,下载Commons Chain。你可以直接下载最新的zip或tar文件,也可以从CVS或者SubVersion源码库检出Commons Chain模块得到最新的代码。解压缩打包文件,将commons-chain.jar放入你的classpath中。 

使用Commons Chain实现这个商业流程,必须将流程中的每一步写成一个类,这个类需要有一个public的方法execute()。这和传统的命令模式(Command pattern)实现相同。下面简单实现了“得到用户信息”: 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.Command;  
  4. import org.apache.commons.chain.Context;  
  5.   
  6. public class GetCustomerInfo implements Command {  
  7.   
  8.     public boolean execute(Context ctx) throws Exception  
  9.         System.out.println(“Get customer info”);  
  10.         ctx.put(“customerName”,“George Burdell”);  
  11.         return false;  
  12.     }  
  13.   
  14. }  

由于只是演示,这个类并没有做很多工作。这里将用户名放入了Context对象ctx中。这个Context对象连接了各个命令。暂时先将这个对象想象成根据关键字存取值的哈希表。所有后来的命令可以通过它访问刚才放入的用户名。TestDriveVehicle,NegotiateSale和 ArrangeFinancing命令的实现只是简单的打印了将执行什么操作。 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.Command;  
  4. import org.apache.commons.chain.Context;  
  5.   
  6. public class TestDriveVehicle implements Command {  
  7.   
  8.     public boolean execute(Context ctx) throws Exception {  
  9.         System.out.println(“Test drive the vehicle”);  
  10.         return false;  
  11.     }  
  12.   
  13. }  
  14.   
  15. public class NegotiateSale implements Command {  
  16.   
  17.     public boolean execute(Context ctx) throws Exception {  
  18.         System.out.println(“Negotiate sale”);  
  19.         return false;  
  20.     }  
  21.   
  22. }  
  23.   
  24. public class ArrangeFinancing implements Command {  
  25.   
  26.     public boolean execute(Context ctx) throws Exception {  
  27.         System.out.println(“Arrange financing”);  
  28.         return false;  
  29.     }  
  30.   
  31. }  

CloseSale从Context对象中取出GetCustomerInfo放入的用户名,并将其打印。 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.Command;  
  4. import org.apache.commons.chain.Context;  
  5.   
  6. public class CloseSale implements Command {  
  7.   
  8.     public boolean execute(Context ctx) throws Exception {  
  9.         System.out.println(“Congratulations “ + ctx.get(“customerName”) +“, you bought a new car!”);  
  10.         return false;  
  11.     }  
  12.   
  13. }  

现在你可以将这个流程定义成一个序列(或者说“命令链”)。 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.impl.ChainBase;  
  4. import org.apache.commons.chain.Command;  
  5. import org.apache.commons.chain.Context;  
  6. import org.apache.commons.chain.impl.ContextBase;  
  7.   
  8. public class SellVehicleChain extends ChainBase {  
  9.   
  10.     public SellVehicleChain() {  
  11.         super();  
  12.         addCommand(new GetCustomerInfo());  
  13.         addCommand(new TestDriveVehicle());  
  14.         addCommand(new NegotiateSale());  
  15.         addCommand(new ArrangeFinancing());  
  16.         addCommand(new CloseSale());  
  17.     }  
  18.   
  19.     public static void main(String[] args) throws Exception {  
  20.         Command process = new SellVehicleChain();  
  21.         Context ctx = new ContextBase();  
  22.         process.execute(ctx);  
  23.     }  
  24.   
  25. }  

运行这个类将会输出以下结果: 
Get customer info 
Test drive the vehicle 
Negotiate sale 
Arrange financing 
Congratulations George Burdell, you bought a new car!
 

在进一步深入之前,让我们来看一下我们使用了的Commons Chain的类和接口。 
 

Command 类和Chain类的关系就是组合模式(Composite pattern)[GoF]的例子:Chain不仅由多个Command组成,而且自己也是Command。这使你可以非常简单得将单个命令(Command)替换成由多个命令(Command)组成的链(Chain)。这个由Command对象唯一操作定义的方法代表了一个直接的命令: 

public boolean execute(Context context); 

参数context仅仅是一个存放了名称-值对的集合。接口Context在这里作为一个标记接口:它扩展了java.util.Map但是没有添加任何特殊的行为。于此相反,类ContextBase不仅提供了对Map的实现而且增加了一个特性:属性-域透明。这个特性可以通过使用Map的put和get 方法操作JavaBean的域,当然这些域必须使用标准的getFoo和setFoo方法定义。那些通过JavaBean的“setter”方法设置的值,可以通过对应的域名称,用Map的get方法得到。同样,那些用Map的put方法设置的值可以通过JavaBean的“getter”方法得到。 

例如,我们可以创建一个专门的context提供显式的customerName属性支持。 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.impl.ContextBase;  
  4.   
  5. public class SellVehicleContext extends ContextBase {  
  6.   
  7.     private String customerName;  
  8.   
  9.     public String getCustomerName() {  
  10.         return customerName;  
  11.     }  
  12.   
  13.     public void setCustomerName(String name) {  
  14.         this.customerName = name;  
  15.     }  
  16.   
  17. }  

现在你既可以进行Map的一般属性存取操作同时也可以使用显式的JavaBean的访问和修改域的方法,这两个将产生同样的效果。但是首先你需要在运行SellVehicleChain时实例化SellVehiceContext而不是ContextBase。 

Java代码  收藏代码

  1. public static void main(String[] args) throws Exception {  
  2.     Command process = new SellVehicleChain();  
  3.     Context ctx = new SellVehicleContext();  
  4.     process.execute(ctx);  
  5. }  

尽管你不改变GetCustomerInfo中存放用户名的方法——仍然使用ctx.put(“customerName”, “George Burdell”)——你可以在CloseSale中使用getCustomerName()方法得到用户名。 

Java代码  收藏代码

  1. public boolean execute(Context ctx) throws Exception {  
  2.     SellVehicleContext myCtx = (SellVehicleContext) ctx;  
  3.     System.out.println(“Congratulations “ + myCtx.getCustomerName() + “, you bought a new car!”);  
  4.     return false;  
  5. }  

那些依赖类型安全和context的显式域的命令(Command)可以利用标准的getter和setter方法。当一些新的命令(Command)被添加时,它们可以不用考虑context的具体实现,直接通过Map的get和put操作属性。不论采用何种机制,ContextBase类都可以保证命令(Command)间可以通过context互操作。 

下面这个例子展示了如何使用Commons Chain的API建立并执行一组顺序的命令。当然,和现在大多数Java软件一样,Commons Chain可以使用XML文件作为配置文件。你可以将“汽车销售”流程的步骤在XML文件中定义。这个文件有个规范的命名chain- config.xml。 

Xml代码  收藏代码

  1. <catalog>  
  2.   <chain name=“sell-vehicle”>  
  3.     <command id=“GetCustomerInfo” className=“com.jadecove.chain.sample.GetCustomerInfo”/>  
  4.     <command id=“TestDriveVehicle” className=“com.jadecove.chain.sample.TestDriveVehicle”/>  
  5.     <command id=“NegotiateSale” className=“com.jadecove.chain.sample.NegotiateSale”/>  
  6.     <command id=“ArrangeFinancing” className=“com.jadecove.chain.sample.ArrangeFinancing”/>  
  7.     <command id=“CloseSale” className=“com.jadecove.chain.sample.CloseSale”/>  
  8.   </chain>  
  9. </catalog>  

Chain的配置文件可以包含多个链定义,这些链定义可以集合进不同的编目中。在这个例子中,链定义在一个默认的编目中定义。事实上,你可以在这个文件中定义多个名字的编目,每个编目可拥有自己的链组。 

现在你可以使用Commons Chain提供的类载入编目并得到指定的链,而不用像SellVehicleChain中那样自己在程序中定义一组命令: 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.Catalog;  
  4. import org.apache.commons.chain.Command;  
  5. import org.apache.commons.chain.Context;  
  6. import org.apache.commons.chain.config.ConfigParser;  
  7. import org.apache.commons.chain.impl.CatalogFactoryBase;  
  8.   
  9. public class CatalogLoader {  
  10.   
  11.     private static final String CONFIG_FILE = “/com/jadecove/chain/sample/chain-config.xml”;  
  12.   
  13.     private ConfigParser parser;  
  14.   
  15.     private Catalog catalog;  
  16.   
  17.     public CatalogLoader() {  
  18.         parser = new ConfigParser();  
  19.     }  
  20.   
  21.     public Catalog getCatalog() throws Exception {  
  22.         if (catalog == null) {  
  23.             parser.parse(this.getClass().getResource(CONFIG_FILE));  
  24.         }  
  25.         catalog = CatalogFactoryBase.getInstance().getCatalog();  
  26.         return catalog;  
  27.     }  
  28.   
  29.     public static void main(String[] args) throws Exception {  
  30.         CatalogLoader loader = new CatalogLoader();  
  31.         Catalog sampleCatalog = loader.getCatalog();  
  32.         Command command = sampleCatalog.getCommand(“sell-vehicle”);  
  33.         Context ctx = new SellVehicleContext();  
  34.         command.execute(ctx);  
  35.     }  
  36. }  

Chain 使用Commons Digester来读取和解析配置文件。因此你需要将Commons Digester.jar加入classpath中。我使用了1.6版本并且工作得很好。Digester使用了Commons Collectios(我使用的版本是3.1),Commons Logging(版本1.0.4),Commons BeanUtils(1.7.0),因此你也需要将它们的jar文件加入classpath中。在加入这些jar后,CatalogLoader就可以被编译和运行,它的输出和另外两个测试完全相同。 

现在你可以在XML文件中定义链,并可以在程序中得到这个链(别忘了链也是命令),这样扩展的可能性和程序的灵活性可以说是无限的。假设过程“安排财务”实际上由一个完全分离的商业部门处理。这个部门希望为这种销售建立自己的工作流程。 Chain提供了嵌套链来实现这个要求。因为链本身就是命令,因此你可以用指向另一个链的引用替换一个单一用途的命令。下面是增加了新流程的链的定义: 

Xml代码  收藏代码

  1. <catalog name=“auto-sales”>  
  2.   <chain name=“sell-vehicle”>  
  3.     <command id=“GetCustomerInfo” className=“com.jadecove.chain.sample.GetCustomerInfo”/>  
  4.     <command id=“TestDriveVehicle” className=“com.jadecove.chain.sample.TestDriveVehicle”/>  
  5.     <command id=“NegotiateSale” className=“com.jadecove.chain.sample.NegotiateSale”/>  
  6.     <command className=“org.apache.commons.chain.generic.LookupCommand”  catalogName=“auto-sales” name=“arrange-financing” optional=“true”/>  
  7.     <command id=“CloseSale” className=“com.jadecove.chain.sample.CloseSale”/>  
  8.   </chain>  
  9.   <chain name=“arrange-financing”>  
  10.     <command id=“ArrangeFinancing” className=“com.jadecove.chain.sample.ArrangeFinancing”/>  
  11.   </chain>  
  12. </catalog>  

Commons Chain提供了一个常用的命令LookupCommand来查找和执行另一个链。属性optional用于控制当指定的嵌套链没有找到时如何处理。 optional=true时,即使链没找到,处理也会继续。反之,LookupCommand将抛出 IllegalArgumentException,告知指定的命令未找到。 

在下面三种情况下,命令链将结束: 

  1. 命令的execute方法返回true
  2. 运行到了链的尽头
  3. 命令抛出异常

当链完全处理完一个过程后,命令就返回true。这是责任链模式(Chain of Responsibility)的基本概念。处理从一个命令传递到另一个命令,直到某个命令(Command)处理了这个命令。如果在到达命令序列尽头时仍没有处理返回true,也假设链已经正常结束。 

当有命令抛出错误时链就会非正常结束。在Commons Chain中,如果有命令抛出错误,链的执行就会中断。不论是运行时错误(runtime exception)还是应用错误(application exception),都会抛出给链的调用者。但是许多应用都需要对在命令之外定义的错误做明确的处理。Commons Chain提供了Filter接口来满足这个要求。Filter继承了Command,添加了一个名为postprocess的方法。 

public boolean postprocess(Context context, Exception exception); 
只要Filter的execute方法被调用,不论链的执行过程中是否抛出错误,Commons Chain都将保证Filter的postprocess方法被调用。和servlet的过滤器(filter)相同,Commons Chain的Filter按它们在链中的顺序依次执行。同样,Filter的postprocess方法按倒序执行。你可以使用这个特性实现自己的错误处理。下面是一个用于处理我们例子中的错误的Filter: 

Java代码  收藏代码

  1. package com.jadecove.chain.sample;  
  2.   
  3. import org.apache.commons.chain.Context;  
  4. import org.apache.commons.chain.Filter;  
  5.   
  6. public class SellVehicleExceptionHandler implements Filter {  
  7.   
  8.     public boolean execute(Context context) throws Exception {  
  9.         System.out.println(“Filter.execute() called.”);  
  10.         return false;  
  11.     }  
  12.   
  13.     public boolean postprocess(Context context, Exception exception) {  
  14.         if (exception == null)  
  15.             return false;  
  16.         System.out.println(“Exception “ + exception.getMessage() + ” occurred.”);  
  17.         return true;  
  18.     }  
  19.   
  20. }  

Filter在配置文件中的定义就和普通的命令(Command)定义相同: 

Xml代码  收藏代码

  1. <chain name=“sell-vehicle”>  
  2.   <command id=“ExceptionHandler” className = “com.jadecove.chain.sample.SellVehicleExceptionHandler”/>  
  3.   <command id=“GetCustomerInfo” className=“com.jadecove.chain.sample.GetCustomerInfo”/>  

Filter 的execute方法按定义的序列调用。然而,它的postprocess方法将在链执行完毕或抛出错误后执行。当一个错误被抛出时, postprocess方法处理完后会返回true,表示错误处理已经完成。链的执行并不会就此结束,但是本质上来说这个错误被捕捉而且不会再向外抛出。如果postprocess方法返回false,那错误会继续向外抛出,然后链就会非正常结束。 

让我们假设ArrangeFinancing因为用户信用卡损坏抛出错误。SellVehicleExceptionHandler就能捕捉到这个错误,程序输出如下: 
Filter.execute() called. 
Get customer info 
Test drive the vehicle 
Negotiate sale 
Exception Bad credit occurred.
 

结合了过滤器(filter)和子链技术后,你就可以造型很复杂的工作流程。 

转自:http://phil-xzh.iteye.com/blog/321536

使用Apache Commons Configuration中XMLConfiguration读写xml配置文件

 apache, java  使用Apache Commons Configuration中XMLConfiguration读写xml配置文件已关闭评论
3月 032014
 

XMLConfiguration是apache工具集里的一个用于对XML文件进行读写的东东,使用起来很方便。下面简单的举几个使用例子。

1,读

这里是我们的XML文件:unit.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<tree>
<unit id=”root” type=”root”>
<back-ground>/root/Desktop/image/4.jpg</back-ground>
<unit id=”1″ type=”alarm”>
<back-ground>/root/Desktop/image/5.jpg</back-ground>
<father>root</father>
<position>x=250:y=225</position>
</unit>

</unit>
</tree>

下面是java代码:

//创建XMLConfiguration

private static XMLConfiguration getConfiguration(File file) {

if (file == null || !file.exists())

return null;

XMLConfiguration config = null;

try {

config = new XMLConfiguration(file);

} catch (ConfigurationException e) {

e.printStackTrace();

}

config.setEncoding(“utf-8”);//设置编码

return config;

}

………

XMLConfiguration tool = getConfiguration(“/home/dx/unit.xml”);

//获取节点值

String rootBackGround = tool.getString(“unit.back-ground”);//这里获取的值是/root/Desktop/image/4.jpg

String alarmBackGround = tool.getString(“unit.unit.back-ground”);//这里获取的值是/root/Desktop/image/5.jpg

//获取节点属性

String rootID = tool.getString(“unit[@id]”);//这里获取的值是root

String alarmID = tool.getString(“unit.unit[@id]”);//这里获取的值是1

//获取重复节点值

如果有两个以上并级的节点如何区分呢?比如在第一级unit下有两个并列的unit:

………………………………..

<unit id=”root” type=”root”>
<back-ground>/root/Desktop/image/4.jpg</back-ground>
<unit id=”1″ type=”alarm”>
<back-ground>/root/Desktop/image/5.jpg</back-ground>
<father>root</father>
<position>x=250:y=225</position>
</unit>
<unit id=”2″ type=”alarm”>
<back-ground>/root/Desktop/image/6.jpg</back-ground>
<father>root</father>
<position>x=300:y=270</position>
</unit>

</unit>

………………………….

String firstID = tool.getString(“unit.unit(0)[@id]”);//这里获取的值是1

String secondID = tool.getString(“unit.unit(1)[@id]”);//这里获取的值是2 

写操作基本基本更读的代码差不多,只是这里我们用addProperty或setProperty
config.setProperty(“unit.unit(0)[@id]”, “0”);
config.setProperty(“unit.unit(1)[@id]”, “1”);
当然还是更读一样 要首先有个xml文件(空的也可以 但必须有根节点,其他节点会自动生成,当然也可以修改指定节点,方法还是一样的)。
这里要注意 XMLConfiguration默认情况下以’,’为分割符如果一个节点值里出现逗号 他会把他分开成两个节点例如:
config.setProperty(“unit.unit”, “a,b”);
我们想要的结果是
………………………….
<unit>
    a,b
</unit>
…………………
可结果会变成
………………………….
<unit>
    a
</unit>
<unit>
    b
</unit>
…………………
如果想要使用逗号 可以设置XMLConfiguration的delimiter例如
XMLConfiguration.setDelimiter(‘ ‘);//设置空格为分割符.
别望了写完save一下.
config.save(); 

解决自动重启tomcat及重启后乱码问题

 apache, linux  解决自动重启tomcat及重启后乱码问题已关闭评论
1月 262014
 

  Tomcat作为开源的服务器,性能与稳定性上比起一些商业服务器还是要差一些。将项目部署到Tomcat后隔段时间就会死掉,出现这种问题的主要原因可 能是内存数据溢出,因为Tomcat容器在发生大量与数据库交互查询或者操作的时候很容易出现问题。定时重启是一个很好的补救措施,定时重启可以定时的清 理内存也降低了Tomcat运行的风险,从而保持tomcat的最佳性能。

定时执行的命令是crontab,编辑/var/spool/cron/下的root文件(路径根据系统的不同可能不同,没有root文件就新建),加入以下两行:
1 6 * * * /usr/local/apache-tomcat-6.0.20/bin/shutdown.sh
2 6 * * * /usr/local/apache-tomcat-6.0.20/bin/startup.sh
其中开头的1 6 * * *表示在每天的早上6点1分执行,详细介绍请百度之。
这样在关闭之后1分种再开启tomcat。
你可能要问,可不可以都在6点1分执行,即都写成1 6 * * *? 答案是不行,虽然开启tomcat的命令写在关闭的下面,但同一时间的命令并不是按写的先后顺序执行的,有可能先执行了开启的命令,后执行关闭的命令,这样今天tomcat就用不了了。
当然你可以把这两行命令(去掉前面的1 6 * * *)写在一个restartTomcat.sh文件里,中间加一行sleep 10s,然后在root文件里加一句1 6 * * * restartTomcat.sh。如此就可以每天早上6点1分自动重启tomcat了。

自动重启的问题解决了,但又带来了一个新的问题:网站运行良好,但有些地方显示的汉字是乱码,手工重启tomcat后就没有乱码了。原因是手工重启与默认系统任务自动重启的字符集是不一样的。解决方法是编辑/usr/local/apache-tomcat-6.0.20/bin/catalina.sh,在里面加一行:
CATALINA_OPTS=-Dfile.encoding=UTF8
就OK了(具体编码根据页面编码修改)。

 

 

以上是网上找的一篇文章

我在自动重启resin中也遇到了此问题,修改bin/httpd.sh文件,加上如下代码:

LANG=”en_US.UTF-8″

export LANG

扩展资料:Linux中LANG,LC_ALL,local详解

locale 是国际化与本土化过程中的一个非常重要的概念,个人认为,对于中文用户来说,通常会涉及到的国际化或者本土化,大致包含三个方面:看中文,写中文,与 window中文系统的兼容和通信。从实际经验上看来,locale的设定与看中文关系不大,但是与写中文,及window分区的挂载方式有很密切的关系。本人认为就像一个纯英文的Windows能够浏览中文,日文或者意大利文网页一样,你不需要设定locale就可以看中文。那么,为什么要设定 locale呢?什么时候会用到locale呢? 
Tags: locale 设定 原因 解释 
一、为什么要设定locale 正如前面我所讲的,设定locale与你能否浏览中文的网页没有直接的关系,即便你把locale设置成 en_US.ISO-8859-1这样一个标准的英文locale你照样可以浏览中文的网页,只要你的系统里面有相应的字符集(这个都不一定需要)和合适的字体(如simsun),浏览器就可以把网页翻译成中文给你看。具体的过程是网络把网页传送到你的机器上之后,浏览器会判断相应的编码的字符集,根据网页采用的字符集,去字体库里面找合适的字体,然后由文字渲染工具把相应的文字在屏幕上显示出来。 
在下文本人会偶尔把字符集比喻成密码本,个人觉得对于一些东西比较容易理解,假如你不习惯的话,把全文copy到任何文本编辑器,用字符集替换密码本即可。 
那有时候网页显示乱码或者都是方框是怎么回事呢?个人认为,显示乱码是因为设定的字符集不对(或者没有相应的字符集),例如网页是用UTF-8编码的,你非要用GB2312去看,而系统根据GB2312去找字体,然后在屏幕上显示,当然是一堆的乱码,也就是说你用一个错误的密码本去翻译发给你的电报,当然内容那叫一个乱;至于有些时候浏览的网页能显示一部分汉字,但有很多的地方是方框,能够显示汉字说明浏览器已经正确的判断出了网页的编码,并在字体库里面找到了相应的文字,但是并不是每个字体库都包含某个字符集全部的字体的缘故,有些时候会显示不完全,找一个比较全的支持较多字符集的字体就可以了。 
既然我能够浏览中文网页,那为什么我还要设定locale呢? 
其实你有没有想过这么一个问题,为什么gentoo官方论坛上中文论坛的网页是用UTF-8编码的(虽然大家一直强烈建议用GB2312编码),但是新浪网就是用GB2312编码的呢?而Xorg的官方网页竟然是ISO-8859-15编码的,我没有设定这个locale怎么一样的能浏览呢?这个问题就像是你有所有的密码本,不论某个网站是用什么字符集编码的,你都可以用你手里的密码本把他们翻译过来,但问题是虽然你能浏览中文网页,但是在整个操作系统里面流动的还是英文字符。所以,就像你能听懂英语,也能听懂中文。 最根本的问题是:你不可以写中文。 
当你决定要写什么东西的时候,首先要决定的一件事情是用那种语言,对于计算机来说就是你要是用哪一种字符集,你就必须告诉你的linux系统,你想用那一本密码本去写你想要写的东西。知道为什么需要用GB2312字符集去浏览新浪了吧,因为新浪的网页是用GB2312写的。 
为了让你的Linux能够输入中文,就需要把系统的locale设定成中文的(严格说来是locale中的语言类别LC_CTYPE ),例如 zh_CN.GB2312、zh_CN.GB18030或者zh_CN.UTF-8。很多人都不明白这些古里古怪的表达方式。这个外星表达式规定了什么东西呢?这个问题稍后详述,现在只需要知道,这是locale的表达方式就可以了。 
二、到底什么是locale? locale这个单词中文翻译成地区或者地域,其实这个单词包含的意义要宽泛很多。Locale是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境。 
这个用户环境可以按照所涉及到的文化传统的各个方面分成几个大类,通常包括用户所使用的语言符号及其分类(LC_CTYPE),数字 (LC_NUMERIC),比较和排序习惯(LC_COLLATE),时间显示格式(LC_TIME),货币单位(LC_MONETARY),信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES),姓名书写方式(LC_NAME),地址书写方式 (LC_ADDRESS),电话号码书写方式 (LC_TELEPHONE),度量衡表达方式(LC_MEASUREMENT),默认纸张尺寸大小 (LC_PAPER)和locale对自身包含信息的概述(LC_IDENTIFICATION)。 
所以说,locale就是某一个地域内的人们的语言习惯和文化传统和生活习惯。一个地区的locale就是根据这几大类的习惯定义的,这些 locale定义文件放在/usr/share/i18n/locales目录下面,例如en_US, zh_CN and [email protected]都是 locale的定义文件,这些文件都是用文本格式书写的,你可以用写字板打开,看看里边的内容,当然出了有限的注释以外,大部分东西可能你都看不懂,因为是用的Unicode的字符索引方式。 
对于[email protected]的一点说明,@后边是修正项,也就是说你可以看到两个德国的locale: /usr/share/i18n/locales/[email protected] /usr/share/i18n/locales/de_DE 打开这两个 locale定义,你就会知道它们的差别在于[email protected]使用的是欧洲的排序、比较和缩进习惯,而de_DE用的是德国的标准习惯。 
上面我们说到了zh_CN.GB18030的前半部分,后半部分是什么呢?大部分Linux用户都知道是系统采用的字符集。 
三、什么是字符集? 字符集就是字符,尤其是非英语字符在系统内的编码方式,也就是通常所说的内码,所有的字符集都放在 /usr/share/i18n/charmaps,所有的字符集也都是用Unicode编号索引的。Unicode用统一的编号来索引目前已知的全部的符号。而字符集则是这些符号的编码方式,或者说是在网络传输,计算机内部通信的时候,对于不同字符的表达方式,Unicode是一个静态的概念,字符集是一个动态的概念,是每一个字符传递或传输的具体形式。就像 Unicode编号U59D0是代表姐姐的“姐”字,但是具体的这个字是用两个字节表示,三个字节,还是四个字节表示,是字符集的问题。例如:UTF-8 字符集就是目前流行的对字符的编码方式,UTF-8用一个字节表示常用的拉丁字母,用两个字节表示常用的符号,包括常用的中文字符,用三个表示不常用的字符,用四个字节表示其他的古灵精怪的字符。而GB2312字符集就是用两个字节表示所有的字符。需要提到一点的是Unicode除了用编号索引全部字符以外,本身是用四个字节存储全部字符,这一点在谈到挂载windows分区的时候是非常重要的一个概念。所以说你也可以把Unicode看作是一种字符集(我不知道它和UTF-32的关系,反正UTF-32就是用四个字节表示所有的字符的),但是这样表述符号是非常浪费资源的,因为在计算机世界绝大部分时候用到的是一个字节就可以搞定的 26个字母而已。所以才会有UTF-8,UTF-16等等,要不然大同世界多好,省了这许多麻烦。 
四、zh_CN.GB2312到底是在说什么? Locale 是软件在运行时的语言环境, 它包括语言(Language), 地域 (Territory) 和字符集(Codeset)。一个locale的书写格式为: 语言[_地域[.字符集]]. 所以说呢,locale总是和一定的字符集相联系的。下面举几个例子: 
1、我说中文,身处中华人民共和国,使用国标2312字符集来表达字符。 zh_CN.GB2312=中文_中华人民共和国+国标2312字符集。 
2、我说中文,身处中华人民共和国,使用国标18030字符集来表达字符。 zh_CN.GB18030=中文_中华人民共和国+国标18030字符集。 
3、我说中文,身处中华人民共和国台湾省,使用国标Big5字符集来表达字符。 zh_TW.BIG5=中文_台湾.大五码字符集 
4、我说英文,身处大不列颠,使用ISO-8859-1字符集来表达字符。 en_GB.ISO-8859-1=英文_大不列颠.ISO-8859-1字符集 
5、我说德语,身处德国,使用UTF-8字符集,习惯了欧洲风格。 
[email protected]=德语_德国.UTF-8字符集@按照欧洲习惯加以修正 
注意不是
[email protected]-8,所以完全的locale表达方式是 [语言[_地域][.字符集] [@修正值] 
生成的locale放在/usr/lib/locale/目录中,并且每个locale都对应一个文件夹,也就是说创建了
[email protected]-8 locale之后,就生成/usr/lib/locale/[email protected]/目录,里面是具体的每个locale的内容。 
五、怎样去自定义locale 在gentoo生成locale还是很容易的,首先要在USE里面加入userlocales支持,然后编辑 locales.build文件,这个文件用来指示glibc生成locale文件。 很多人不明白每一个条目是什么意思。其实根据上面的说明现在应该很明确了。 
File: /etc/locales.build en_US/ISO-8859-1 en_US.UTF-8/UTF-8 
zh_CN/GB18030 zh_CN.GBK/GBK zh_CN.GB2312/GB2312 zh_CN.UTF-8/UTF-8 
上面是我的locales.build文件,依次的说明是这样的: 
en_US/ISO-8859-1:生成名为en_US的locale,采用ISO-8859-1字符集,并且把这个locale作为英文_美国locale类的默认值,其实它和en_US.ISO-8859-1/ISO-8859-1没有任何区别。 
en_US.UTF-8/UTF-8:生成名为en_US.UTF-8的locale,采用UTF-8字符集。 
zh_CN/GB18030:生成名为zh_CN的locale,采用GB18030字符集,并且把这个locale作为中文_中国locale类的默认值,其实它和zh_CN.GB18030/GB18030没有任何区别。 
zh_CN.GBK/GBK:生成名为zh_CN.GBK的locale,采用GBK字符集。 zh_CN.GB2312/GB2312:生成名为zh_CN.GB2312的locale,采用GB2312字符集。 zh_CN.UTF-8/UTF-8:生成名为zh_CN.UTF-8的 locale,采用UTF-8字符集。 
关于默认locale,默认locale可以简写成en_US或者zh_CN的形式,只是为了表达简单而已没有特别的意义。 
Gentoo在locale定义的时候掩盖了一些东西,也就是locale的生成工具:localedef。在编译完glibc之后你可以用这个localedef 再补充一些locale,就会更加理解locale了。具体的可以看 localedef 的manpage。 
$localedef -f 字符集 -i locale定义文件 生成的locale的名称 例如 $localedef -f UTF-8 -i zh_CN zh_CN.UTF-8 
上面的定义方法和在locales.build中设定zh_CN.UTF-8/UTF-8的结果是一样一样的。 
六、locale的五脏六腑 
刚刚生成了几个locale,但是为了让它们生效,必须告诉Linux系统使用那(几)个locale。这就需要对locale的内部机制有一点点的了解。在前面我已经提到过,locale把按照所涉及到的文化传统的各个方面分成12个大类,这12个大类分别是: 1、语言符号及其分类 (LC_CTYPE) 2、数字(LC_NUMERIC) 3、比较和排序习惯(LC_COLLATE) 4、时间显示格式(LC_TIME) 5、货币单位(LC_MONETARY) 6、信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES) 7、姓名书写方式(LC_NAME) 8、地址书写方式(LC_ADDRESS) 9、电话号码书写方式(LC_TELEPHONE) 10、度量衡表达方式 (LC_MEASUREMENT) 11、默认纸张尺寸大小(LC_PAPER) 12、对locale自身包含信息的概述 (LC_IDENTIFICATION)。 
其中,与中文输入关系最密切的就是 LC_CTYPE, LC_CTYPE 规定了系统内有效的字符以及这些字符的分类,诸如什么是大写字母,小写字母,大小写转换,标点符号、可打印字符和其他的字符属性等方面。而locale定义zh_CN中最最重要的一项就是定义了汉字(Class “hanzi”)这一个大类,当然也是用Unicode描述的,这就让中文字符在Linux系统中成为合法的有效字符,而且不论它们是用什么字符集编码的。 
LC_CTYPE % This is a copy of the “i18n” LC_CTYPE with the following modifications: – Additional classes: hanzi 
copy “i18n” 
class “hanzi”; / % ..;/ ..;/ ;;;;;;;;/ ;;;;;;;;/ ;;;; END LC_CTYPE 
在en_US的locale定义中,并没有定义汉字,所以汉字不是有效字符。所以如果要输入中文必须使用支持中文的locale,也就是zh_XX,如zh_CN,zh_TW,zh_HK等等。 
另外非常重要的一点就是这些分类是彼此独立的,也就是说LC_CTYPE,LC_COLLATE和 LC_MESSAGES等等分类彼此之间是独立的,可以根据用户的需要设定成不同的值。这一点对很多用户是有利的,甚至是必须的。例如,我就需要一个能够输入中文的英文环境,所以我可以把 LC_CTYPE设定成zh_CN.GB18030,而其他所有的项都是en_US.UTF-8。 
七、怎样设定locale呢? 
设定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了简便起见,还有两个变量: LC_ALL和LANG。它们之间有一个优先级的关系: LC_ALL>LC_*>LANG 可以这么说,LC_ALL是最上级设定或者强制设定,而LANG是默认设定值。 1、如果你设定了LC_ALL=zh_CN.UTF-8,那么不管LC_*和LANG设定成什么值,它们都会被强制服从 LC_ALL的设定,成为 zh_CN.UTF-8。 2、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_*=en_US.UTF-8,并且没有设定LC_ALL的话,那么系统的locale 设定以LC_*=en_US.UTF-8。 3、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_*,和LC_ALL均未设定的话,系统会将LC_*设定成默认值,也就是LANG的值 zh_CN.UTF-8 。 4、假如你设定了 LANG=zh_CN.UTF-8,而其他的LC_CTYPE=en_US.UTF-8,其他的LC_*,和LC_ALL均未设定的话,那么系统的 locale设定将是:LC_CTYPE=en_US.UTF-8,其余的 LC_COLLATE,LC_MESSAGES等等均会采用默认值,也就是 LANG的值,也就是LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=zh_CN.UTF-8。 
所以,locale是这样设定的: 1、如果你需要一个纯中文的系统的话,设定LC_ALL= zh_CN.XXXX,或者LANG= zh_CN.XXXX都可以,当然你可以两个都设定,但正如上面所讲,LC_ALL的值将覆盖所有其他的locale设定,不要作无用功。 2、如果你只想要一个可以输入中文的环境,而保持菜单、标题,系统信息等等为英文界面,那么只需要设定 LC_CTYPE=zh_CN.XXXX,LANG= en_US.XXXX就可以了。这样LC_CTYPE=zh_CN.XXXX,而LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=en_US.XXXX。 3、假如你高兴的话,可以把12个LC_*一一设定成你需要的值,打造一个古灵精怪的系统: LC_CTYPE=zh_CN.GBK/GBK(使用中文编码内码GBK字符集); LC_NUMERIC=en_GB.ISO-8859-1(使用大不列颠的数字系统) [email protected](德国的度量衡使用ISO-8859-15字符集) 罗马的地址书写方式,美国的纸张设定……。估计没人这么干吧。 4、假如你什么也不做的话,也就是LC_ALL,LANG和LC_*均不指定特定值的话,系统将采用POSIX作为lcoale,也就是C locale。

Apache Commons Digester 使用详解

 apache  Apache Commons Digester 使用详解已关闭评论
1月 242014
 

很好的文章,看后受益匪浅,分享下:http://chen-sien-163-com.iteye.com/blog/1568649

使用Apache Commons Digester
本文主要参考了The serverside上的一篇文章。描述了2种典型的使用digester的方法,以及所需要注意的地方。详细内容请参见正文。
Apache Commons Digester
简化xml文件处理,它依赖的组件:BeanUtils、Logging、Collections。
基本概念
1. 在Digester的内部采用SAX来解析XML文件。为了能够正确的使用它,必须在解析之前进行相应的设置。同时,在解析xml文件的过程中,它使用Stack来保存和检索这个期间产生的对象。
2. 为了简化使用,它通过匹配模式来定位要解析的xml标签。匹配模式的例子如下:
xml文件:
Java BoyJSPJava GirlEJB每个标签与相应的匹配模式对应如下表:
标签
匹配模式studentsstudents/studentstudents/student/namestudents/student/course
如果将xml文件结构视为一颗树的话,那么每个标签的匹配模式就是从根元素到这个元素的路径。除了使用具体的标签,还可以使用通配符。
3. 使用匹配模式可以很方便的定位需要处理的元素,为了处理这些元素,需要定义处理规则。规则在匹配模式被找到时起作用。所有的规则都是从org.apache.commons.digester.Rule派生的。所有已定义的Rule对象,可以在org.apache.commons.digester中找到。常用的规则:
– ObjectCreate,创建对象实例。
– SetProperties,将标签属性(Attribute)与要创建的对象的属性相关联。
– BeanPropertySetter,将标签所包含标签与要创建的对象的属性相关联。
– SetNext,设置遇到下一个标签时的动作。
– CallMethod,设置当匹配模式被找到时要调用的方法。
– CallParam,设置对应的callMethod中指定方法所需要的参数值。
基本使用
以正确的顺序调用Digester方法是成功使用Digester处理XML文件的关键。使用步骤如下:
1. 创建org.apache.commons.digester.Digester实例并配置,包括设置实现Digester Rule的对象。
2. 使用Digester的push方法在Digester使用的stack中放置一个初始对象。在解析xml文件的过程中,Digester使用stack来保存它所找到的对象。第一个对象在遇到第一个标签时被放置到stack中,当最后一个标签处理完毕时被弹出。为了最后能检索到这个对象,因此需要一个初始对象来保留一个指向它的引用。
3. 注册匹配模式和rule。
4. 调用parse来解析xml文件。
使用举例
1. 简单例子,从一个xml文件中创建对象。
XML文件:
Java BoyJSPJava GirlEJB要创建的对象:
public class Student {
private String name;
private String course;
……
}
解析:
public class DigesterStudy {
private Vector students;
public DigesterStudy(){
students= new Vector( 5);
}
public static void main(String[] args) {
DigesterStudy ds= new DigesterStudy();
ds.digest();
}
public void digest(){
//创建实例
Digester digester= new Digester();
//将初始对象压入digester的stack
digester.push( this);
//指明匹配模式和要创建的类
digester.addObjectCreate( “students/student”, Student.class);
//设置对象属性
digester.addBeanPropertySetter( “students/student/name”);
digester.addBeanPropertySetter( “students/student/course”);
//当移动到下一个标签中时的动作
digester.addSetNext( “students/student”, “addStudent”);
try {
//解析
DigesterStudy ds= (DigesterStudy)digester.parse( getClass()
.getClassLoader()
.getResourceAsStream( “students.xml”));
System.out.print( ds);
} catch (Exception e) {
e.printStackTrace();
}
}
public void addStudent( Student student){
students.add( student);
}
public String toString(){
return students.toString();
}
}
2. 复杂例子,从xml文件中创建相互之间有关系的对象。
XML文件:
C1JSPC2ServletsC3EJBSCJPSCWCDOCPSCJPSCEA要创建的对象:
public class Academy {
private String name;
private Vector students;
private Vector teachers;
……
}
public class Student {
private String name;
private String division;
private Vector courses;
……
}
public class Course {
private String id;
private String name;
……
}
public class Teacher {
private String name;
private Vector certifications;
……
}
其中的在程序中对应一个字符串,故而没有列出。
解析:
public void digest(){
Digester digester= new Digester();
//注意,此处并没有象上例一样使用push,是因为此处从根元素创建了一个对//象实例
digester.addObjectCreate( “academy”, Academy.class);
//将< academy >的属性与对象的属性关联
digester.addSetProperties( “academy”);
digester.addObjectCreate( “academy/student”, Student.class);
digester.addSetProperties( “academy/student”);
digester.addObjectCreate( “academy/student/course”, Course.class);
digester.addBeanPropertySetter( “academy/student/course/id”);
digester.addBeanPropertySetter( “academy/student/course/name”);
digester.addSetNext( “academy/student/course”, “addCourse”);
digester.addSetNext( “academy/student”, “addStudent”);
digester.addObjectCreate( “academy/teacher”, Teacher.class);
digester.addSetProperties( “academy/teacher”);
//当遇到academy/teacher/certification时,调用addCertification
digester.addCallMethod( “academy/teacher/certification”,
“addCertification”, 1);
//设置addCertification的参数值,此处的0表示这个元素体的第一个值
//为参数值传入addCertification。在此处,即为的值。
//(因为就只有一个)
digester.addCallParam( “academy/teacher/certification”, 0);
digester.addSetNext( “academy/teacher”, “addTeacher”);
try {
Academy a= (Academy)digester.parse( getClass()
.getClassLoader()
.getResourceAsStream( “students.xml”));
System.out.print( a);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
3. 从配置文件中创建Digester,通过DigesterLoader可以从配置文件中创建Digester实例,从而省去类似于上例手工配置Digester的代码。
Digester的配置文件,对应上例的配置文件如下:
使用配置文件之后,上例的代码变为:
Digester digester = DigesterLoader.createDigester(
this.getClass().getClassLoader().getResource(“academyRules.xml”));
Academy a= (Academy)digester.parse( getClass()
.getClassLoader().getResourceAsStream( “students.xml”));
注意
在使用Digester需要注意的地方:
– 调用顺序,正确的调用顺序才能得出正确的结果。方法调用顺序基本和标签在xml文件中的层次关系相对应。基本的顺序是:先创建对象;然后设置属性;随后处理子元素;最后设置遇到下一个元素所对应的动作。对于子元素的处理,是同样的过程。
– 正确的使用初始对象。对比上面2个例子,之所以在第一个例子中显示的调用了digester的push方法,其原因就在于我们并没有如第二个例子一样用xml的root元素创建一个实例。如果不显式的调用,我们将会丢失这个元素的引用,那么也就无法得到后续的对象。
– digester的addSetNex方法中所指定的方法实际上是包含匹配模式对应标签的父标签对应对象的方法。在上两个例子中addStudent,都是包含Student对象的那个对象的方法。对于第一个例子,是DigesterStudy;对于第二个例子,是Academy。而且它的位置通常是在创建对象语句组的最后,与addObjectCreate相对应。在这2个语句之间的代码中所指定的方法都是所创建对象的方法。而且它们的顺序与匹配模式所对应的标签的顺序必须是一致的。
– 使用配置文件来创建digester,这样会带来很大的灵活性。

apache 和 Tomcat 安全性配置

 apache, tomcat  apache 和 Tomcat 安全性配置已关闭评论
6月 182013
 

转:http://www.cnblogs.com/jonethan/archive/2012/05/15/2500923.html

    Apache 和 Tomcat是我们平时使用比较多的两个Web服务器,本文收集一些关于这两个服务器的安全性配置的方法和技巧。另外安全是相对的,需要服务器安全、数据库安全、应用程序安全互相配合。仅从服务器配置上只能在某些方面提高系统的安全性。

Apache安全配置

1、 隐藏或伪装apache版本号

    打乱攻击者的步骤,给攻击者带来麻烦。一般软件的漏洞信息是和版本相关的,在攻击者收集你服务器软件信息时给予迷惑信息是个不错的选择。

默认情况下,系统会把apache的版本模块都显示出来(http返回的头),如下图

 

    添加上如下两行,再次看http返回的信息,已经不包含apache的版本好了。

 

    还有另外一种方法是,修改apache的源码,把版本号改为一个错误的,这个需要重新编译apache,不考虑了。

2、 禁止使用目录索引

    对于可以访问的web目录,要使用相对保守的途径进行访问,不要让用户查看任何目录索引列表,修改conf/httpd.conf

 

3、 配置web目录的访问策略

    结合业务,如果apache只作为文件访问服务器,可以配置只允许客户端访问那些类型的资源,比如如下的配置。

复制代码
<Directory "D:/ftp/tk/media">

<!-- 只允许flv或jpg的文件访问权限 -->

    <FilesMatch ".flv$">

        Allow from all

    </FilesMatch>

    <FilesMatch ".jpg$">

        Allow from all

    </FilesMatch>

    Options -Indexes FollowSymLinks

    AllowOverride None

    Order allow,deny

    deny from all

</Directory>
复制代码

 

4、 Apache服务器的访问控制

    apache的access.conf文件负责设置文件的访问权限,可以实现互联网域名和ip地址的访问控制

如允许192.168.1.1到192.168.1.254的主机访问,可以这样设定

order deny,allow

deny from all

allow from pair 192.168.1.0/255.255.255.0

5、 Apache 防Dos

    apache服务经常会碰到Dos攻击,防范的主要手段是通过软件,apahce Dos Evasive Maneuvers Module

来实现的,它是一款mod_access的代替软件,可以对抗DoS攻击,该软件可以快速拒绝来自相同地址对同一URL的重复请求,通过查询内部一张各子进程的哈希表来实现

可以到网址:http://online/.securityfocus.com/tools/ 上下载软件

6、 建立安全的目录结构

apache服务器包括四个目录结构

ServerRoot #保存配置文件,二进制文件与其他服务器配置文件

DocumentRoot #保存web站点内容,包括HTML文件和图片等

ScripAlias #保存CGI脚本

Customlog 和 Errorlog #保存日志和错误日志

建议的目录结构为,以上四种目录相互独立并且不存在父子逻辑关系。

ServerRoot目录只能为root用户访问

DocumentRoot目录应该能够被管理web站点内容的用户访问和使用apache服务器的apache用户与组访问

ScripAlias目录应该只能被CGI开发人员和apache用户访问

Customlog 和 Errorlog只能被root访问

下边是一个安全目录结构的事例

D:/

|+/apache2.2(ServerRoot)

|+/logs(Customlog和Errorlog)

|+/cgi-bin(ScripAlias)

|+/html(DocumentRoot)

这样的目录结构是比较安全的,因为目录之间独立,某个目录权限错误不会影响到其他目录

7、 勤打补丁

     针对于一些缓存区溢出等漏洞,通过这种手段可以有效防御,及时修复已知漏洞可以很好的加强服务器安全。

Tomcat安全配置

1、  使用非管理员账户运行tomcat,配置有限的权限,限定在特定的目录下

    默认的安装tomcat是以Administrator运行的,拥有很高的权限,这样可能会导致一些安全隐患。当用户上传了恶意的jsp可以随意的删除服务器上的文件和目录,执行某些恶意代码或木马程序。所以使用受限账户启动tomcat可以很好的将权限控制在某些范围内。

2、 更改关闭服务器端口

    在conf/server.xml中默认有下面一行:

<Server port="8005" shutdown="SHUTDOWN">

这样允许任何人只要telnet到服务器的8005端口,输入”SHUTDOWN”,然后回车,服务器立即就被关掉了。

从安全的角度上考虑,我们需要把这个shutdown指令改成一个别人不容易猜测的字符串,可以同时把端口也改了。

例如修改如下:

<Server port="8005" shutdown="tiyuan123">

这样就只有在telnet到8005,并且输入”c1gstudio”才能够关闭Tomcat.

注意:这个修改不影响shutdown.bat的执行。运行shutdown.bat一样可以关闭服务器。

3、 删除掉tomcat默认的其他应用

    Tomcat默认安装时,会有一些默认的管理应用,如manager和host-manager等,而且他们默认的登录密码都是空,所以在实际部署时需要更改密码,或是删除掉这些应用。

4、 设置默认的错误页面

    网站中常见的404、500等错误在浏览器中会显示服务器的详细信息,很容易暴露一些敏感的信息,所以在实际部署中需要替换成更外的一些页面,当出现错误时,转向一个体验良好的界面。

打开conf/web.xml文件,在文件的最后添加如下类似的代码

 

同时在根目录下创建404.jsp和500.jsp文件

5、 屏蔽目录文件自动列出的方法

    和apache的原理类似,设置tomcat不显示目录列表,在tomcat的conf/web.xml中,进行以下配置

 

6、 关闭8009端口

    8009端口是tomcat和apache的mod_proxy_ajp,mod_jk沟通的端口,没有用到就关了。

在<tomcatpath>/conf/server.xml 中的这段注释掉

<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->

 

———————————————————————–

参考文章:

Apache安全配置(基于linux)

http://www.blogjava.net/bukebushuo/articles/293776.html

apache模块详解

http://www.djx1981.com/archives/224.html

tomcat安全配置

http://blog.c1gstudio.com/archives/865

http://www.cnblogs.com/dreamof/archive/2010/09/04/1818032.html

http://www.diybl.com/course/4_webprogram/jsp/jsp_js/2008624/127872.html

windows平台下的tomcat安全设置

http://dazhilao.iteye.com/blog/482562

server2003下的tomcat安全

http://www.myhack58.com/Article/60/61/2011/30064.htm

 Posted by at 上午11:33  Tagged with:

HttpClient 教程

 apache  HttpClient 教程已关闭评论
5月 272013
 

转自:http://www.cnblogs.com/loveyakamoz/archive/2011/07/21/2112804.html

超文本传输协议(HTTP)也许是当今互联网上使用的最重要的协议了。Web服务,有网络功能的设备和网络计算的发展,都持续扩展了HTTP协议的角色,超越了用户使用的Web浏览器范畴,同时,也增加了需要HTTP协议支持的应用程序的数量。

尽管java.net包提供了基本通过HTTP访问资源的功能,但它没有提供全面的灵活性和其它很多应用程序需要的功能。HttpClient就是寻求弥补这项空白的组件,通过提供一个有效的,保持更新的,功能丰富的软件包来实现客户端最新的HTTP标准和建议。

为扩展而设计,同时为基本的HTTP协议提供强大的支持,HttpClient组件也许就是构建HTTP客户端应用程序,比如web浏览器,web服务端,利用或扩展HTTP协议进行分布式通信的系统的开发人员的关注点。

1. HttpClient的范围

  • 基于HttpCore[http://hc.apache.org/httpcomponents-core/index.html]的客户端HTTP运输实现库
  • 基于经典(阻塞)I/O
  • 内容无关

2. 什么是HttpClient不能做的

  • HttpClient不是一个浏览器。它是一个客户端的HTTP通信实现库。HttpClient的目标是发送和接收HTTP报文。HttpClient不会去缓存内容,执行嵌入在HTML页面中的javascript代码,猜测内容类型,重新格式化请求/重定向URI,或者其它和HTTP运输无关的功能。

第一章 基础

1.1 执行请求

HttpClient最重要的功能是执行HTTP方法。一个HTTP方法的执行包含一个或多个HTTP请求/HTTP响应交换,通常由HttpClient的内部来处理。而期望用户提供一个要执行的请求对象,而HttpClient期望传输请求到目标服务器并返回对应的响应对象,或者当执行不成功时抛出异常。

很自然地,HttpClient API的主要切入点就是定义描述上述规约的HttpClient接口。

这里有一个很简单的请求执行过程的示例:

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int l;
byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1) {
}
}

1.1.1 HTTP请求

所有HTTP请求有一个组合了方法名,请求URI和HTTP协议版本的请求行。

HttpClient支持所有定义在HTTP/1.1版本中的HTTP方法:GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS。对于每个方法类型都有一个特殊的类:HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace和HttpOptions。

请求的URI是统一资源定位符,它标识了应用于哪个请求之上的资源。HTTP请求URI包含一个协议模式,主机名称,可选的端口,资源路径,可选的查询和可选的片段。

HttpGet httpget = new HttpGet(
"http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
HttpClient提供很多工具方法来简化创建和修改执行URI。
URI也可以编程来拼装:
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
"q=httpclient&btnG=Google+Search&aq=f&oq=", null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());

输出内容为:

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

查询字符串也可以从独立的参数中来生成:

List<NameValuePair> qparams = new ArrayList<NameValuePair>();
qparams.add(new BasicNameValuePair("q", "httpclient"));
qparams.add(new BasicNameValuePair("btnG", "Google Search"));
qparams.add(new BasicNameValuePair("aq", "f"));
qparams.add(new BasicNameValuePair("oq", null));
URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search",
URLEncodedUtils.format(qparams, "UTF-8"), null);
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());

输出内容为:

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

1.1.2 HTTP响应

HTTP响应是由服务器在接收和解释请求报文之后返回发送给客户端的报文。响应报文的第一行包含了协议版本,之后是数字状态码和相关联的文本段。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());

输出内容为:

HTTP/1.1
200
OK
HTTP/1.1 200 OK

1.1.3 处理报文头部

一个HTTP报文可以包含很多描述如内容长度,内容类型等信息属性的头部信息。

HttpClient提供获取,添加,移除和枚举头部信息的方法。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie",
"c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie",
"c2=b; path="/", c3=c; domain="localhost"");
Header h1 = response.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = response.getLastHeader("Set-Cookie");
System.out.println(h2);
Header[] hs = response.getHeaders("Set-Cookie");
System.out.println(hs.length);

输出内容为:

Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"

获得给定类型的所有头部信息最有效的方式是使用HeaderIterator接口。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie",
"c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie",
"c2=b; path="/", c3=c; domain="localhost"");
HeaderIterator it = response.headerIterator("Set-Cookie");
while (it.hasNext()) {
System.out.println(it.next());
}

输出内容为:

Set-Cookie: c1=a; path=/; domain=localhost
Set-Cookie: c2=b; path="/", c3=c; domain="localhost"

它也提供解析HTTP报文到独立头部信息元素的方法方法。

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,


HttpStatus.SC_OK, "OK");


response.addHeader("Set-Cookie",


"c1=a; path=/; domain=localhost");


response.addHeader("Set-Cookie",


"c2=b; path="/", c3=c; domain="localhost"");


HeaderElementIterator it = new BasicHeaderElementIterator(


response.headerIterator("Set-Cookie"));


while (it.hasNext()) {


HeaderElement elem = it.nextElement();


System.out.println(elem.getName() + " = " + elem.getValue());


NameValuePair[] params = elem.getParameters();


for (int i = 0; i < params.length; i++) {


System.out.println(" " + params[i]);


}


}

输出内容为:

c1 = a
path=/
domain=localhost
c2 = b
path=/
c3 = c
domain=localhost

1.1.4 HTTP实体

HTTP报文可以携带和请求或响应相关的内容实体。实体可以在一些请求和响应中找到,因为它们也是可选的。使用了实体的请求被称为封闭实体请求。HTTP规范定义了两种封闭实体的方法:POST和PUT。响应通常期望包含一个内容实体。这个规则也有特例,比如HEAD方法的响应和204 No Content,304 Not Modified和205 Reset Content响应。

HttpClient根据其内容出自何处区分三种类型的实体:

  • streamed流式:内容从流中获得,或者在运行中产生。特别是这种分类包含从HTTP响应中获取的实体。流式实体是不可重复生成的。
  • self-contained自我包含式:内容在内存中或通过独立的连接或其它实体中获得。自我包含式的实体是可以重复生成的。这种类型的实体会经常用于封闭HTTP请求的实体。
  • wrapping包装式:内容从另外一个实体中获得。

当从一个HTTP响应中获取流式内容时,这个区别对于连接管理很重要。对于由应用程序创建而且只使用HttpClient发送的请求实体,流式和自我包含式的不同就不那么重要了。这种情况下,建议考虑如流式这种不能重复的实体,和可以重复的自我包含式实体。

1.1.4.1 重复实体

实体可以重复,意味着它的内容可以被多次读取。这就仅仅是自我包含式的实体了(像ByteArrayEntity或StringEntity)。

1.1.4.2 使用HTTP实体

因为一个实体既可以代表二进制内容又可以代表字符内容,它也支持字符编码(支持后者也就是字符内容)。

实体是当使用封闭内容执行请求,或当请求已经成功执行,或当响应体结果发功到客户端时创建的。

要从实体中读取内容,可以通过HttpEntity#getContent()方法从输入流中获取,这会返回一个java.io.InputStream对象,或者提供一个输出流到HttpEntity#writeTo(OutputStream)方法中,这会一次返回所有写入到给定流中的内容。

当实体通过一个收到的报文获取时,HttpEntity#getContentType()方法和HttpEntity#getContentLength()方法可以用来读取通用的元数据,如Content-Type和Content-Length头部信息(如果它们是可用的)。因为头部信息Content-Type可以包含对文本MIME类型的字符编码,比如text/plain或text/html,HttpEntity#getContentEncoding()方法用来读取这个信息。如果头部信息不可用,那么就返回长度-1,而对于内容类型返回NULL。如果头部信息Content-Type是可用的,那么就会返回一个Header对象。

当为一个传出报文创建实体时,这个元数据不得不通过实体创建器来提供。

StringEntity myEntity = new StringEntity("important message",
"UTF-8");
System.out.println(myEntity.getContentType());
System.out.println(myEntity.getContentLength());
System.out.println(EntityUtils.getContentCharSet(myEntity));
System.out.println(EntityUtils.toString(myEntity));
System.out.println(EntityUtils.toByteArray(myEntity).length);

输出内容为

Content-Type: text/plain; charset=UTF-8
17
UTF-8
important message
17

1.1.5 确保低级别资源释放

当完成一个响应实体,那么保证所有实体内容已经被完全消耗是很重要的,所以连接可以安全的放回到连接池中,而且可以通过连接管理器对后续的请求重用连接。处理这个操作的最方便的方法是调用HttpEntity#consumeContent()方法来消耗流中的任意可用内容。HttpClient探测到内容流尾部已经到达后,会立即会自动释放低层连接,并放回到连接管理器。HttpEntity#consumeContent()方法调用多次也是安全的。

也可能会有特殊情况,当整个响应内容的一小部分需要获取,消耗剩余内容而损失性能,还有重用连接的代价太高,则可以仅仅通过调用HttpUriRequest#abort()方法来中止请求。

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
int byteOne = instream.read();
int byteTwo = instream.read();
// Do not need the rest
httpget.abort();
}

连接不会被重用,但是由它持有的所有级别的资源将会被正确释放。

1.1.6 消耗实体内容

推荐消耗实体内容的方式是使用它的HttpEntity#getContent()或HttpEntity#writeTo(OutputStream)方法。HttpClient也自带EntityUtils类,这会暴露出一些静态方法,这些方法可以更加容易地从实体中读取内容或信息。代替直接读取java.io.InputStream,也可以使用这个类中的方法以字符串/字节数组的形式获取整个内容体。然而,EntityUtils的使用是强烈不鼓励的,除非响应实体源自可靠的HTTP服务器和已知的长度限制。

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
long len = entity.getContentLength();
if (len != -1 && len < 2048) {
System.out.println(EntityUtils.toString(entity));
} else {
// Stream content out
}
}

在一些情况下可能会不止一次的读取实体。此时实体内容必须以某种方式在内存或磁盘上被缓冲起来。最简单的方法是通过使用BufferedHttpEntity类来包装源实体完成。这会引起源实体内容被读取到内存的缓冲区中。在其它所有方式中,实体包装器将会得到源实体。

HttpGet httpget = new HttpGet("http://localhost/");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity = new BufferedHttpEntity(entity);
}

1.1.7 生成实体内容

HttpClient提供一些类,它们可以用于生成通过HTTP连接获得内容的有效输出流。为了封闭实体从HTTP请求中获得的输出内容,那些类的实例可以和封闭如POST和PUT请求的实体相关联。HttpClient为很多公用的数据容器,比如字符串,字节数组,输入流和文件提供了一些类:StringEntity,ByteArrayEntity,InputStreamEntity和FileEntity。

File file = new File("somefile.txt");
FileEntity entity = new FileEntity(file, "text/plain; charset="UTF-8"");
HttpPost httppost = new HttpPost("http://localhost/action.do");
httppost.setEntity(entity);

请注意InputStreamEntity是不可重复的,因为它仅仅能从低层数据流中读取一次内容。通常来说,我们推荐实现一个定制的HttpEntity类,这是自我包含式的,用来代替使用通用的InputStreamEntity。FileEntity也是一个很好的起点。

1.1.7.1 动态内容实体

通常来说,HTTP实体需要基于特定的执行上下文来动态地生成。通过使用EntityTemplate实体类和ContentProducer接口,HttpClient提供了动态实体的支持。内容生成器是按照需求生成它们内容的对象,将它们写入到一个输出流中。它们是每次被请求时来生成内容。所以用EntityTemplate创建的实体通常是自我包含而且可以重复的。

ContentProducer cp = new ContentProducer() {
public void writeTo(OutputStream outstream) throws IOException {
Writer writer = new OutputStreamWriter(outstream, "UTF-8");
writer.write("<response>");
writer.write(" <content>");
writer.write(" important stuff");
writer.write(" </content>");
writer.write("</response>");
writer.flush();
}
};
HttpEntity entity = new EntityTemplate(cp);
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);
1.1.7.2 HTML表单

许多应用程序需要频繁模拟提交一个HTML表单的过程,比如,为了来记录一个Web应用程序或提交输出数据。HttpClient提供了特殊的实体类UrlEncodedFormEntity来这个满足过程。

List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("param1", "value1"));
formparams.add(new BasicNameValuePair("param2", "value2"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
HttpPost httppost = new HttpPost("http://localhost/handler.do");
httppost.setEntity(entity);

UrlEncodedFormEntity实例将会使用URL编码来编码参数,生成如下的内容:

param1=value1&param2=value2

1.1.7.3 内容分块

通常,我们推荐让HttpClient选择基于被传递的HTTP报文属性的最适合的编码转换。这是可能的,但是,设置HttpEntity#setChunked()方法为true是通知HttpClient分块编码的首选。请注意HttpClient将会使用标识作为提示。当使用的HTTP协议版本,如HTTP/1.0版本,不支持分块编码时,这个值会被忽略。

StringEntity entity = new StringEntity("important message",
"text/plain; charset="UTF-8"");
entity.setChunked(true);
HttpPost httppost = new HttpPost("http://localhost/acrtion.do");
httppost.setEntity(entity);

1.1.8 响应控制器

控制响应的最简便和最方便的方式是使用ResponseHandler接口。这个放完完全减轻了用户关于连接管理的担心。当使用ResponseHandler时,HttpClient将会自动关注并保证释放连接到连接管理器中去,而不管请求执行是否成功或引发了异常。

HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet("http://localhost/");
ResponseHandler<byte[]> handler = new ResponseHandler

public byte[] handleResponse(
HttpResponse response) throws ClientProtocolException, IOException {
HttpEntity entity = response.getEntity();
if (entity != null) {
return EntityUtils.toByteArray(entity);
} else {
return null;
}
}
};
byte[] response = httpclient.execute(httpget, handler);

1.2 HTTP执行的环境

最初,HTTP是被设计成无状态的,面向请求-响应的协议。然而,真实的应用程序经常需要通过一些逻辑相关的请求-响应交换来持久状态信息。为了开启应用程序来维持一个过程状态,HttpClient允许HTTP请求在一个特定的执行环境中来执行,简称为HTTP上下文。如果相同的环境在连续请求之间重用,那么多种逻辑相关的请求可以参与到一个逻辑会话中。HTTP上下文功能和java.util.Map<String,Object>很相似。它仅仅是任意命名参数值的集合。应用程序可以在请求之前或在检查上下文执行完成之后来填充上下文属性。

在HTTP请求执行的这一过程中,HttpClient添加了下列属性到执行上下文中:

  • 'http.connection':HttpConnection实例代表了连接到目标服务器的真实连接。
  • 'http.target_host':HttpHost实例代表了连接目标。
  • 'http.proxy_host':如果使用了,HttpHost实例代表了代理连接。
  • 'http.request':HttpRequest实例代表了真实的HTTP请求。
  • 'http.response':HttpResponse实例代表了真实的HTTP响应。
  • 'http.request_sent':java.lang.Boolean对象代表了暗示真实请求是否被完全传送到目标连接的标识。

比如,为了决定最终的重定向目标,在请求执行之后,可以检查http.target_host属性的值:

DefaultHttpClient httpclient = new DefaultHttpClient();


HttpContext localContext = new BasicHttpContext();


HttpGet httpget = new HttpGet("http://www.google.com/");


HttpResponse response = httpclient.execute(httpget, localContext);


HttpHost target = (HttpHost) localContext.getAttribute(


ExecutionContext.HTTP_TARGET_HOST);


System.out.println("Final target: " + target);


HttpEntity entity = response.getEntity();


if (entity != null) {


entity.consumeContent();


}

输出内容为:

Final target: http://www.google.ch

1.3 异常处理

HttpClient能够抛出两种类型的异常:在I/O失败时,如套接字连接超时或被重置的java.io.IOException异常,还有标志HTTP请求失败的信号,如违反HTTP协议的HttpException异常。通常I/O错误被认为是非致命的和可以恢复的,而HTTP协议错误则被认为是致命的而且是不能自动恢复的。

1.3.1 HTTP运输安全

要理解HTTP协议并不是对所有类型的应用程序都适合的,这一点很重要。HTTP是一个简单的面向请求/响应的协议,最初被设计用来支持取回静态或动态生成的内容。它从未向支持事务性操作方向发展。比如,如果成功收到和处理请求,HTTP服务器将会考虑它的其中一部分是否完成,生成一个响应并发送一个状态码到客户端。如果客户端因为读取超时,请求取消或系统崩溃导致接收响应实体失败时,服务器不会试图回滚事务。如果客户端决定重新这个请求,那么服务器将不可避免地不止一次执行这个相同的事务。在一些情况下,这会导致应用数据损坏或者不一致的应用程序状态。

尽管HTTP从来都没有被设计来支持事务性处理,但它也能被用作于一个传输协议对关键的任务应用提供被满足的确定状态。要保证HTTP传输层的安全,系统必须保证HTTP方法在应用层的幂等性。

1.3.2 幂等的方法

HTTP/1.1 明确地定义了幂等的方法,描述如下

[方法也可以有“幂等”属性在那些(除了错误或过期问题)N的副作用>0的相同请求和独立的请求是相同的]

换句话说,应用程序应该保证准备着来处理多个相同方法执行的实现。这是可以达到的,比如,通过提供一个独立的事务ID和其它避免执行相同逻辑操作的方法。

请注意这个问题对于HttpClient是不具体的。基于应用的浏览器特别受和非幂等的HTTP方法相关的相同问题的限制。

HttpClient假设没有实体包含方法,比如GET和HEAD是幂等的,而实体包含方法,比如POST和PUT则不是。

1.3.3 异常自动恢复

默认情况下,HttpClient会试图自动从I/O异常中恢复。默认的自动恢复机制是受很少一部分已知的异常是安全的这个限制。

  • HttpClient不会从任意逻辑或HTTP协议错误(那些是从HttpException类中派生出的)中恢复的。
  • HttpClient将会自动重新执行那么假设是幂等的方法。
  • HttpClient将会自动重新执行那些由于运输异常失败,而HTTP请求仍然被传送到目标服务器(也就是请求没有完全被送到服务器)失败的方法。
  • HttpClient将会自动重新执行那些已经完全被送到服务器,但是服务器使用HTTP状态码(服务器仅仅丢掉连接而不会发回任何东西)响应时失败的方法。在这种情况下,假设请求没有被服务器处理,而应用程序的状态也没有改变。如果这个假设可能对于你应用程序的目标Web服务器来说不正确,那么就强烈建议提供一个自定义的异常处理器。

1.3.4 请求重试处理

为了开启自定义异常恢复机制,应该提供一个HttpRequestRetryHandler接口的实现。

DefaultHttpClient httpclient = new DefaultHttpClient();


HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {


public boolean retryRequest(IOException exception,


int executionCount,HttpContext context) {


if (executionCount >= 5) {


// 如果超过最大重试次数,那么就不要继续了


return false;


}


if (exception instanceof NoHttpResponseException) {


// 如果服务器丢掉了连接,那么就重试


return true;


}


if (exception instanceof SSLHandshakeException) {


// 不要重试SSL握手异常


return false;


}


HttpRequest request = (HttpRequest) context.getAttribute(


ExecutionContext.HTTP_REQUEST);


boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);


if (idempotent) {


// 如果请求被认为是幂等的,那么就重试


return true;


}


return false;


}


};


httpclient.setHttpRequestRetryHandler(myRetryHandler);

1.4 中止请求

在一些情况下,由于目标服务器的高负载或客户端有很多活动的请求,那么HTTP请求执行会在预期的时间框内而失败。这时,就可能不得不过早地中止请求,解除封锁在I/O执行中的线程封锁。被HttpClient执行的HTTP请求可以在执行的任意阶段通过调用HttpUriRequest#abort()方法而中止。这个方法是线程安全的,而且可以从任意线程中调用。当一个HTTP请求被中止时,它的执行线程就封锁在I/O操作中了,而且保证通过抛出InterruptedIOException异常来解锁。

1.5 HTTP协议拦截器

HTTP协议拦截器是一个实现了特定HTPP协议方面的惯例。通常协议拦截器希望作用于一个特定头部信息上,或者一族收到报文的相关头部信息,或使用一个特定的头部或一族相关的头部信息填充发出的报文。协议拦截器也可以操纵包含在报文中的内容实体,透明的内容压缩/解压就是一个很好的示例。通常情况下这是由包装器实体类使用了“装饰者”模式来装饰原始的实体完成的。一些协议拦截器可以从一个逻辑单元中来结合。

协议拦截器也可以通过共享信息来共同合作-比如处理状态-通过HTTP执行上下文。协议拦截器可以使用HTTP内容来为一个或多个连续的请求存储一个处理状态。

通常拦截器执行的顺序不应该和它们基于的特定执行上下文状态有关。如果协议拦截器有相互依存关系,那么它们必须按特定顺序来执行,正如它们希望执行的顺序一样,它们应该在相同的序列中被加到协议处理器。

协议拦截器必须实现为线程安全的。和Servlet相似,协议拦截器不应该使用实例变量,除非访问的那些变量是同步的。

这个示例给出了本地内容在连续的请求中怎么被用于持久一个处理状态的:

DefaultHttpClient httpclient = new DefaultHttpClient();


HttpContext localContext = new BasicHttpContext();


AtomicInteger count = new AtomicInteger(1);


localContext.setAttribute("count", count);


httpclient.addRequestInterceptor(new HttpRequestInterceptor() {


public void process(final HttpRequest request,


final HttpContext context) throws HttpException, IOException {


AtomicInteger count = (AtomicInteger) context.getAttribute("count");


request.addHeader("Count", Integer.toString(count.getAndIncrement()));


}


});


HttpGet httpget = new HttpGet("http://localhost/");


for (int i = 0; i < 10; i++) {


HttpResponse response = httpclient.execute(httpget, localContext);


HttpEntity entity = response.getEntity();


if (entity != null) {


entity.consumeContent();


}


}

1.6 HTTP参数

HttpParams接口代表了定义组件运行时行为的一个不变的值的集合。很多情况下,HttpParams和HttpContext相似。二者之间的主要区别是它们在运行时使用的不同。这两个接口表示了对象的集合,它们被视作为访问对象值的键的Map,但是服务于不同的目的:

  • HttpParams旨在包含简单对象:整型,浮点型,字符串,集合,还有运行时不变的对象。
  • HttpParams希望被用在“一次写入-多处准备”模式下。HttpContext旨在包含很可能在HTTP报文处理这一过程中发生改变的复杂对象
  • HttpParams的目标是定义其它组件的行为。通常每一个复杂的组件都有它自己的HttpParams对象。HttpContext的目标是来表示一个HTTP处理的执行状态。通常相同的执行上下文在很多合作的对象中共享。

1.6.1 参数层次

在HTTP请求执行过程中,HttpRequest对象的HttpParams是和用于执行请求的HttpClient实例的HttpParams联系在一起的。这使得设置在HTTP请求级别的参数优先于设置在HTTP客户端级别的HttpParams。推荐的做法是设置普通参数对所有的在HTTP客户端级别的HTTP请求共享,而且可以选择性重写具体在HTTP请求级别的参数。

DefaultHttpClient httpclient = new DefaultHttpClient();


httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,HttpVersion.HTTP_1_0);


httpclient.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET,"UTF-8");


HttpGet httpget = new HttpGet("http://www.google.com/");


httpget.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION,HttpVersion.HTTP_1_1);


httpget.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,Boolean.FALSE);


httpclient.addRequestInterceptor(new HttpRequestInterceptor() {


public void process(final HttpRequest request,


final HttpContext context) throws HttpException, IOException {


System.out.println(request.getParams().getParameter(


CoreProtocolPNames.PROTOCOL_VERSION));


System.out.println(request.getParams().getParameter(


CoreProtocolPNames.HTTP_CONTENT_CHARSET));


System.out.println(request.getParams().getParameter(


CoreProtocolPNames.USE_EXPECT_CONTINUE));


System.out.println(request.getParams().getParameter(


CoreProtocolPNames.STRICT_TRANSFER_ENCODING));


}


});

输出内容为:

HTTP/1.1
UTF-8
false
null

1.6.2 HTTP参数bean

HttpParams接口允许在处理组件的配置上很大的灵活性。很重要的是,新的参数可以被引入而不会影响老版本的二进制兼容性。然而,和常规的Java bean相比,HttpParams也有一个缺点:HttpParams不能使用DI框架来组合。为了缓解这个限制,HttpClient包含了一些bean类,它们可以用来按顺序使用标准的Java eban惯例初始化HttpParams对象。

HttpParams params = new BasicHttpParams();
HttpProtocolParamBean paramsBean = new HttpProtocolParamBean(params);
paramsBean.setVersion(HttpVersion.HTTP_1_1);
paramsBean.setContentCharset("UTF-8");
paramsBean.setUseExpectContinue(true);
System.out.println(params.getParameter(
CoreProtocolPNames.PROTOCOL_VERSION));
System.out.println(params.getParameter(
CoreProtocolPNames.HTTP_CONTENT_CHARSET));
System.out.println(params.getParameter(
CoreProtocolPNames.USE_EXPECT_CONTINUE));
System.out.println(params.getParameter(
CoreProtocolPNames.USER_AGENT));

输出内容为:

HTTP/1.1
UTF-8
false
null

1.7 HTTP请求执行参数

这些参数会影响到请求执行的过程:

  • 'http.protocol.version':如果没有在请求对象中设置明确的版本信息,它就定义了使用的HTTP协议版本。这个参数期望得到一个ProtocolVersion类型的值。如果这个参数没有被设置,那么就使用HTTP/1.1。
  • 'http.protocol.element-charset':定义了编码HTTP协议元素的字符集。这个参数期望得到一个java.lang.String类型的值。如果这个参数没有被设置,那么就使用US-ASCII。
  • 'http.protocol.eontent-charset':定义了为每个内容主体编码的默认字符集。这个参数期望得到一个java.lang.String类型的值。如果这个参数没有被设置,那么就使用ISO-8859-1。
  • 'http.useragent':定义了头部信息User-Agent的内容。这个参数期望得到一个java.lang.String类型的值。如果这个参数没有被设置,那么HttpClient将会为它自动生成一个值。
  • 'http.protocol.strict-transfer-encoding':定义了响应头部信息中是否含有一个非法的Transfer-Encoding,都要拒绝掉。
  • 'http.protocol.expect-continue':为包含方法的实体激活Expect: 100-Continue握手。Expect: 100-Continue握手的目的是允许客户端使用请求体发送一个请求信息来决定源服务器是否希望在客户端发送请求体之前得到这个请求(基于请求头部信息)。Expect: 100-Continue握手的使用可以对需要目标服务器认证的包含请求的实体(比如POST和PUT)导致明显的性能改善。Expect: 100-Continue握手应该谨慎使用,因为它和HTTP服务器,不支持HTTP/1.1协议的代理使用会引起问题。这个参数期望得到一个java.lang.Boolean类型的值。如果这个参数没有被设置,那么HttpClient将会试图使用握手。
  • 'http.protocol.wait-for-continue':定义了客户端应该等待100-Continue响应最大的毫秒级时间间隔。这个参数期望得到一个java.lang.Integer类型的值。如果这个参数没有被设置,那么HttpClient将会在恢复请求体传输之前为确认等待3秒。
  •  

 

 

 

开源Apache Camel简化SOA实施进程(转)

 apache-camel  开源Apache Camel简化SOA实施进程(转)已关闭评论
4月 012013
 

在过去的几年中,整合技术得到了突飞猛进的发展。XML/REST/Web 服务/面向服务架构(SOA)的革命不断的促使工程师们和软件公司去创造丰富的协议、适配器、传输器、容器、标准,最佳实践···应有尽有。

无可否认的,现有的这些代码是非常复杂的、多样化的,几乎没有什么它们不可以做到的事情。但这些软件包都是从技术上来建立的,从而使得如何有效的使用其功能成为了读者很大的挑战。

目前,众多读者都完成了这一项挑战。丰富的经验和成千上万的成功案例促进了许多基础架构设计模式定义的形成,从而帮助开发者在整合时能够开门见山,少走弯路。其中,有一套设计模式在业界引起了注意,那就是Hohpe 和 Woolf's企业整合模式。这些模式包含了一些技术上无关的词汇来描述大量整合的解决方案。他们并不是侧重于低层次的编程,而是采取自上而下的方案来开发一个异步的、以信息为基础的架构。

一致的词汇是很好的,而易于使用的框架在实际建立基础架构的时候岂不是更好吗?

这正是Apache的开源Camel项目背后的理念。现在,行之有效的并且真实的一套模式已可以获得了,很明显,下一步就是要去创造一个能够以最简单的方式来实施这个模式的驱动器。

Camel 是一个代码先行的工具,它允许开发者在无须学习厂商定制和复杂的底层技术的情况下进行精密的大规模集成。Camel使用Java领域说明性专用语言来连接信息系统和配置路由和调解规则,在POJO基础上实施企业整合模式。这样,就能使得开发人员不必阅读一页又一页的诸如JMS或JBI之类的技术规范,也不用处理较低级别的Spring框架就可以直接设计和建立面向服务架构(SOA)。

Apache Camel是从Apache其他项目尤其是Apache ActiveMQ 以及Apache ServiceMix的代码和灵感有机的衍生出来的。该项目的成员们发现:人们在许多不同的情况下都想要建立或是使用企业集成模式书中的模式。因此,Camel团队就这样明确的目的开始建立这样的框架。

Camel 概况:

建立Camel的第一步是解偶底层框架中的模式。一些人想要利用企业服务总线(ESB)中的模式,还有一些人则选择Message Broker中的模式,而其他人需要使用应用程序自身的模式或信息提供者间进行对话。仍然有一些人想要在Web服务框架下或是其他沟通平台上使用。比起将此路由代码与特定的Message Broker或企业服务总线(ESB)绑定来说,Camel选择提取此代码作为独立的框架, 这样一来它就能被应用到任何的项目中去。Camel有一个脚本――不管是在Servlet, 或是Web服务包中,全面企业服务总线(ESB),还是信息应用程序中,任何地方都可以做到重新利用。

Camel的最初也是主要的优势在于开发团队无须为了连接系统在容器上下功夫。很多人会认为从容器上下功夫是一个正确的途径,甚至是当成对勇气的考验,但对于越来越多的团队来说这些阻碍成为了不必要的障碍。有了Apache Camel,开发人员就能在完成任务的过程中将无关的任务减少到最低。如果其他要求得到保证的话,Camel还可以在JBI容器中实施,但这并不是必要的。

为了简化编程,Camel还支持特定的Java和XML域语言,使得企业集成模式你那个在所有的Java IDE或Spring XML中使用。这种更高层次的提取能更有效的解决问题。

Camel重新利用了许多Spring 2 的特性,如说明性交易,控制配置倒置,以及众多为了与JMS和JDBC,Java Persistence API (JPA)结合工作的实用层面的东西。这提高了抽取的水平,简化了编程,减少了需要编写的XML,但如果人们要是追根究底花些功夫的话,也会发现其实Camel仍然暴露了线级访问。

Camel示例

我们分别解释不同的方式实现Apache Camel的配置,首先将使用Java DSL(领域特定语言),接下来是Spring XML配置。

Java DSL 配置

如下的列子就是实现了当你想在一个目录结构中从JMS对列里得到信息。首先,我们需要创建一个CamelContext对象:

CamelContext context = new DefaultCamelContext();

这里远不止一种方法向CamelContext添加组件。你也可以在我们创建好路由之后再添加组件,正如一下示例所示: 

context.addRoutes(new RouteBuilder() { 
  public void configure() { 
  from("test-jms:queue:test.queue").to("file://test"); 
  // set up a listener on the file component 
  from("file://test").process(new Processor() { 
  public void process(Exchange e) { 
  System.out.println("Received exchange: " + e.getIn()); 
  } 
  }); 
  } 
  });

或者明确的加入JMS组件,如下所示:  

ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); 
  // note we can explicity name the component 
  context.addComponent("test-jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));

下次你必须要启动Camel context。如果你使用Spring去配置Camel context这将会为你实现自动化过程,如果你是使用纯Java的话那就必须用先调用start()的方法:

camelContext.start();

这将启动所有的配置路由规则。

当我们完全启动CamelContext后我们可以输入一些对象至Camel。

在通常的使用中,更多的情况往往是通过一些功能组件,外部系统将一些信息或是事件直接输入到Camel中,但是在这里我们将使用CamelTemplate,这也是一个最简单的方法用以测试之前的配置是否正确。

CamelTemplate template = new CamelTemplate(context);

这时我们可以使用CamelTemplate通过JMS发送一些测试消息:  

for (int i = 0; i < 10; i++) { 
  template.sendBody("test-jms:queue:test.queue", "Test Message: " + i); 
  }

我们从CamelTemplate中发送对象(此时通常为文本对象)纳入CamelContext中并提供给测试组件:jms:queue:test.queue。这些文本对象会自动修改进JMS信息中,并在JMS对列中以test.queue张贴出来。当我们创建好路线我们配置好FileComponent去接收test.queue。 

FileComponent文件将从对列中提取信息并保存在一个名为test的目录中。每一条信息都会存放在一个与其目的地或是信息ID相对应的文件中。

最后,我们配置自己的listener并从FileComponent获得通知,以文本的形式打印出来。

Spring XML配置

这个示例将使用Spring XML配置实现文件的转换,利用XQuery从目录中将结果发送至JMS 对列。它从目录中选取文件进行解析,然后使用XQuery进行转换,再发送至消息对列。当然,为了更清楚的看到生成文件,我们也会有一些别的方法去从JMS对列中选取内容再写到一个输出目录中。

运行示例

让我们用Camel Maven插件来运行这段示例。举例来说,通过开源软件的源代码版或可执行版即可实现以下操作:  

cd examples/camel-example-spring-xquery 
  mvn camel:run

你现在应该可以看到在target/outputFiles目录下所生成的文件内容,这就是从JMS对列中所读出的转化信息。 

代码走查

需要做的是用那些定义在类下的 META-INF/spring/camelContext.xml文件正常启动Spring应用。这是应用Camel的XML配置进行配置CamelContext的规范Spring XML文档。

请务必注意这段XML示例文件底部,我们明确的配置了ActiveMQ组件以及关于如何连接代理的详细内容。

关键的Spring XML代码内容如下所示:

<camelContext useJmx="true" xmlns="http://activemq.apache.org/camel/schema/spring"> 

   <!– lets parse files, transform them with XQuery and send them to JMS –> 
   <route> 
     <from uri="file:src/data?noop=true"/> 
     <to uri="xquery:myTransform.xquery"/> 
     <to uri="jms:MyQueue"/> 
   </route> 

   <!– now lets write messages from the queue to a directory –> 
   <route> 
     <from uri="jms:MyQueue"/> 
     <to uri="file:target/outputFiles"/> 
   </route> 

</camelContext> 

<!– lets configure the default ActiveMQ broker URL –> 
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> 
   <property name="connectionFactory"> 
     <bean class="org.apache.activemq.ActiveMQConnectionFactory"> 
       <property name="brokerURL" value="vm://localhost?broker.persistent=false"/> 
     </bean> 
   </property> 
</bean> 

希望这篇文章能够让你真正认识到使用Apache Camel来实现企业应用整合是一件多么实际的事。 

转自:http://www.uml.org.cn/soa/200910162.asp