<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>疯狂的菠菜</title>
    <description>菠菜也疯狂</description>
    <link>http://macrochen.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>使用Dorado框架开发必备参考(更新中)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215921" style="color:red;">http://macrochen.javaeye.com/blog/215921</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          下面是我经常会用到的一些东西, 记下来, 以作为以后的参考:<br /><br />如果要设置模糊查询, 一般要在QueryCommand中这样写:<br /><pre name="code" class="js">var name = dsQuery.getValue("NAME");
var parameters = command.parameters();
if (name) {
    parameters.setValue("NAME", "%" + name + "%")
}</pre><br /><br />dataset如果没有分页的话, 默认一次是只取100条, 这也意味着如果记录超过100条就要考虑分页了, 如果我们不希望分页但是记录又操作了100条的话, 这需要将pageSize设置为-1<br /><br />对于SQLDataset来说, orginTable属性是必须设置的, 这个比较奇怪, 我觉得这个orginTable只是对数据的修改才会有效, 虽然通过UpdateCommand做了提交,但是如果没有设置originTable就不应该做修改操作, 但是dorado内部却没有这么做, 所以这里除了无奈的设置originTalbe属性之外,还要借助Record.setState(none)的方式来避免做更新操作<br /><br />还有一个可能会经常碰到的奇怪的地方,就是如果数据库中用一个char(1)来表示一个boolean值,在dataset中不能直接将其属性设置为boolean,而是必须设置为string, 然后手工设置editerType为checkbox, 而且在编辑的时候还需要在编辑器的onSetValue()事件中写如下代码:<br />checkBox.checked = dsEntity.getValue("admin") == 1 ? true : false;否则打勾选中跟dataset之间的同步会有问题<br /><br />单表操作是一个系统中会经常碰到的问题, 它简单,繁琐, 重复, 堪称无聊中的无聊. 因此我们在处理这种操作界面的时候通常会考虑到尽可能的重用, 因为没法做到继承, 所以我们只好不停的copy&paste, 当然通过插件提供的模板也可以简化我们的工作, 不过我在开发中更喜欢copy&paste, 毕竟做一个比较通用的模板也不是分分钟就能搞定的. 那么如何保证我们的copy&paste更有效, 改动更少呢? 我发现在在命名上有一定的技巧, 就是对各种通用的dataset和control采用泛型化的命名方式, 比如对一个公告表的单表操作, 在dataset上可以命名为dsEntity, 对于数据的列表显示可以命名为tblEntity, 编辑AutoForm可以命名frmEntity如此等等, 这样在对这些id进行引用时可以保证具有一定的稳定性, 不会因为换成另外一个单表的维护配置文件, 而需要对这些常用的id进行修改, 这样做多了, 模板自然而然就形成了. 因此我不推荐一开始就去建一个模板, 而是应该在应用中通过不断提炼总结而得到一些通用的模板<br /><br />subwindow不要在view.xml文件中创建， 直接在jsp中定义， 否则在使用show(true, true)打开subwindow的时候出现无法居中的问题<br /><br />通过远程调用不需要使用将WrappedDataset转换成具体的dataset(使用updatecommand执行远程调用时需要转换), 但是在复写init之类的方法则需要这样做, 即在ViewModel中需要将Dataset转换成指定类型的Dataset需要这样做:<br /><pre name="code" class="java">Dataset dsRole = this.getDataset("dsEntity");
DatasetWrapper dr = (DatasetWrapper) dsRole;
SqlDataset ds = (SqlDataset) dr.getWrappedDataset();</pre><br /><br />在复写initDatasets方法的时候,需要注意通过getState()判断当前的请求的状态,因为在执行updatecommand之类的command,也会调用initDatasets方法<br /><br />给某个日期字段设置为当前日期的写法<br /><pre name="code" class="js">var current = dsQuery.getCurrent();
current.setValue("datetime", "${Util.getDate('yyyy-MM-dd HH:mm:ss')}");</pre><br />或直接在dataset的指定field设置defaultValue属性为${Util.getDate("yyyy-MM-dd")}<br /><br /><br />对于formdataset来说，通过listener来为其加载数据（比如使用dataset.insertRecord()方法），在对应的的autoform中是不会取得数据的<br /><br />dorado与报表<br />交叉表只能放在summery区，否则会出现<br /><div class="quote_title">引用</div><div class="quote_div">Error filling print... Error incrementing crosstab dataset <br />net.sf.jasperreports.engine.JRRuntimeException: Error incrementing crosstab dataset <br /><br />java.io.InvalidClassException: net.sf.jasperreports.engine.base.JRBaseReport; local class incompatible: stream classdesc serialVersionUID = 607, local class serialVersionUID = 608</div><br />错误原因是 jasperreports.jar  版本不统一<br />jrxml complie to jasper 是在 iReport  中完成的，然后直接把 .jasper  拷贝 到应用中使用，一直抛错，后来发现是 iReport   和应用中使用的 jar 版本不一致，<br />iReport   ：jasperreports-1.1.1.jar<br />应用：jasperreports-1.2.0.jar<br />统一为 jasperreports-1.2.0.jar 就可以了<br /><br />如果要使用主从更新的话, 那么要取消掉主从之间的外键约束,否则在同时保存主从表的时候会出现违反主从外键约束的异常<br /><br />通过在dataset的listener中使用insertRecord()添加一条记录的时候,记录的状态是none,然后放到前台来修改,最终提交的时候注意设置该记录的状态为new,因为该记录的状态仍然为none<br /><br />在后台通过DBStatement插入记录的时候,如果有字段为date类型,那么插入的日期默认情况下是不带日期的(其他不清楚,反正oracle下是如此),要使得数据带日期,需要这样做:<br /><pre name="code" class="java">Variant variant = new Variant();
variant.setDataType(12); 
variant.setValue(new Date());
params.setVariant(fieldName, variant);</pre><br /><br />在staticTable中给日期字段设置显示格式的pattern写法:<div class="quote_title">引用</div><div class="quote_div">Util.formatDate(record.getDate("AUDIT_DATE"),"yyyy-MM-dd HH:mm:ss")</div><br />true和false转男女的写法:<div class="quote_title">引用</div><div class="quote_div">Util.switchValue(record.getBoolean("sex"),Resource.hr.male,Resource.hr.female)</div><br />格式化日期的表达式: <div class="quote_title">引用</div><div class="quote_div">Util.formatDate(record.getString("XXX"),"yyyy-MM-dd")</div><br />el表达式在server运行，一律是java对象。<br />record就是Record对象，Util是com.bstek.dorado.common.rtexpr.impl.ExpressionUtils<br /><br />在dorado写sql的时候,参数名最好采用下划线的方式命名, 这样更简洁<br /><br />如果需要实现alert(message) + AbortException的功能, 请这样使用:<pre name="code" class="js">throw new DoradoException(message);</pre><br /><br />dbstatement.close请谨慎使用, 因为close是关闭连接, 事务结束, 如果有多个操作的时候, 可能因为是在多个事务中操作而导致数据不一致<br /><br />对于customdropdown来说, 从dropdown向外面传递选中的数据, 需要在指定的地方这样写:<br />DropDown.closeFrame(dsEntity.getCurrent());<br />在外面接收需要在dropdown的onSelect事件中这样写:<br /><pre name="code" class="js">if (selectedObject) {
    var params = dsRichFormItem.parameters();
    var formId = selectedObject.getValue("ID");
    params.setValue("paymentFormId", formId);
    params.setValue("formId", formId);
    dsRichFormItem.flushData();
}
return true;</pre><br />向customdropdown传递参数需要在ViewModel中通过指定路径后面添加参数来实现:<br /><pre name="code" class="java">protected void initControl(Control control) throws Exception {
	super.initControl(control);
	if (getState() == STATE_VIEW && control.getId().equals("lstEmployee")) {
		CustomDropDown lstEmployee = (CustomDropDown) control;
		lstEmployee.setPath(lstEmployee.getPath() + "?employeeId=" + getInitiatorId());
	}
}</pre><br /><br />在command中使用dsAssign.postRecord()验证语句时要这样用:<br /><pre name="code" class="js">if (dsAssign.postRecord()) {
    var employeeId = dsAssign.getValue("employeeId");
    command.parameters().setValue("employeeId", employeeId);
    command.parameters().setValue("accountId", dsAssign.getValue("accountId"));
}else{
	return false;
}</pre><br />否则在出错的时候也会提交
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215921#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 18:21:54 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215921</link>
        <guid>http://macrochen.javaeye.com/blog/215921</guid>
      </item>
      <item>
        <title>赖世雄教你学英语语法学习笔记(未完)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215917" style="color:red;">http://macrochen.javaeye.com/blog/215917</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium">关系代词部分（18－19）<br />关系代词使用的三个原则<br />1、关系代词前面必须有先行词，也就是被代替的名词<br />2、关系代词在所引导的从句中必须当主语或宾语<br />3、关系代词在从句中不能当主语或宾语的时候前面必须有介词<br /><br />形容词性从句分为非限定和限定， 非限定是可有可无的， 用逗号分隔；限定性的不用逗号分隔，一般用来修饰独一无二的名词的形容词性从句都是非限定性的</span>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215917#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 18:12:56 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215917</link>
        <guid>http://macrochen.javaeye.com/blog/215917</guid>
      </item>
      <item>
        <title>PL SQL学习笔记(未完)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215915" style="color:red;">http://macrochen.javaeye.com/blog/215915</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium">sql属于第三代语言, 也就是说通过该语言能告诉操作者能做什么, 而无法告诉操作者如何做, 也就是屏蔽了系统内部的数据结构和算法<br />pl sql属于第四代语言, 它通过在sql的基础上引入变量, 控制语句, 过程和函数, 使得它除了sql的功能之外, 还更加灵活.<br /><br />plsql的大致结构如下:<br /><div class="quote_title">引用</div><div class="quote_div">DECLARE<br />   变量声明<br />BEGIN<br />   执行代码块<br />   EXCEPTION<br />   异常执行代码块<br />END</div><br />plsql也就是"三段式"的结构(声明, 执行代码快, 异常处理块), 其中只有执行代码块是必须的,其他两个部分是可选的.<br /><br />pl sql中的变量可以自定义, 一些复杂的数据结构,比如记录类型, 表类型之类的<br /><br />游标是用来处理使用select语句从数据库中检索到的多行记录的工具, 通过游标可以多一组记录集逐行进行处理, 对数据的统计和分析来说游标非常有用<br /><br />过程和函数(统称为子程序)是经过编译的程序代码块,二者的区别是, 函数有返回值, 过程没有<br /><br />为了方便管理, 可以将子程序, 变量以及变量类型组成包, 包包括两部分:说明和包体, 跟jdk有些类似, oralce也提供了一些内置的package, 一般都是以DBMS_打头用来方便在我们的plsql中调用.<br /><br />一般的程序设计语言的基本单位是语句, 但是plsql的基本单位是语句块, 也就是declare ... begin ... end之间的部分.可以给语句块取一个名字, 也可以没有名字(匿名语句块), declare只是用来提供变量声明的, 如果没有变量声明则可以直接以begin打头.因为代码的执行部分是放在begin...end;(后面必须加分号)之间的.<br /><br />用户自定义类型, 可以这样理解自定义类型, 自定义类型是基本类型的一个子集, 因此又称之为基本类型的子类型, 如果是基于数据库表中的某个字段的类型,可以这样写:SUBTYPE ID_num IS emp.empno%TYPE, 但是它只能集成列的尺寸约束, 不会集成象not null之类的约束.<br /><br />在plsql中关于null有几点需要注意:<br />和包含null的值进行比较, 得到的结果总是null; <br />将逻辑操作符not应用一个null值上, 其结果也为null; <br />case...when条件选择语句中如果没有找到匹配的条件, 返回的结果是null, 而且null也不能做为when中的比较结果来进行匹配, 必须使用is null来测试结果;<br />在if...then...else条件语句中, 如果为false或者null会执行else中的语句;<br />零长度的字符串被看作null;<br />在使用||串接字符串时, null将被忽略;<br /><br />plsql与其他的语言相比, 多了一个goto和null语句, 使用goto有很多限制, 不用多说, null语句表示什么也不做.<br /><br />161 过程与函数<br />180 包<br />package是plsql代码块的一种组织形式, 它分包规范和包体, 只有子程序和游标需要在包体部分定义, 要想包体中定义的子程序和游标在包之外也能使用, 必须在包规范中进行声明<br />218 游标<br />游标可以用来增强sql语句的功能, 有时候sql无法搞定的时候, 我们就可以借助游标来对从数据库中取出的表进行处理<br />对于显式游标的处理可以概括为四个步骤:声明游标,打开游标, 提取游标和关闭游标<br />245 触发器<br />...<br /></span>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215915#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 18:09:52 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215915</link>
        <guid>http://macrochen.javaeye.com/blog/215915</guid>
      </item>
      <item>
        <title>Java学习笔记(适合面试前复习)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215912" style="color:red;">http://macrochen.javaeye.com/blog/215912</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium"><strong>Java基础</strong><br />基本类型不是new出来的则是放在栈里面,对象的引用也是放在栈里面的,只要是用new()来新建对象的，都会在堆中创建<br /><br />String类被设计成为不可改变(immutable)的类。如果你要改变其值，可以，但JVM在运行时根据新值悄悄创建了一个新对象，然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的，但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中，会带有一定的不良影响。<br /><br />关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤： <br />(1)先定义一个名为str的对String类的对象引用变量：String str； <br />(2)在栈中查找有没有存放值为"abc"的地址，如果没有，则开辟一个存放字面值为"abc"的地址，接着创建一个新的String类的对象o，并将o的字符串值指向这个地址，而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址，则查找对象o，并返回o的地址。<br />(3)将str指向对象o的地址。 <br />值得注意的是，一般String类中字符串值都是直接存值的。但像String str = "abc"；这种场合下，其字符串值却是保存了一个指向存在栈中数据的引用！<br /><br />java内存泄露的两个条件：无用，无法回收。<br /><br />BigInteger能表示任意精度的整数, BigDecimal能表示任意精度的浮点数, 但都是用精度换速度的做法<br /><br /><strong>容器集合</strong><br />List的子类里面LinkedList和ArrayList是非同步的,而Vector和Stack是同步的, LinkedList底层采用的是双向链表(前指针+数据+后指针), 因此查询效率低, 增删效率高; ArrayList底层采用的是Array,因此查询效率高, 增删效率低; Vecotr是重量级的List, 一般在并发保证线程安全时采用<br /><br />如果想跟踪添加给HashSet的元素的顺序，LinkedHashSet实现会有帮助。LinkedHashSet的迭代器按照元素的插入顺序来访问各个元素。<br /><br />List中的元素有顺序, 可重复, Set中的元素无序, 不可重复(SortedSet是有序的)<br /><br />Iterator的next方法将跳到下一个元素, 并返回刚刚跳过的元素, 因此使用remove方法的时候,必须先调用next方法<br /><br />在Map 中插入、删除和定位元素，HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键，那么TreeMap会更好。<br /><br />LinkedHashMap扩展HashMap，以插入顺序将关键字/值对添加进链接哈希映像中。<br /><br />Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，因为它是同步的。 <br /><br /><strong>效率</strong><br />在方法内部频繁存取变量的时候, 使用局部变量比使用实例变量和静态变量要有更高的效率<br /><br />异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地（Native）方法，fillInStackTrace()方法检查堆栈，收集调用跟踪信息。只要有异常被抛出，VM就必须调整调用堆栈，因为在处理过程中创建了一个新的对象。<br /><br /><strong>I/O</strong><br />任何有能力产生数据流（源）的java io对象就可以看作是一个InputStream对象, 何有能力接收数据源(流)的java io对象我们就可以看作是一个OutputStream对象<br /><br />io数据流有两种类别,一种就是InputStream/OutputStream,一种是Reader/Writer, 分别用来处理字节流和字符流, 字符也是一种字节, 为什么还要用Reader/Writer呢, 这是因为在处理字符的时候可能涉及到转码,转码主要是Reader与InputStream,以及Writer与OutputStream之间, 转码的过程都是通过InputStreamReader和OutputStreamWriter来做的, 有时候我们可能没有显式的调用这两个类, 但是相关的类在内部是做了这种转换的, 比如FileReader其实就是通过InputStreamReader将数据从FileInputStream取过来的, 比如将操作系统的文字编码转化为Unicode(因为在Java内部都是采用Unicode来保存数据的), 记住这里有一个前提,那就是操作系统当前的编码格式和文档的编码格式是一直的,如果不一致就必须指定当前的InputStream的编码格式.如果不知道字符所采用何种编码方式, 那么就不要使用Reader和Writer而应该改用InputStream和OutStream<br /><br />对输入/输出流进行缓冲可以提高代码执行效率, 也就是使用BufferedXxxx和BufferedXxxx对原有的流进行封装, 缓冲是一个非常重要而基本的加速I/O访问的技术<br /><br />final关键字修饰的变量,表明该变量是不变的, 但是这里的不变只是所引用本身不变, 至于引用的对象是否改变则没有约束, 比如final StringBuffer a=new StringBuffer("immutable");a.append(" broken!"); //编译通过, 这里我们可以看出, final只对引用的"值"(也就是它所指向的那个对象的内存地址)有效, 它迫使引用只能指向初始指向的那个对象, 改变它的指向会导致编译器错误, 至于它所指向的对象的变化, final是不负责的.<br /><br />当你要clone的类里面含有可修改的引用字段的时候，那么你一定要把整个类的蓝图进行复制，如果对你clone得到的对象进行修改的时候还会影响到原来的实例，那么这是不可取的。所以应该这样clone()<br /><br /><strong>JVM</strong><br />一般来说，我们使用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,其中必须实现的方法是loadClass()，对于这个方法需要实现如下操作:(1) 确认类的名称;(2) 检查请求要装载的类是否已经被装载;(3) 检查请求加载的类是否是系统类;(4) 尝试从类装载器的存储区获取所请求的类;(5) 在虚拟机中定义所请求的类;(6) 解析所请求的类;(7) 返回所请求的类。<br /><br />Java中的类的装载过程也就是代理装载的过程。比如:Web浏览器中的JVM需要装载一个小应用程序TestApplet。JVM调用小应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先请求它的父装载器, 即系统装载器装载TestApplet是否装载了这个类, 由于TestApplet不在系统装载器的装载路径中, 所以系统装载器没有找到这个类, 也就没有装载成功。接着ACL自己装载TestApplet。ACL通过网络成功地找到了TestApplet.class 文件并将它导入到了JVM中。在装载过程中, JVM发现TestAppet是从超类java.applet.Applet继承的。所以JVM再次调用ACL来装载java.applet.Applet类。ACL又再次按上面的顺序装载Applet类, 结果ACL发现他的父装载器已经装载了这个类, 所以ACL就直接将这个已经装载的类返回给了JVM , 完成了Applet类的装载。接下来,Applet类的超类也一样处理。最后, TestApplet及所有有关的类都装载到了JVM中。<br /><br />当 JVM 需要使用类时，它根据名称向 ClassLoader 请求这个类，然后 ClassLoader 试图返回一个表示这个类的 Class 对象。通过覆盖对应于这个过程不同阶段的方法，可以创建定制的 ClassLoader。其中有个loadClass(String name, boolean resolve)方法，该方法为ClassLoader的入口点，在jdk1.2以后，loadClass方法将缺省调用findClass方法，详细内容可以参考API文档，我们编写的ClassLoader主要就是为了覆盖以上两个方法。<br /><br /><strong>Java EE</strong><br />Tomcat Server处理一个http请求的过程 <br />假设来自客户的请求为： <br />http://localhost:8080/wsota/wsota_index.jsp <br />1) 请求被发送到本机端口8080，被在那里侦听的Coyote HTTP/1.1 Connector获得 <br />2) Connector把该请求交给它所在的Service的Engine来处理，并等待来自Engine的回应 <br />3) Engine获得请求localhost/wsota/wsota_index.jsp，匹配它所拥有的所有虚拟主机Host <br />4) Engine匹配到名为localhost的Host（即使匹配不到也把请求交给该Host处理，因为该Host被定义为该Engine的默认主机） <br />5) localhost Host获得请求/wsota/wsota_index.jsp，匹配它所拥有的所有Context <br />6) Host匹配到路径为/wsota的Context（如果匹配不到就把该请求交给路径名为""的Context去处理） <br />7) path="/wsota"的Context获得请求/wsota_index.jsp，在它的mapping table中寻找对应的servlet <br />8) Context匹配到URL PATTERN为*.jsp的servlet，对应于JspServlet类 <br />9) 构造HttpServletRequest对象和HttpServletResponse对象，作为参数调用JspServlet的doGet或doPost方法 <br />10)Context把执行完了之后的HttpServletResponse对象返回给Host <br />11)Host把HttpServletResponse对象返回给Engine <br />12)Engine把HttpServletResponse对象返回给Connector <br />13)Connector把HttpServletResponse对象返回给客户browser <br /><br /><strong>JDK新特性</strong><br />注释(Annotation)，得先提一提什么是元数据(metadata)。所谓元数据就是数据的数据。也就是说，元数据是描述数据的。就象数据表中的字段一样，每个字段描述了这个字段下的数据的含义。而J2SE5.0中提供的注释就是java源代码的元数据，也就是说注释是描述java源代码的。<br /><br /><strong>多线程 线程</strong><br />实现线程，有两种方法，一种是继承Thread类，一种是实现Runnable接口。优先采用实现Runnable接口的方法。首先，把需要共享的数据放在一个实现Runnable接口的类里面，然后，把这个类的实例传给多个Thread的构造方法。这样，新创建的多个Thread，都共同拥有一个Runnable实例，共享同一份数据。如果采用继承Thread类的方法，就只好使用static静态成员了。如果共享的数据比较多，就需要大量的static静态成员，令程序数据结构混乱，难以扩展。这种情况应该尽量避免。使用Runnable还有一个好处就是多线程应用对象可以继承别的对象而不是必须继承Thread类, 从而提高多线程对象的灵活性.<br /><br />多线程的执行过程是当一个程序(一个线程)执行的过程中, 通过调用Thread.start(), 让当前的程序(线程)继续执行的同时, 那个start的Thread会同时执行位于run()方法中的代码, 多段可执行代码交替执行的过程.<br /><br />由于多个线程拥有度大力的执行对战和程序执行上下文, 因此定义在线程方法中的局部变量不会出现资源共享的问题, 资源共享同步主要发生在成员变量上.<br />按照线程体在计算机系统内存中的状态不同，可以将线程分为创建、就绪、运行、睡眠、挂起和死亡等类型。这些线程状态类型下线程的特征为： <br />　　创建状态：当利用new关键字创建线程对象实例后，它仅仅作为一个对象实例存在，JVM没有为其分配CPU时间片等线程运行资源； <br />　　就绪状态：在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时，线程已经得到除CPU时间之外的其它系统资源，只等JVM的线程调度器按照线程的优先级对该线程进行调度，从而使该线程拥有能够获得CPU时间片的机会。 <br />　　睡眠状态：在线程运行过程中可以调用sleep方法并在方法参数中指定线程的睡眠时间将线程状态转换为睡眠状态。这时，该线程在不释放占用资源的情况下停止运行指定的睡眠时间。时间到达后，线程重新由JVM线程调度器进行调度和管理。 <br />　　挂起状态：可以通过调用suspend方法将线程的状态转换为挂起状态。这时，线程将释放占用的所有资源，由JVM调度转入临时存储空间，直至应用程序调用resume方法恢复线程运行。 <br />　　死亡状态：当线程体运行结束或者调用线程对象的stop方法后线程将终止运行，由JVM收回线程占用的资源。 <br />在Java中比较特殊的线程是被称为守护（Daemon）线程的低级别线程。这个线程具有最低的优先级，用于为系统中的其它对象和线程提供服务。将一个用户线程设置为守护线程的方式是在线程对象创建之前调用线程对象的setDaemon方法。典型的守护线程例子是JVM中的系统资源自动回收线程，它始终在低级别的状态中运行，用于实时监控和管理系统中的可回收资源。 <br /><br />jvm固定了线程和内存之间的关系, jvm的内存又分为主内存和工作内存, 线程要操作的数据放在工作内存中, 工作内存的变脸是主内存的拷贝, 线程执行完后工作内存中的变量还需要更新主内存中对应的变量.对于使用了synchornized保护的代码对主内存变量将被锁定从而保证主内存和工作内存之间的变量报纸一致性.但是使用synchonrized的代码在性能上会带来不小的损失<br /><br />ThreadLocal并非是一个线程的本地实现版本，它并不是一个Thread，而是thread local variable（线程局部变量）。也许把它命名为ThreadLocalVar更加合适。线程局部变量（ThreadLocal）其实的功用非常简单，就是为每一个使用该变量的线程都提供一个变量值的副本，是每一个线程都可以独立地改变自己的副本，而不会和其它线程的副本冲突。它的内部其实可以理解为仅有一个Entry的Map, key是当前的thread, 而value则是要保存的变量.因此它只有两个方法set()将变量跟thread关联, get()方法从map中取出变量, 还有一个就是initValue方法, 是用来生成自己的ThreadLocal的时候用的, 用来给出默认值, 在get和set方法中调用</span>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215912#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 18:05:38 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215912</link>
        <guid>http://macrochen.javaeye.com/blog/215912</guid>
      </item>
      <item>
        <title>jBPM学习笔记(更新中...)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215909" style="color:red;">http://macrochen.javaeye.com/blog/215909</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium">★execution是一种类似有限状态机和UML状态图的东西, 用于在整个流程中流动, 将携带一些context信息, 比如当前节点.Transition通过使用take方法来将execution从一个节点传递到另一个节点.而当execution到达一个node之后, 将通过node的execute方法继续传递到下一个节点, 如果这个节点没有继续传递execution的话, 那么它将处于等待状态. 在开始节点将对新创建的execution进行初始化, 然后等待一个事件(或一个信号)来将其进行传递<br />★execution的传递过程是这样的:首先调用execution的event方法, 然后在event中找到leaving transition, 如果找到, 那么将通过调用transition的take方法将execution做为参数进行传递.到达一个node之后, 接着调用node的execute方法, 在execute中做相应的处理之后, 选择一个transition进入下一个node.<br />★action是用来在jbpm中插入业务逻辑的地方, 它一般都是跟一个event相关联, 最基本的event是node-leave和node-enter, 一般我们是在这里加入我们的业务逻辑<br />★可以将JbpmContext看成一个事务, 在流程运行过程中所需要的服务将包含在其中(这些服务在JbpmConfiguration中配置), 一般我们在执行完成之后将其关闭<br />★一般我们通过调用JbpmContext的loadXxxx方法取得的流程(任务)实例, 需要调用JbpmContext的save方法来做最后的保存, 但是如果是调用JbpmContext的xxxForUpdate方法来取得流程(任务)实例的话, 这不必调用jbpmContext.save方法, 这是因为xxxForUpdate方法将把这些实例添加到一个自动保存列表中, 这样在调用jbpmContext.close()方法的时候将自动被保存到数据库中.如loadProcessInstanceForUpdate方法:<br /><pre name="code" class="java">
public ProcessInstance loadProcessInstanceForUpdate(long processInstanceId) {
	ProcessInstance processInstance = getGraphSession().loadProcessInstance(processInstanceId);
	addAutoSaveProcessInstance(processInstance);
	return processInstance;
}</pre><br />而在close的时候会调用autoSave方法, 其代码如下:<br /><pre name="code" class="java">
void autoSave() {
    if (autoSaveProcessInstances!=null) {
      Iterator iter = autoSaveProcessInstances.iterator();
      while (iter.hasNext()) {
        ProcessInstance processInstance = (ProcessInstance) iter.next();
        save(processInstance);
        iter.remove();
      }
    }
}</pre><br />★在默认情况下, jbpm在第一次调用hibernate session的时候将开启一个事务, 在jbpmContext关闭的时候结束一个事务, 而结束一个事务是commit还是rollback取决于我们在关闭前是否调用了jbpmContext.setRollbackOnly()方法, 调用则rollback, 否则为commit.<br />★token表示exection的执行路径, 它是一个运行时概念, 其内部维护了一个对当前节点的引用, 当流程实例创建之后, taoken(这里它被成为rootToken)也相应的被创建<br />★图形化的流程做为一种需求沟通手段非常直观, 而所有的底层细节则需要通过action来实现, 它也是用来实现业务逻辑的地方<br />★流程中的所有节点不外乎有两种职责:执行指定的java代码, 传递execution<br />★decision node有两种判断方式, 一种是定义在流程定义文件中, 一种是通过插入的handler java代码来做判断. 在配置文件中的判断做法是在transition中添加condition元素, 然后在condition中通过使用el表达式或者beanshell脚本来作判断返回的boolean值. 在运行的时候遍历所有的transition的condition, 当碰到第一个transition中的condition返回true则将从该路径离开当前节点, 如果都为false, 那么将从默认的transition离开当前节点.还有一种方法就是通过decision的expression属性的返回结果来指定从哪个transition离开.<br />★一种替代decision node的方案, 如果需要在流程外部来做判断, 那么为还可以通过state节点来处理, 首先让exection进入state节点, 这样整个流程将处于等待状态, 然后在流程外部做完处理之后, 通过调用token.signal(xxx)或者taskInstance.signal(xxx)来让流程继续执行.<br />★使用普通node来模拟decision node, 这里涉及到定制node的用法, 其实也很简单, 就是在actionhandler实现类中除了处理逻辑之外, 还需要做的一件事就是传播execution. 比如类似这样的做法:<br /><pre name="code" class="java">public class AmountUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // business logic
    Float erpAmount = ...get amount from erp-system...;
    Float processAmount = (Float) ctx.getContextInstance().getVariable("amount");
    float result = erpAmount.floatValue() + processAmount.floatValue();
    ...update erp-system with the result...;
    
    // graph execution propagation
    if (result > 5000) {
      ctx.leaveNode(ctx, "big amounts");
    } else {
      ctx.leaveNode(ctx, "small amounts");
    }
  }
}</pre><br />同样我们还可以定义自己的fork和join节点<br />★Superstate是用来对node进行分组的. 而且可以被嵌套使用, 一般在复杂的流程设计中会使用到(比如流程中的node有一定的层次关系), 其好处能对进入Superstate中的任意的一个node进行统一的控制(通过独有的两个事件superstate-enter and superstate-leave), 对于到达内部node的transition写法也引入了用于表示层次关系的"/"(下级node)和".."(上级node)<br />★在将任务分配给具体的执行人(actor)时, 可以针对某一个taskInstance写一个AssignmentHandler, 将taskInstance的actorId设置为执行人, 如果多个taskInstance都是同一个执行者的话, 那么可以考虑swimlane, swimlane相当于权限管理中的角色, 先将taskInstance跟swimlane绑定, 然后将actor跟swinlane绑定.<br />★除了可以将taskInstance跟一个人绑定之外(也就是所谓的push模式), 还可以同时跟多个人绑定(也就是所谓的pull模式), 然后由这些人自己来决定谁来负责处理该taskInstance.因此我们可以得出结论一个taskInstance只能由一个人来处理<br />★...<br /><br />jbpm表结构说明<br />jbpm_variableaccess中存放的是子processstate节点中定义的变量引用设置<br />jbpm_node则是所有流程的所有节点<br />...<br /></span>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215909#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 17:59:03 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215909</link>
        <guid>http://macrochen.javaeye.com/blog/215909</guid>
      </item>
      <item>
        <title>设计模式笔记心得(2)--Bridge模式,Decorator模式</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215898" style="color:red;">http://macrochen.javaeye.com/blog/215898</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium"><strong>Bridge模式</strong><br />Bridge的核心就是将抽象和实现分离, 让二者独立变化<br />当需要将类的行为和属性分离的时候,可以采用Bridge模式, 在抽象体实现属性, 在实现体实现行为, 从而达到属性和行为能独立变化, 他们之间通过桥接(也就是将二者绑定, 在抽象类中隐藏)的方式来进行关联, 而且这种桥接绑定的关系是可动态改变的, 这也是与继承相比最大的优点, 有时候我们也可以将其理解为代理模式的一种演化, 将行为代理给各个不同的行为实现类来处理.<br />该模式在行为和属性存在交叉重合的场景下非常有用, 比如网上那个coffee的例子可谓bridge模式的经典例子, coffee有两种属性:大杯和中杯, 同时有两种行为:加奶和不加奶, 这样属性和行为可以交叉组合而得到不同的coffee.<br /><br />Bridge模式是使用组合而非继承的典型模式<br /><br /><strong>Decorator模式</strong> <br />装饰器模式的两个要点:<br />1.装饰器类必须继承被装饰类;<br />2.装饰器类必须持有被装饰类实例;<br />采用Wrapper模式的说法可能更形象一些, 实际上就是在具体类的基础上一层层添加所需要的功能, 将被装饰的对象采用不同的装饰类层层包裹起来, 同Bridge模式一样, 他也采用组合的方式来将某个功能添加到抽象类之上, 当需要将各种装饰效果进行组合的时候可以避免生成多个抽象类的子类.<br /><br /><strong>为何采用Decorator而不是普通的继承?</strong> <br />一般通过继承来扩展一个功能, 最简单的就是对要扩展的方法进行复写, 另一个就是添加抽象方法交给子类自己去实现, 功能越多,添加的抽象方法就越多, 而这些方法都是静态的, 一旦要派生具体子类就必须实现(或者空实现) , 而这些抽象方法的调用方式也是固定的(除非复写再改写), 这样显然是不够灵活的做法, 而且如果存在多种功能的组合会导致派生的子类成几何数增长, 当出现这种情况时我们就不应该使用简单的继承,而应该进行重构使用其他的设计模式来更好的满足我们的需求.<br /><br /><strong>Decorator和Bridge的区别</strong> <br />Decorator是对已有功能的增强, 装饰类都是围绕被装饰类已有的功能而展开的, 无论装饰类怎么变, 它都必须围绕被装饰类来变化(或者是围绕二者公共的接口来变化), 因为对于客户程序来说, 调用装饰前后的对象应该是一致的. 而Bridge模式是将行为从被桥接对象(抽象类)中分离出去, 完全放到实现类中去实现, 行为实现将更少的受到被桥接对象的限制. 所以如果这种约束是必要的,就可以采用Decorator模式, 反之则可以采用Bridge模式<br /><br /><strong>注意事项</strong> <br />Decorator, Bridge采用的是组合模式, 不可避免的需要创建一些类实例, 相对于继承来说内存消耗要相对大一些, 比如使用Decorator除了需要new装饰器类实例还需要new被装饰类, 因此出于效率的考虑new出来的对象应该尽可能的小, 也就是要做到new的对象类必须职责单一, 按需来new对象, 对于Decorator来说, 抽象对象的实现应该尽可能的简单, 将复杂的东东放到装饰类中实现, 而Bridge则要将不同的职责封装到行为实现中</span>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215898#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 17:44:37 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215898</link>
        <guid>http://macrochen.javaeye.com/blog/215898</guid>
      </item>
      <item>
        <title>设计模式笔记心得(1)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/215897" style="color:red;">http://macrochen.javaeye.com/blog/215897</a>&nbsp;
          发表时间: 2008年07月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium">在使用设计模式的时候始终记住两点:<br />1.找到变化的部分，并将其封装起来<br />2.更多的考虑用对象组合机制(复杂情况)，而不是用对象继承机制(简单情况)<br /><br />分离抽象和实现是设计模式的精髓所在.<br /><br />使用设计模式的终极目标就是为了尽可能的实现重用, 从而使系统能满足需求,适应变化, 更具有可维护性, 如果不以此为目的将被视为设计模式的滥用<br /><br />使用继承只能算面向对象设计的低级层次, 而根据不同的场景需求,结合恰当的设计模式更多的使用组合才是面向对象设计的高级层次<br />继承能解决简单的分离抽象和实现, 而使用组合能解决复杂的情况, 实现更多的重用<br /><br />23种设计模式的使用不是一成不变的, 各种模式之间会根据需求的变化而进行演化. 比如在起初阶段,可能继承多于组合, 在需求变的复杂之后可能更多是使用组合类型的设计模式, 因此要深刻理解各种设计模式的不同使用场景从而抉择该使用何种或者演化为何种设计模式, 针对需求选择合适的设计模式是设计水平的体现</span>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/215897#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 17 Jul 2008 17:40:28 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/215897</link>
        <guid>http://macrochen.javaeye.com/blog/215897</guid>
      </item>
      <item>
        <title>最近想看的书</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/211783" style="color:red;">http://macrochen.javaeye.com/blog/211783</a>&nbsp;
          发表时间: 2008年07月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>1 . 《庆余年》&mdash;&mdash;2008年最大红大紫的小说</p>
<p>这个作者以前写过一本《映秀十年事》，后来写《庆余年》的时候有人做了一个书名联诗：映秀十年事，生者庆余年。5月份地震一发生，很多书迷都觉得特别诡异。不过《庆余年》和地震可没有关系，是一部架空历史的权谋小说，目前是网上最红的小说，我觉得看了《庆余年》，《雍正王朝》的那种权谋心术几乎就是小儿科了。书中那些人，超强的心计，看得让人觉得自己都是白痴。但是一切却又顺理成章。网上的小说，写到后面作者往往力不从心，比如《诛仙》和《鬼吹灯》，但是《庆余年》却越写越精彩，令人欲罢不能。</p>
<p>&nbsp;</p>
<p>2 . 《那一曲军校恋歌》&mdash;&mdash;神秘流传，史上最纯净的爱情小说</p>
<p>这个小说我觉得之所以打动人是因为真实，是写80年代末期的校园爱情的，有80年代理想主义的氛围，怀旧着读，确实感人，追忆逝水年华呀，应该是作者的亲身经历。编造一个纯净的爱情太容易了，只能打动那些爱读小言情的小白们。但是一旦真实，就可怕了。这个小说最狠的就是那种淡淡的味道，看起来很淡，一回味却很浓，太狠了。</p>
<p>&nbsp;</p>
<p>3 . 《这该死的青春》&mdash;&mdash;女版《梦里花落知多少》，更贫，更悍。</p>
<p>太贫了，女生写的青春小说，写得比男的还剽悍，汗，汗，越读越汗，汗水涟涟&hellip;&hellip;简直是塑造了一代剽悍女、豪放女、幽怨女和未来剩女呀。绝对是一部以剽悍为精神，以八卦为血肉，以幽默为口水的超级奇文。莫非现在的80后新女性已经翻身做主人到了如此情状了吗？而且文中体现了强势未来剩女对男人的超级征服精神&mdash;&mdash;如同励志大片。当上个世纪80年代处于青春期的女性在写《那一曲军校恋歌》那样理想纯净之文时，当时出生的现在的强悍女未来的坚挺剩女在跺脚诅咒《这该死的青春》&mdash;&mdash;该死你还写得这么津津有味！文中超级语录已成网络经典疯狂流传中，比如：这发展也太光速鸟，我骑着刘翔都撵不上了。</p>
<p>&nbsp;</p>
<p>4 . 《大漠苍狼》&mdash;&mdash;挥刀自宫的超强小说</p>
<p>应该算探险小说吧。作者很神秘，基本不露头。在网上连载了几天就被传疯了。写的是70年代地质勘探的故事，洞穴探险。作者的口气活像个从70年代过来的地质队员，虽然我们明知道他不是，说不定是个80后，这年头，聪明而渊博的人太多了。这个作者设置紧张气氛的能力超强，几乎每个章节都有悬念，抓人得很，要命。而且洞穴小说，以前没有，很新鲜。有好来坞大片编剧的素质。</p>
<p>&nbsp;</p>
<p>5 . 《浮沉》&mdash;&mdash;《输赢》之后的经典商战</p>
<p>这个作者在网上连载的时候把自己搞得很神秘，叫&ldquo;京城洛神&rdquo;，很少透露自己的信息，偶尔透露，让人浮想联翩，以为是微软大中国区的某位美女高管。后来才知道完全不是，大家都上当了。哈哈。不过上美女的当总是值得的。这本书写得细，而且好象很有事实根据，作者对大公司的商业战争很熟，因此写得很激烈。女人写商战，而且还包含了了很强烈的个人奋斗精神，不容易。</p>
<p>&nbsp;</p>
<p>6 . 《广州朝九晚五》&mdash;&mdash;神秘的女猎头小说。</p>
<p>作者真的是个猎头。女猎头。天天狩猎，狩的是人。小说写得也很金领哦，满嘴英文。我一开始读得有点反感&mdash;&mdash;欺负我的二把刀英语。不过确实写得好看。女猎头的生存法则，大公司的人才需求之道，职场生存的经验，白领阶层的情感迷途，都很到位。我的好几个女性朋友大力推荐上榜。 <br />&nbsp; </p>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/211783#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 05 Jul 2008 00:10:15 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/211783</link>
        <guid>http://macrochen.javaeye.com/blog/211783</guid>
      </item>
      <item>
        <title>在云南做项目的日子---东川红土地之行(多图)</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/203916" style="color:red;">http://macrochen.javaeye.com/blog/203916</a>&nbsp;
          发表时间: 2008年06月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>又到了周末, 这周项目没什么大的问题, 不用加班, 于是开始google云南昆明附近适合两天骑行的好去处, 找来找去, 无意中发现了东川, 于是开始狂收集相关的资料, 周六一大早开始了我的东川红土地之行.... <br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_00602f4dcda6335.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_00602f4dcda6335.jpg');" alt="" /><br /></p>
<p>&nbsp;</p>
<p>早上七点的样子起床, 昆明东南西北各有一个汽车车站, 网上说有的车站有的说有到东川的班车,有的说没有, 又找了一个北站车站的电话确认了一下北站汽车站, 说有8:30和9:30到东川的班车, 确认了一下到北站的路线, 开路...<br /><strong>到达北站汽车站</strong></p>
<p><img src="http://www.wecycling.com/attachment/Day_080615/8_24_9728e900ed382fd.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_9728e900ed382fd.jpg');" alt="" width="700" />&nbsp;</p>
<p>&nbsp;</p>
<p><strong>9:30到北川的大巴</strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_8224d2adbd94dc3.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_8224d2adbd94dc3.jpg');" alt="" width="700" /></p>
<p>&nbsp;</p>
<div class="tpc_content" id="read_153398">路上停车休息, 路边有很多买零食和土特产的小摊, 以土豆居多, 烤土豆, 煎土豆, 炸土豆... <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_28c2bc8362a58ab.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_28c2bc8362a58ab.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_31e858dd75dede4.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_31e858dd75dede4.jpg');" alt="" width="700" />&nbsp;</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div class="tpc_content" id="read_153399">东川的泥石流也是很有名的 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_67e462b4549c2b1.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_67e462b4549c2b1.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153400">东川的南大门 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_5d5fb2870ef9954.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_5d5fb2870ef9954.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153401">到达东川县城之后逛了一圈, 没什么特色, 就是一小县城, 然后去新华书店买一张本地地图, 直接回答没有, 于是又在路上问, 东川最有名的红土地所在地"花石头村"怎么走, 问了两个当地的路人竟然都不知道这个地方, 又问红土地所在, 大致给指定了一下方位, 于是开始出发 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_1b813f5fdc6f7ff.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_1b813f5fdc6f7ff.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153402">一路都是盘山公路, 在高原爬坡是一件非常恐怖的事情, 而且还是折叠, 有崩溃的想法 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_8997745e1ce593d.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_8997745e1ce593d.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153403">在半山腰 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_a2b2028aeab2c3a.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_a2b2028aeab2c3a.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153404">好不容易爬上一座山, 结果面前又是一座, 直接崩溃, 只好在路边等过路的班车, 还好过路的班车比较多, 没多久就来一辆, 不过不是到红土地, 而是一个到叫"乌龙"的地方, 坐到一个三岔路口, 下车, 继续爬坡, 这时距离红土地还有23公里 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_c7cddeb4fb22cce.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_c7cddeb4fb22cce.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153405">到了半山腰, 下面就是那个叫"乌龙"的小镇 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_800e556888e795b.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_800e556888e795b.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153406">盘山公路虽然不陡, 但是不比平原, 非常累, 其中一段直接扛车从一条土路直线上去 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_7c9b378d8978040.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_7c9b378d8978040.jpg');" height="700" alt="" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153407">过路车比较少, 路也不错, 直接在路中间休息 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_a3019bb1f123319.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_a3019bb1f123319.jpg');" height="700" alt="" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153408">还只是半山腰, 这是山的另一边, 有一条土路通向一个山寨 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_d11ebf8863cccc9.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_d11ebf8863cccc9.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153409">盘山公路 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_0c77d8b45ce17ba.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_0c77d8b45ce17ba.jpg');" alt="" width="700" /></div>
<div class="tpc_content">从山的一边盘旋到另一边, 下面也是一个村子, 现在地势已经非常高了 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_4b536f170e0c7e1.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_4b536f170e0c7e1.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153412">防迷路, 看地图 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_85eef467baf1a71.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_85eef467baf1a71.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153413">山脚下又一个村子 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_a15a1d8801d38da.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_a15a1d8801d38da.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153414">一个非常不错的景点, 不过当时天气很差, 能见度低, 排不出pp效果很不理想 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_940340767de1539.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_940340767de1539.jpg');" height="700" alt="" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153416">到月亮田, 已是"弹尽粮绝", 身上的一壶水已经喝光, 身上也没了吃的, 人困车乏, 只好又等过路的班车, 一直坐到目的地:花石头村 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_e50cf6e060cbf48.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_e50cf6e060cbf48.jpg');" height="700" alt="" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153418">在车上拍的 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_8a2f10907066dc4.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_8a2f10907066dc4.jpg');" alt="" width="700" />&nbsp; <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_b38c270ae73b630.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_b38c270ae73b630.jpg');" alt="" width="700" />&nbsp; <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_a38a4c86b020e7a.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_a38a4c86b020e7a.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153419">村子的人比较少, 一般都是聚居 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_6a0bc91034b3144.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_6a0bc91034b3144.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153420">从另一个角度拍 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_78818328bfd6e8d.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_78818328bfd6e8d.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153423">等待丰收的麦子 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_4d96838418c2332.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_4d96838418c2332.jpg');" height="700" alt="" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153424">继续向山顶进发, 在村子买了一点水和饼干, 这点小坡已经不在话下<br />在买饼干的时候比较搞笑, 本来也打算买绿茶的, 但是一看日期还是去年的存货, 放弃.<br />后来秤饼干, 11块一斤, 买了6个, 三两, 我问多少钱, 店主直接了当的告诉我, 她不会算帐, 不知道多少钱, 让我看着给 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_f9221ca6dfa866f.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_f9221ca6dfa866f.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153427">自拍一张 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_ba6450e118a8ad2.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_ba6450e118a8ad2.jpg');" alt="" width="700" /></div>
<div class="tpc_content"><img src="http://www.wecycling.com/attachment/Day_080615/8_24_346b41ce49ca632.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_346b41ce49ca632.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153428">红土地的精华在花石头村, 花石头的精华在锦绣园 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_ea8855ac4344eb3.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_ea8855ac4344eb3.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153429">再留影一张 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_d1d3d86a0c3c7c7.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_d1d3d86a0c3c7c7.jpg');" alt="" width="700" /></div>
<div class="tpc_content"><img src="http://www.wecycling.com/attachment/Day_080615/8_24_941d7c447baa77c.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_941d7c447baa77c.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153431">锦绣园全景, 不知是大自然的杰作, 还是人类的伟大 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_3c01bef17bca5d5.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_3c01bef17bca5d5.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_1514ea1169bb708.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_1514ea1169bb708.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_6e7c1d0208976ad.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_6e7c1d0208976ad.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_19917faeb33814b.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_19917faeb33814b.jpg');" alt="" width="700" />&nbsp; &nbsp;</div>
<div class="tpc_content"><img src="http://www.wecycling.com/attachment/Day_080615/8_24_6221b12a3112365.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_6221b12a3112365.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153435">七彩坡, 另一个景点, 其实跟锦绣园看到的是同一个地方, 只是换了一个角度而已 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_41f16480d0094f9.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_41f16480d0094f9.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_6946d82352ce7da.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_6946d82352ce7da.jpg');" alt="" width="700" />&nbsp;</div>
<div class="tpc_content">
<div class="tpc_content" id="read_153437">红土地, 一个谋杀菲林的地方 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_efe0d1234844a74.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_efe0d1234844a74.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153438">住宿的地方, 张开权影友之家<br />在网上搜索花石头住宿的地方, 推荐的都是张开权家开的这家旅店, 不过价格已经不是网上的30, 刚好碰到一个澳门的影友, 他住了一个标间, 于是商量share一下, 人均价格30的样子, 晚饭跟老板一起吃, 都是一般的农家饭, 加早饭一共15块, 还算便宜. <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_d368d18516fd98e.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_d368d18516fd98e.jpg');" alt="" width="700" /></div>
<div class="tpc_content" id="read_153439">因为知名度大, 影友之家还是比较好找的, 差不多应该算村里最好的房子, 因此可以判断张开权可能已经是村子里面的"首富"了 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_8a81445db1d57c5.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_8a81445db1d57c5.jpg');" height="700" alt="" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_1ce4fadfcabfce9.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_1ce4fadfcabfce9.jpg');" height="700" alt="" />&nbsp;</div>
<div class="tpc_content">
<div class="tpc_content" id="read_153440">老板ms也是玩儿摄影的, 里面挂有不少的pp和相册 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_bf5ee27441ae32f.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_bf5ee27441ae32f.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_ea264ce1a159d49.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_ea264ce1a159d49.jpg');" alt="" width="700" />&nbsp;</div>
<div class="tpc_content">
<div class="tpc_content" id="read_153441">第二日, 六点起床, 准备上山去拍日出, 但是开始下雨, 雾气比较大 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_c71b562bd417849.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_c71b562bd417849.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153442">雨越下越大, 能见度大概就100, 200米的样子, 别说看日出了, 连梯田都没法看清&nbsp;<strong></strong><br /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_73d0b6ef6e3dee3.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_73d0b6ef6e3dee3.jpg');" alt="" width="700" />&nbsp;</div>
<div class="tpc_content">
<div class="tpc_content" id="read_153444">因为下雨, 而回昆明的班车一天只有一趟, 大概八点半, 九点的样子经过这里, 所以让老板联系了一下班车司机,九点的样子, 果然一辆中巴停在门口, 于是跟澳门的影友互留了联系方式,就此分别. <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_3ace7f1733fdf72.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_3ace7f1733fdf72.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153445">回城路上随拍 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_fda5918e54963b9.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_fda5918e54963b9.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_4872616034f6305.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_4872616034f6305.jpg');" alt="" width="700" /> <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_77c67807f6e5ab1.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_77c67807f6e5ab1.jpg');" alt="" width="700" />&nbsp; &nbsp; <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_5d0355a83f33228.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_5d0355a83f33228.jpg');" alt="" width="700" /></div>
<div class="tpc_content">
<div class="tpc_content" id="read_153446">中途停车休息, 路边做生意的, 烤土豆, 煮鸡蛋</div>
<div class="tpc_content"><img src="http://www.wecycling.com/attachment/Day_080615/8_24_b29201e1d5fc239.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_b29201e1d5fc239.jpg');" alt="" width="700" />&nbsp;</div>
<div class="tpc_content">
<div class="tpc_content" id="read_153447">云南人的吃法, 土豆蘸辣椒面 <strong></strong><br /><img src="http://www.wecycling.com/attachment/Day_080615/8_24_39593ccef8ef8f9.jpg" border="0" onclick="if(this.width&gt;=700) window.open('http://www.wecycling.com/attachment/Day_080615/8_24_39593ccef8ef8f9.jpg');" height="700" alt="" /></div>
<div class="tpc_content">下午2点左右回到昆明, 下起了暴雨, 狂奔回家, 两天的东川之行完美结束<br />路程:去156km高速＋40km盘山路, 回109km山路<br />花费:去40￥（大巴车费）＋5￥＋6￥（中途班车费）＋30￥住宿＋15￥（晚、早餐）＋32￥（回程班车费）＝122￥</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/203916#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 16 Jun 2008 02:11:14 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/203916</link>
        <guid>http://macrochen.javaeye.com/blog/203916</guid>
      </item>
      <item>
        <title>Dorado重用最佳实践</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/203117" style="color:red;">http://macrochen.javaeye.com/blog/203117</a>&nbsp;
          发表时间: 2008年06月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在实际开发中我们都会面临组件重用的问题, 面向对象的语言可以通过各种设计模式来实现重用.那么在dorado框架中如何进行重用呢? 对于dorado的重用问题,下面我们从三个方面来进行说明.<br /><strong>1.通过使用jsp:include指令进行重用</strong><br />将要重用的内容写到一个jsp页面中, 然后通过jsp:include标记在需要使用的位置进行重用, 该jsp中可再引用d:view标签,比如定义一个jsp页面:<br /><pre name="code" class="html">
&lt;%@ page contentType="text/html; charset=UTF-8"%>
&lt;%@ taglib uri="http://www.bstek.com/dorado" prefix="d"%>
&lt;d:View config="com.baiyao.workflow.component.ChargeType">
	&lt;d:SubWindow id="winChargeType" title="选择类型" width="300" height="200"
		draggable="true" resizable="true" status="hidden"
		showMinimizeButton="false" showMaximizeButton="false"
		showCloseButton="true">
		&lt;d:Layout type="border" width="100%" height="100%">
			&lt;d:Pane position="center">
				&lt;d:DataTable id="tblType" />
			&lt;/d:Pane>
			&lt;d:Pane position="bottom" align="center">
				&lt;d:Button id="btnOK" />
			&lt;/d:Pane>
		&lt;/d:Layout>
	&lt;/d:SubWindow>
&lt;/d:View>
</pre><br /><br />然后在另外一个页面中进行引入:<br /><pre name="code" class="html">
&lt;d:View config="com.baiyao.workflow.charge.ChargeInput">
	&lt;d:AutoForm id="frmForm">
		&lt;d:FormElementRenderer group="group1" element="render1">
			&lt;d:Layout type="border">
				&lt;d:Pane position="center">
					&lt;d:DataTable id="tblFormItem" />
				&lt;/d:Pane>
				&lt;d:Pane position="bottom" align="center">
					&lt;d:Button id="btnAdd" />&nbsp;&nbsp;
					&lt;d:Button id="btnDelete" />
				&lt;/d:Pane>
			&lt;/d:Layout>
		&lt;/d:FormElementRenderer>
	&lt;/d:AutoForm>
	&lt;jsp:include page="/workflow/component/win_charge_type.jsp" />
&lt;/d:View>
</pre><br />该重用方式比较原始, 主要是利用了jsp:include指令, 会编写jsp的人都会用, 相对来说还是非常简单的. 比较适合组件在页面中的布局中规中矩的情况, 比如实现某一类功能的组件都集中在页面中的某一区域.如果某一类功能的组件在页面中的分布跨度比较大的话, 则会因为没法封装在一个jsp中而无法实现重用.由于重用范围仅限于jsp页面中, 因此其维护性还是非常不错的.但是如果对单个简单组件专门写一个jsp从而在多个jsp页面重用显然有些得不偿失.而且如果对于page1页面中适用到了conrol1,conrol2,conrol3几个组件, 同样在page2页面中也有这3个组件, 大部分属性都差不多, 但是只有少数几个属性不同, 那么我们只能对jsp进行重用, 而jsp对应的view.xml文件则需要写两个. 因此重用性会大一定的折扣<br />易用程度:★★★★★<br />适用范围:★★★☆☆<br />重用粒度:★★☆☆☆<br />可维护性:★★★★★<br /><br /><strong>2.对view组件定义进行重用</strong><br />view重用分为两种, 一种是通过调用Server API来创建客户端所需要的组件, 另外一种就是将多个view.xml中定义的组件进行组合<br /><br /><strong>2.1.通过调用Server API来创建客户端所需要的组件</strong><br />通过调用Server API使用java代码在后台来来创建页面所需要的各种Control和Dataset, 而不是在view.xml文件中定义需要的control和dataset, 因为采用这种方式就可以通过利用java的OO特性(封装, 继承和多态), 比如一个保存按钮, 可能在一个涉及到编辑操作的页面中都会用到, 因此我们可以创建一个ViewModel基类,在基类的initControls()方法中通过new Button()的方式来创建该按钮, 然后指定按钮的属性, 有时间的还要加上事件的javascript脚本, 这样所有从该基类继承的ViewModel所对应的view所在的页面都可以适用该按钮了.<br /><pre name="code" class="java">
@Override
protected void initControls() throws Exception {
	Button btnSave = (Button) createControl("Button", "btnSave");
	btnSave.setCommand("cmdSave");
	btnSave.setValue("保存");
	btnSave.setWidth("70");

	UpdateCommand cmdSave= (UpdateCommand) createControl(
			"UpdateCommand", "cmdSave");
	cmdSave.setMethod("save");
	DatasetInfo info = cmdSave.addDatasetInfo("dsEntity");
	info.setSubmitScope(DatasetInfo.ALL_CHANGE);
	return btnSave;
}
</pre><br /><br />该重用方式实际上就是将在view.xml配置文件中定义的control或者dataset通过java代码来实现, 实际上是换了一种写法而已, 但是相对于配置文件中的定义来说, 不够直观, 而且要写更多java的代码, 如果该该组件比较复杂的话, 比如写有复杂的事件代码, 则需要去拼javascript字符串脚本, 这样维护性将非常差.但是这种方式的重用粒度非常细, 比如可以只对一个button中的某几个属性进行重用. 而且对于基类定义的组件可以根据需要进行重载, 这样灵活性将非常好.<br />易用程度:★★★☆☆<br />适用范围:★★☆☆☆<br />重用粒度:★★★★★<br />可维护性:★☆☆☆☆<br /><br /><strong>2.2.多个view.xml中定义的组件进行组合重用</strong><br />在前面介绍jsp重用方式的时候, 我们知道是将一个jsp页面分解成多个页面来进行重用, 而这里我们换一个角度: 将一个view.xml根据重用的需要分解成多个view.xml文件, 其实在jsp重用中, 也使用了多个view.xml文件(进行了view.xml的分解), 但是二者的合并时机是不同的, jsp重用是在jsp页面的时候做的view.xml合并, 而这里的合并是在ViewModel初始化中进行的. <br />这里先来介绍一下JSP Dorado Taglib, ViewModel和view.xml之间的关系(仅限个人理解)<br />在dorado的view tag中, 根据给定的view文件所在的位置, 创建ViewModel对象, 该对象中会包含一个ViewModelConfig, 它是对应的view.xml文件的一个解析, 接着进行ViewModel的初始化工作, 初始化就是实际创建组件的过程, 这里面有一些细节我们这里不做研究(比如对于不同的control, 创建的时机是不同的). 最后得到的是一个个组件的Java对象, 我们可以将其看成一个个javabean, jsp页面上的dorado taglib就会根据这些组件对象来生成html或者js脚本. <br />三者之间的关系大致是这样, 接着我们开始探讨将多个view.xml合并的时机, 最开始我采用在生成ViewModel的时候将要组合的其他view.xml引入, 来生成最终我需要的ViewModelConfig对象(在原有基础上添加了其他view.xml中的组件), 后来这种做法失败了(这个过程太多复杂, 比如还涉及到缓存问题, 很容易出现在第一次展现没有问题, 但是在通过command发送ajax请求找不到对应的对象而出错), 于是我在ViewModel初始化(就是init方法)的时候将要组合的view.xml引入进去, 这时候成功了, 没有出现问题.可能dorado原来的设计没有考虑到合并多个view.xml的做法, 因此ViewModel在这一方面还是很封闭的, 很多相关的方法都是private的, 因此需要copy出来<br /><pre name="code" class="java">
public class BaseViewModel extends DefaultViewModel {
	protected static ControlFactory controlFactory;
	protected List&lt;ViewModelConfig> compositedViewModelConfigs;
	protected List&lt;String> compositedConfigNames;
	private int state;

	@Override
	public void init(int state) throws Exception {
		List&lt;String> result = new ArrayList&lt;String>();
		addViewModelConfig(result);

		// 因为在初始化其他viewModelConfig的时候需要使用到状态, 而此时还没有执行super的init方法,
		// 因此sate还是最初的STATE_VIEW状态
		setState(state);

		initCompositedViewModelConfigs(state, result);
		super.init(state);
	}

	protected void setState(int state) {
		this.state = state;
	}

	public int getState() {
		return state;
	}

	/**
	 * 添加需要组合的view.xml文件路径
	 * 
	 * @param result
	 */
	protected void addViewModelConfig(List&lt;String> result) {
		result.add(JbpmConstants.VIEW_TASK_INSTANCE);
	}

	/**
	 * 根据ViewModelConfig初始化创建组件
	 * 
	 * @param state
	 * @param configNames
	 * @throws Exception
	 */
	protected void initCompositedViewModelConfigs(int state,
			List&lt;String> configNames) throws Exception {
		for (String configName : configNames) {
			ViewModelConfig config = getViewModelConfig(configName);

			loadDatasetConfigs(config);
			loadControlConfigs(config);
			if (state == STATE_VIEW) {
				loadEventConfig(DoradoContext.getContext(), config);
			}
		}
	}

	/**
	 * 根据view.xml文件名得到ViewModelConfig对象
	 * 
	 * @param configName
	 * @return
	 * @throws Exception
	 */
	protected ViewModelConfig getViewModelConfig(String configName)
			throws Exception {
		ViewModelConfig config = null;
		if (compositedViewModelConfigs == null) {
			compositedViewModelConfigs = new ArrayList&lt;ViewModelConfig>();
			compositedConfigNames = new ArrayList&lt;String>();
		}
		if (!compositedConfigNames.contains(configName)) {
			ViewModel viewModel = ViewModelManager.getViewModel(null,
					configName, getNamespace(), "request");
			config = viewModel.getConfig();
			compositedViewModelConfigs.add(config);
			compositedConfigNames.add(configName);
		}
		return config;
	}

	@SuppressWarnings("unchecked")
	protected static ControlFactory getControlFactory() {
		if (controlFactory == null)
			try {
				String clazz = Setting.getString("view.controlFactory");
				Class cl = Class.forName(clazz);
				controlFactory = (ControlFactory) cl.newInstance();
			} catch (IllegalAccessException ex) {
				Log.error(ex);
			} catch (InstantiationException ex) {
				if (System.getProperty("java.version").compareTo("1.4") >= 0)
					Log.error(ex.getCause());
				else
					Log.error(ex);
			} catch (ClassNotFoundException ex) {
				Log.error(ex);
			}
		return controlFactory;
	}

	@SuppressWarnings("unchecked")
	protected void loadDatasetConfigs(ViewModelConfig viewModelConfig)
			throws Exception {
		if (viewModelConfig == null)
			return;
		List keys = viewModelConfig.datasetNodes();
		int count = keys.size();
		for (int i = 0; i &lt; count; i++) {
			String id = (String) keys.get(i);
			XmlNode node = viewModelConfig.getDatasetNode(id);
			if (state != 2 && state != 3)
				createDataset(node);
		}

	}

	@SuppressWarnings("unchecked")
	protected void loadControlConfigs(ViewModelConfig config) throws Exception {
		if (config == null)
			return;
		List keys = config.controlNodes();
		int count = keys.size();
		for (int i = 0; i &lt; count; i++) {
			String id = (String) keys.get(i);
			XmlNode node = config.getControlNode(id);
			String type = node.getAttribute("type");
			Class typeClass = getControlFactory().getControlType(type);
			if (typeClass != null) {
				if ((com.bstek.dorado.view.control.Logical.class)
						.isAssignableFrom(typeClass)) {
					createControl(type, id);
					continue;
				}
				if (state == STATE_VIEW
						&& !(com.bstek.dorado.view.control.Placeable.class)
								.isAssignableFrom(typeClass))
					createControl(type, id);
			} else {
				throw new IllegalArgumentException("Unknown control type '"
						+ type + "'!");
			}
		}

	}

	protected void loadEventConfig(DoradoContext context, ViewModelConfig config) {
		if (config == null)
			return;
		XmlNode eventNodes[] = null;
		XmlNode eventsNode = config.getRoot().getChild("Events");
		if (eventsNode != null)
			eventNodes = eventsNode.getChildren();
		if (eventNodes != null) {
			for (int i = 0; i &lt; eventNodes.length; i++) {
				XmlNode eventNode = eventNodes[i];
				String script = XmlConfigUtils.getNodeContent(eventNode,
						context);
				EventHandler event = new EventHandler(eventNode
						.getAttribute("name"), script);
				addEventHandler(event);
			}

		}
	}

	public Control getControl(String id) throws Exception {
		ViewModelConfig config = getConfig();
		Control control = getControl(config, id, true);
		return control;
	}

	/**
	 * 在多个view.xml文件中遍历直到找出要对应的javabean模型数据来创建control
	 * 
	 * @param config
	 * @param id
	 * @param loop
	 *            是否循环查找, 如果是在compositeViewModelConfigs中查找的话应该避免循环查找, 否则会死循环
	 * @return
	 * @throws Exception
	 */
	private Control getControl(ViewModelConfig config, String id, boolean loop)
			throws Exception {
		Control control = (Control) controls.get(id);
		if (control == null && config != null) {
			XmlNode node = config.getControlNode(id);
			if (node != null) {
				String type = node.getAttribute("type");
				control = createControl(type, id);
			} else if (loop) {
				// 注意顺序, 添加组合的view.xml文件的原则是后添加的同id的control或dataset将覆盖前面的
				for (int i = compositedViewModelConfigs.size() - 1; i >= 0; i--) {
					compositedViewModelConfigs.get(i);
					control = getControl(compositedViewModelConfigs.get(i), id,
							false);
					if (control != null) {
						break;
					}
				}
			}
		}
		return control;
	}

	public Control createControl(String type, String id) throws Exception {
		Control control = (Control) controls.get(id);
		if (control == null) {
			control = constructControl(type, id);
			ViewModelConfig config = getConfig();
			if (config != null) {
				initControl(control, config, id);
			}
			controls.forceAdd(id, control);
			initControl(control);
		}
		return control;
	}

	private void initControl(Control control, ViewModelConfig config, String id)
			throws Exception {
		XmlNode node = config.getControlNode(id);
		if (node == null) {
			for (int i = compositedViewModelConfigs.size() - 1; i >= 0; i--) {
				config = compositedViewModelConfigs.get(i);
				node = config.getControlNode(id);
				if (node != null) {
					break;
				}
			}
		}
		control.init(DoradoContext.getContext(), node);
	}

	protected ViewDataset createDataset(String type, String id, XmlNode node)
			throws Exception {
		ViewDataset dataset = constructDataset(type, id);
		DoradoContext context = DoradoContext.getContext();
		if (node != null) {
			dataset.init(context, node);
			if (state == STATE_REPORT)
				dataset.setAutoLoadData(true);
		}
		datasets.forceAdd(id, dataset);
		initDataset(dataset);
		return dataset;
	}

	public ViewDataset createDataset(XmlNode node) throws Exception {
		String type = node.getAttribute("type");
		String id = node.getAttribute("id");
		return createDataset(type, id, node);
	}

	@Override
	public ViewDataset getDataset(String id) {
		// 在当前config中找, 如果找不到, 将在组合config中去找
		ViewDataset dataset = super.getDataset(id);
		if (dataset == null) {
			for (ViewModelConfig config : compositedViewModelConfigs) {
				if (dataset == null && config != null) {
					XmlNode node = config.getDatasetNode(id);
					if (node != null)
						try {
							String type = node.getAttribute("type");
							dataset = createDataset(type, id);
							if (dataset != null)
								break;
						} catch (Exception ex) {
							Log.error(ex);
						}
				}
			}
		}
		return dataset;
	}

	@Override
	public ViewDataset createDataset(String type, String id) throws Exception {
		XmlNode node = null;
		ViewModelConfig config = getConfig();
        if(config != null) {
        	node = config.getDatasetNode(id);
        	if (node == null) {
        		for (ViewModelConfig vmc : compositedViewModelConfigs) {
        			node = vmc.getDatasetNode(id);
        			if (node != null) {
        				break;
        			}
				}
        	}
        }
        return createDataset(type, id, node);
	}
}
</pre><br />该重用方式集成了jsp重用的优点, 又在一定程度上消除了它的缺点, 与jsp重用相比, 其优点在于, 它不会受到组件在页面中的位置布局的影响. 在重用粒度上能对单个的组件进行重用, 相比jsp重用要细, 但是比Server API的重用方式要粗一些. 因为只是配置文件上的重用, 因此主要是对配置文件的维护, 可维护性要比Server API方式要好, 该方式需要使用者对view.xml配置文件, jsp dorado taglib, ViewModel类三者之间的关系有非常好的认识.<br />易用程度:★★☆☆☆<br />适用范围:★★★★☆<br />重用粒度:★★★★☆<br />可维护性:★★★★★<br /><br /><strong>其他重用技巧</strong><br />如果view.xml文件都一样(说明界面一样), 只是ViewModel不同(说明后台业务逻辑不同)的情况下, 我们可以让其公用同一个view.xml, 只是在d:view的配置上加上clazz属性指定二者不同的ViewModel即可.比如这样的写法:<br /><pre name="code" class="html">
&lt;d:View config="com.baiyao.workflow.settlement.SettlementInput"
	clazz="com.baiyao.workflow.settlement.ExpenseSettlementInputViewModel">
	&lt;jsp:include page="/workflow/settlement/settlement_input.jsp" />
&lt;/d:View>
</pre><br /><br /><strong>如何选择</strong><br />其实这几种重用方式互相之间并不矛盾, 可以在一个功能模块中根据需要结合起来适用.但是如果使用太多的重用方式, 会提高项目的复杂程度, 这样就会影响到可维护性, 因此重用也应该适可而止, 否则就是过犹不及.
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/203117#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 13 Jun 2008 21:57:01 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/203117</link>
        <guid>http://macrochen.javaeye.com/blog/203117</guid>
      </item>
      <item>
        <title>圣火传昆明（图）</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/201596" style="color:red;">http://macrochen.javaeye.com/blog/201596</a>&nbsp;
          发表时间: 2008年06月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium">"左盼右盼"今天奥运圣火来到了昆明, 就一个火把本来没什么好看的, 在家呆了一天了, 实在闷得慌, 出去透透气也好, 远的地方也没法去, 就去看看好了, 先在网上google了一下传递线路, 第二天一大早就赶到了出发点昆明体育馆, 发现时间已经过了怎么还没见到火把的影子, 后来一问, 改线路了, 害得好多人还在那里等首发咧, 我也奇怪, 这么大的事情, 怎么就那么几个人, tnnd, 改线路也不提前通知一声, 难道还怕ZD分子出来捣乱不成?后来看到还有好多的群众在哪儿傻等, 而我已是一骑绝尘杀向终点--滇池边上的民族村, 从南到北, 很多人都跟我一样朝一个方向前行, 开车的, 骑车的, 坐车的, 走路的, 大人, 小孩, 男的, 女的, 越靠近滇池, 人越多, 后来密密麻麻的都是人, 因为今天是端午放假的最后一天, 所以大家都休息, 出来的人自然就多了. 在快接近圣火传递的路上将近100米的位置, 已经开始限制自行车进入, 看样子是没机会近距离一睹圣火的风采, 这时候不远处传来一阵雷动, 想必是火炬已经过来了, 没办法去只好折返, 而还有源源不断的人流继续朝向一个方向移动, 想想这些人真傻的, 他们跟我一样基本上是已经没有希望见到圣火的影子了. <br />   等我回到家的时候电视里面正在直播火炬的最后一棒交接, 当我端起饭碗的时候, 圣火传递已经结束了.</span><br /><br />被忽悠的圣火传递起点<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15849/ada0e84d-8670-3ebb-8447-7fb0deea5974.jpg" /><br /><br />除了人还是人<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15845/6126beb7-d1a8-368c-9f7f-d14f6d15fb40.jpg " /><br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15843/234dd554-9381-3613-8831-414523b9db90.jpg" /><br /><br />盛装迎圣火<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15841/3591a74a-e3ad-33cc-ba2c-2ceaef167a39.jpg" />
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/201596#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 09 Jun 2008 23:00:34 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/201596</link>
        <guid>http://macrochen.javaeye.com/blog/201596</guid>
      </item>
      <item>
        <title>六月杭州行</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/201583" style="color:red;">http://macrochen.javaeye.com/blog/201583</a>&nbsp;
          发表时间: 2008年06月09日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: medium">     上周四去杭州, 三折的机票是早就定好了的, 中午出发, 给朋友买了一包十八怪做为礼物, 匆匆上路, 外面下起了雨,  两点的飞机, 十二点就出发, 生怕坐错车, 还在baidu地图上查了一下线路, 可还是被站牌给忽悠了, 因为人民路改造, 机场的巴士已改道, 好不容易找到站牌, 明明写的有这趟车, 等了好久还没来, 后来看到来一辆缓缓的开过来, 快到跟前突然一个拐弯进入另外一条马路, 根据我的判断这趟车可能该线了, 公交公司也太烂了, 明明车不从这边走, 还挂个站牌说有这趟车, 这不是害人么, 恨不得给砸了,nnd.<br /><br />    到了机场, 时间刚刚好, 拿登机牌, 安检非常的顺利. 四点的样子到桂林转机晚上七点才到杭州,坐机场大巴横穿整个杭州市区, 空气感觉非常的闷热, 而此时的昆明还需要穿外套, 可见昆明春城的名号绝非徒有虚名. 杭州的灯光城市建筑昆明是没法比的. 又有一种回到了大都市感觉.到目的地先跟TY打电话, 因为他不知道我这么晚才到, 所以先吃饭了, 还好我在飞机上也吃过了, 现在没有饿的感觉, 于是将东西放到预订的酒店, 打电话跟LH, 然后去他那里, TY顺便过去搬东西, 因为TY远在绵阳的父母因为地震也过来避一避, 还有他的小侄子也在这边一起过来这边找了一个学校借读.最后我跟LH顺便找了一个饭馆点了一点东西边吃边聊了一些技术上的东东.<br /><br />    第二天的事情进行的还是非常的顺利, 准备的还算比较充分, 虽然我一直以为还有很多东西不是很熟悉, 中午的时候就在TY的公司吃饭. 下午拿了TY的GANIT本来打算到西湖逛逛的, 后来发现外面的天气异常闷热, 于是就去LH的家里上网, 晚上LH回来我们一起去附近的外婆家吃饭, 据说在杭州乃至整个浙江都非常有名, 菜出奇的便宜, 味道也不错, 客人几乎都是爆满, 而且还有n多人拿号排队等候, 一进去发现一楼已经有大概一二十人在排队等候, 等了差不多半个多小时终于轮到我们, 价格果然便宜, 味道还是不错地说, 吃饭的过程中, 他给我讲了一些他们公司的事情, 这些东西都是我没有经历过的, 也算是见了一回广, 大概九点多的时候我们才离开, 刚好TY从LH哪儿搬出来, 所以正好多了一个房间, 我可以住一晚, 他凌晨2点要发布系统,所以就回公司去了, 我也没什么事儿, 洗洗就睡了.<br /><br />    第二天起来发现LH还没有回来, 肯定系统发布不是很顺利, 通宵了. 等了一会儿还没回来, 于是给他发了一条短信, 就自个儿骑GANIT出去逛了, 杭州也来过好多次了, 西湖也去过好几次, ms就龙井村还没去过, 于是在网上找了一下线路, 然后就向龙井村出发了, 那个天真的热啊, 完全和昆明没法比, 再一次想起了昆明的好来, 昆明的太阳虽然也毒, 但是空气干燥, 就是热, 一阵风一吹身上的汗就干了, 而杭州不一行,是一种闷热, 没多久就是汗流浃背, 衣服都贴在身上, 让人感觉非常的不舒服.龙井村也没什么特别就是树多点, 人少点, 相对城区凉快点, 茶园没有想想的多, 路边很少看到人家, 很多都开农家乐了, 当然这个对我没什么兴趣, 好不容易爬上山, 后来找到另外一条路俯冲而下, 开始进城, 中午随便在找到一个地方吃罢饭,天气开始有变, 突然下起了雨, 而且越下越大, 变成暴雨, 还好有一段路是高架路, 人在下面骑还可以避雨, 没多久雨停了, 立马飞奔骑回去, 中途小小的湿身一把.<br /><br />    下午五点, LH才回家, 果然系统发布遇到了一些问题, 今天在公司忙了一天, 我也得离开赶七点多的飞机,顺便他也要出去吃今天的第一顿饭, 就此作别, 晚上十二点终于回到昆明, 由于已是深夜, 凉的很, 跟杭州相比完全是冰火两重天. </span><br /><br />TY的GANIT<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15835/d178c2dc-6762-3f05-a082-6e9334b6ba77.jpg" /><br /><br />穿过杭州市区<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15837/8aafa78c-8ebd-37e8-8442-3de6059a45e3.jpg" /><br /><br />路过淘宝<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15839/ed3f16d3-5530-31bf-8a85-375c39600949.jpg" /><br /><br />来到龙井村<br /><img src="http://macrochen.javaeye.com/upload/picture/pic/15833/e3f7eb21-dcdb-3b72-bf87-970dfb3c2010.jpg" />
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/201583#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 09 Jun 2008 22:33:08 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/201583</link>
        <guid>http://macrochen.javaeye.com/blog/201583</guid>
      </item>
      <item>
        <title>如何将jbpm流程模型与业务模型结合的方式</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/200450" style="color:red;">http://macrochen.javaeye.com/blog/200450</a>&nbsp;
          发表时间: 2008年06月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          谋划中...
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/200450#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 05 Jun 2008 11:37:58 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/200450</link>
        <guid>http://macrochen.javaeye.com/blog/200450</guid>
      </item>
      <item>
        <title>使用JbpmContextFilter和手工创建关闭JbpmContext的选择</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/200449" style="color:red;">http://macrochen.javaeye.com/blog/200449</a>&nbsp;
          发表时间: 2008年06月05日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          谋划中...
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/200449#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 05 Jun 2008 11:37:15 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/200449</link>
        <guid>http://macrochen.javaeye.com/blog/200449</guid>
      </item>
      <item>
        <title>jBPM中的变量浅析</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/200072" style="color:red;">http://macrochen.javaeye.com/blog/200072</a>&nbsp;
          发表时间: 2008年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在jbpm主要有两类变量, 一种是流程实例范围的变量, 另一种是任务实例范围的变量. 而在流程实例氛围中的变量又分为两种:一种是瞬时变量(transient variable), 另一种是普通的变量, 瞬时变量通过setTransientVariable()方法来设置, 该变量不会持久化到jbpm_variableinstance表中, 它只能在当前流程实例的整个生命周期中存在, 一旦流程实例结束之后, 该变量将不复存在. 而另一种是通过setVariable()和setVariableLocally()方法进行保存,通过getVariable()访问的变量, 这种变量是在整个流程实例中都可访问的, 因为它会保存到jbpm_variableinstance表中. <br /><br />流程实例变量还是很好理解的, 任务实例变量则相对复杂一些. <br />任务实例变量还可以直接访问实例变量, 这可以按照java程序中变量的作用范围来理解, 任务实例变量相当于局部变量, 仅在当前流程实例中可见, 流程实例变量相当于全局变量, 在整个流程中可见. 当通过任务实例查找一个流程时, 如果在当前的任务实例中没有找到, 将继续到流程实例中去找. 比如通过ContextInstance.setVariable("foo", "foo")创建的流程实例变量, 那么任务实例可以直接通过TaskInstance.getVariable("foo")访问到.<br /><br />而如果要在TaskInstance中修改流程实例变量, 则需要通过Task Controller. 在定义流程文件的时候, 我们会在task节点下使用这样的配置:<br /><pre name="code" class="xml">&lt;controller>
	&lt;variable access="read,required" name="foo">&lt;/variable>
&lt;/controller></pre> <br />Task Controller会在创建一个任务实例时, 根据Controller下配置的variable, 先找是否存在同名的流程实例变量, 如果存在, 那么将用流程实例变量的值来创建属于当前任务实例变量, 在任务结束的时候, 将任务实例变量的值保存到同名的流程实例变量中.如果在流程实例中不存在同名的流程变量, 那么会创建一个同名的流程变量, 比如上面的配置, 将同时创建一个名为foo的流程实例变量和任务实例变量.如果指定了mapped-name属性, 那么name指的是流程实例变量名, mapped-name指的是任务实例变量名,比如: <br /><pre name="code" class="xml">&lt;controller>
	&lt;variable access="read,required" name="foo" mapped-name = "bar">&lt;/variable>
&lt;/controller></pre> <br />该配置将在任务实例变量bar和流程实例变量foo之间建立映射关系, 变量值将在这两个变量之间传递.至于access属性, 我自己试验了一下, 设置为read, required, write不同的组合值好像没有什么区别.<br /><br />流程实例变量的设置也有一些要注意的地方, 如果变量没有在task controller中作映射, 当使用TaskInstance.setVariable("foo", "bar")的时候, 则保存到jbpm_variableinstance表中的taskinstance_字段是空的, 也就是只能做为流程实例变量访问, 而无法通过任务实例变量访问到, 如何才能通过任务实例来访问该变量呢? 回答是使用TaskInstance.setVariableLocally("foo", "bar")来设置流程实例变量, 这样保存到jbpm_variableinstance表会同时任务实例id设置为taskinstance_字段的值, 同时它还创建了一个同名的流程实例变量(不知道jbpm为什么要这么做).<br /><br />其实关于变量的内容还有很多, 目前的项目中还没有碰到, 这里不做一一分析.
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/200072#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 04 Jun 2008 13:51:31 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/200072</link>
        <guid>http://macrochen.javaeye.com/blog/200072</guid>
      </item>
      <item>
        <title>jBPM中JbpmContext.close()一个值得注意的问题</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/199590" style="color:red;">http://macrochen.javaeye.com/blog/199590</a>&nbsp;
          发表时间: 2008年06月03日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在使用jBPM做开发的过程中, JbpmContextFilter 是一个非常方便的过滤器, 从源代码中我们可以看到:<br /><pre name="code" class="java">  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    String actorId = null;

    // see if we can get the authenticated swimlaneActorId
    if (servletRequest instanceof HttpServletRequest) {
      HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
      Principal userPrincipal = httpServletRequest.getUserPrincipal();
      if (userPrincipal != null) {
        actorId = userPrincipal.getName();
      }
    }

    JbpmContext jbpmContext = getJbpmConfiguration().createJbpmContext(jbpmContextName);
    try {
      if (isAuthenticationEnabled) {
        jbpmContext.setActorId(actorId);
      }
      filterChain.doFilter(servletRequest, servletResponse);
    } finally {
      jbpmContext.close();
    }
  }
</pre><br />它主要做了两个事情: 一个是在流程继续执行之前为我们创建了一个JbpmContext; 另一个工作就是流程执行之后完成jbpmContext的关闭. 而如果不使用这个Filter的话, 那么在我们的流程处理中,就需要自己做这两件事. 接着我们再看org.jbpm.JbpmContext.close()的代码:<br /><pre name="code" class="java">public void close() {
    log.debug("closing jbpmContext " + toString());
    try {
      if (services!=null) {
        try {
          autoSave();
        } finally {
          services.close();
        }
      }
    } finally {
      if (jbpmConfiguration!=null) {
        jbpmConfiguration.jbpmContextClosed(this);
      }
    }
  }
</pre><br />在关闭之前都会执行自动保存.<br />从上面的分析我们可以看到, jbpm似乎没有提供我们处理流程内部异常的地方,它不管是否出现异常, 都会将我们的流程操作节点持久化到对应的jbpm数据库表中.本人就因为这个原因导致流程出现问题, 在我的一个流程运行过程中, 在某个审核步骤完成, 进入下一个task node的时候, 该Task节点的AssignmentHandler不幸抛出了异常, 按照我们的理解, 流程应该停留在前一步审核阶段, 但实际情况是审核节点已经被设置成完成状态, 而下一个task node的taskInstance也已经持久化到jbpm_taskinstance表中(只是处于未完成状态), 因为是在assign的时候出现的问题, 因此这个新创建的任务节点将成为一个无人处理的节点, 所有人都无法见不到它了. <br />出现上面的情况一点也不奇怪, 因为在TaskMgmtInstance的createTaskInstance方法中中有这样的代码:<br /><pre name="code" class="java">// create the task instance
taskInstance.create(executionContext);

// if this task instance is created for a task, perform assignment
if (task!=null) {
  taskInstance.assign(executionContext);
}
</pre><br />它先创建taskInstance, 完成taskInstance的属性设置, 然后执行assign动作(异常就出现在这里), 在出现异常之后程序会回到JbpmContextFilter中,然后执行JbpmContext的close方法(该方法中完成所有新建和修改对象的持久化工作).<br /><br />为了解决这个问题,就需要我们自己来处理jbpm引擎内部抛出的异常, 这一点jbpm做的非常好, 因为jpbm抛出的异常都是继承自JbpmException, 所以我们只要捕获该异常就可以了.而我们结束流程操作一般都是出现在这样几个地方:调用TaskInstance.end(), ProcessInstance.signal()以及相关的同名方法, 因此只要在调用这些方法的地方对异常进行捕获就可以了.<br />我将这几个方法放到工具类, 以方便调用:<br /><pre name="code" class="java">
public static void end(TaskInstance ti) {
	end(ti, (Transition) null);
}

public static void end(final TaskInstance ti, final String transitionName) {
	new JbpmOperation() {
		@Override
		void doExecute(JbpmContext ctx) {
			ti.end(transitionName);
		}
	}.execute();
}

public static void end(final TaskInstance ti, final Transition transition) {
	new JbpmOperation() {
		@Override
		void doExecute(JbpmContext ctx) {
			ti.end(transition);
		}
	}.execute();
}

public static void signal(final ProcessInstance pi) {
	new JbpmOperation() {
		@Override
		void doExecute(JbpmContext ctx) {
			pi.signal();
		}
	}.execute();
}
</pre><br /><br />然后定义了一个回调的操作类用来处理在遇到JbpmException异常的时候告诉JbpmContext进行rollback:<br /><pre name="code" class="java">
/**
 * 用来处理流程异常, 如果流程本身出现异常(这些异常可能来自我们定义的各种assignmentHandler, actionHandler,
 * decisionHandler等),通过设置rollbackOnly来阻止其持久化
 * 
 * @author Macro Chen
 * @since Jun 3, 2008
 */
abstract class JbpmOperation {
	void execute() {
		JbpmContext ctx = JbpmUtils.getJbpmContext();
		try {
			doExecute(ctx);
		} catch (JbpmException e) {
			// 通过该方法让ctx提交的时候rollback
			ctx.setRollbackOnly();
			throw e;
		} finally {
			// 在filter中close
			// ctx.close();
		}
	}

	abstract void doExecute(JbpmContext ctx);

}
</pre>
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/199590#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 03 Jun 2008 01:49:56 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/199590</link>
        <guid>http://macrochen.javaeye.com/blog/199590</guid>
      </item>
      <item>
        <title>一次部署多个jbpm 流程定义文件</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/198736" style="color:red;">http://macrochen.javaeye.com/blog/198736</a>&nbsp;
          发表时间: 2008年05月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          通常我们在部署流程定义文件都需要借助jbpm的eclipse plugin,但是该插件有一个问题,就是每次打开一个流程文件非常的慢,而且一次只能deploy一个流程定义文件,如果有多个流程的话,就需要我们在插件中同时打开多个流程定义文件,这让人感觉非常麻烦.于是我们对流程部署进行了一下改进.<br />在插件中部署一个流程定义,其实就是将processdefinition.xml文件上传到一个指定的servlet,这个servlet就是org.jbpm.web.ProcessUploadServlet, 而对应的url地址是web.xml中定义的:<br /><pre name="code" class="xml">
&lt;servlet>
	&lt;servlet-name>UploadServlet&lt;/servlet-name>
	&lt;servlet-class>
		org.jbpm.web.ProcessUploadServlet
	&lt;/servlet-class>
&lt;/servlet>
&lt;servlet-mapping>
	&lt;servlet-name>UploadServlet&lt;/servlet-name>
	&lt;url-pattern>/upload&lt;/url-pattern>
&lt;/servlet-mapping>
</pre><br />因此我们只需要自己写一个jsp页面,将要上传的文件提交到该url(http://myhost:myport/myapp/upload)即可.<br />通过参看ProcessUploadServlet的源代码,我们发现,它要求上传的文件必须是经过压缩的:<br /><pre name="code" class="java">
    if (fileItem.getContentType().indexOf("application/x-zip-compressed") == -1) {
	log.debug("Not a process archive");
	return "Not a process archive";
    }
    try {
	log.debug("Deploying process archive " + fileItem.getName());
	ZipInputStream zipInputStream = new ZipInputStream(fileItem.getInputStream());
	JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
	log.debug("Preparing to parse process archive");
	ProcessDefinition processDefinition = ProcessDefinition.parseParZipInputStream(zipInputStream);
	log.debug("Created a processdefinition : " + processDefinition.getName());
	jbpmContext.deployProcessDefinition(processDefinition);
	zipInputStream.close();
	return "Deployed archive " + processDefinition.getName() + " successfully";
    } catch (IOException e) {
	return "IOException";
    }
</pre><br />也就是说,我们在上传流程定义文件之间必须将其压缩成zip文件(而且还不能嵌套其他的文件夹,zip里只能包含processdefinition.xml), 因此我们需要对其进行改造一下,能让我们直接上传流程定义xml文件:<br /><pre name="code" class="java">
private String handleRequest(HttpServletRequest request) {
        //check if request is multipart content
        log.debug("Handling upload request");
        if (!FileUpload.isMultipartContent(request)) {
            log.debug("Not a multipart request");
            return "Not a multipart request";
        }
        String result = "";
        try {
            DiskFileUpload fileUpload = new DiskFileUpload();
            List list = fileUpload.parseRequest(request);
            log.debug("Upload from GPD");
           
            for (Iterator it = list.iterator(); it.hasNext();) {
				FileItem fileItem = (FileItem) it.next();

	            if (fileItem.getContentType() == null || fileItem.getContentType().indexOf("text/xml") == -1) {
	                log.debug("Not a process archive");
	                continue;
	            }
	            try {
	                log.debug("Deploying process archive " + fileItem.getName());
	                InputStream is = fileItem.getInputStream();
	                JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
	                log.debug("Preparing to parse process archive");
	                ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(is);
	                log.debug("Created a processdefinition : " + processDefinition.getName());
	                jbpmContext.deployProcessDefinition(processDefinition);
	                is.close();
	                if (StringUtils.isNotEmpty(result)) {
	                	result += ",";
	                }
	                result += processDefinition.getName();
	            } catch (IOException e) {
	                return "IOException";
	            }
			}
        } catch (FileUploadException e) {
            e.printStackTrace();
            return "FileUploadException";
        }
        
        if (StringUtils.isEmpty(result)) {
        	log.debug("No process file in the request");
            return "No process file in the request";
        }
        
		return "Deployed process [" + result + "] successfully";
    }
</pre><br /><br />接下来是写jsp文件:<br /><pre name="code" class="html">
&lt;%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
&lt;html>
	&lt;head>
		&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		&lt;title>Upload Process Definition Xml files&lt;/title>
		&lt;script src="upload.js" type="text/javascript">&lt;/script>
		&lt;style>
fieldset {
	border: 1px solid #eee;
	padding: 5px 10px;
	margin: 0;
}

fieldset ul {
	list-style: none;
	margin: 0 0 1.5em 0;
	padding: 0;
}

fieldset ul li {
	list-style: none;
	margin: 0 0 0.5em 0;
	padding: 0;
}
&lt;/style>
	&lt;/head>
	&lt;body>
	&lt;body>
		&lt;form action="&lt;%=request.getContextPath()%>/upload" enctype="multipart/form-data"
			id="process_form" method="post">
			&lt;fieldset>
				&lt;ul>
					&lt;li>
						&lt;label>
							新增流程定义文件
						&lt;/label>
						&lt;input type="file" id="process_upload" />
					&lt;/li>
					&lt;li style="text-align: center">
						&lt;input class="submit" id="submit_button" name="commit"
							type="submit" value="提交" />
					&lt;/li>
				&lt;/ul>
			&lt;/fieldset>
		&lt;/form>
		&lt;script type="text/javascript">
  var multiple_upload_process_counter = 0;	
  multiple_upload_process($("process_upload"));
&lt;/script>
	&lt;/body>
&lt;/html>
</pre><br />这里我借鉴了一下javaeye的application.js来添加多个上传文件:)
          <br/>
          <span style="color:red;">
            <a href="http://macrochen.javaeye.com/blog/198736#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 30 May 2008 16:42:27 +0800</pubDate>
        <link>http://macrochen.javaeye.com/blog/198736</link>
        <guid>http://macrochen.javaeye.com/blog/198736</guid>
      </item>
      <item>
        <title>在云南做项目的日子－－2008-05-24骑行抚仙湖（图）</title>
        <author>macrochen</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://macrochen.javaeye.com">macrochen</a>&nbsp;
          链接：<a href="http://macrochen.javaeye.com/blog/196801" style="color:red;">http://macrochen.javaeye.com/blog/196801</a>&nbsp;
          发表时间: 2008年05月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          周五晚上无事,于是筹划周末的骑行计划,远的没时间去,近的又没什么好去处,最后选中了不远不近的抚仙湖,于是在网上搜索了一堆相关的资料,但是鲜有抚仙湖骑行的资料.于是就这样出发了.<br />周六起床,吃完早饭就朝汽车站奔去.赶上了8点20的一辆中巴,还好去的不是什么热门的旅游圣地,都是一