spring中使用Quartz

网上看到很多Quartz的帖子,说得都很不错,关于他的由来和原理我这里就不累赘了。我主要想解决的事情是一开始我遇到的无法自动启动的问题。

用例子说话。

一、写一个自定义的类,继承至Object就可以了。并且可以配合set方法,用于在spring中注入。

 

public class PoolingSendMail {
       
        public static Logger poolingSendMailLog = Logger.getLogger(PoolingSendMail.class);
       
        private String message;
       
        public void setMessage(String message) {
                this.message = message;
        }

        public void sendMail() {
                System.out.println(message);
        }
}

二、配置文件

在spring的配置文件中,增加下面的配置

        <bean id="poolingSendMail" class="com.wtmec.RFQ.pooling.PoolingSendMail">
                <property name="message">
                        <value>nihao</value>
                </property>
        </bean>
        <bean id="poolingDetail"
                class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

                <property name="targetObject">
                        <ref bean="poolingSendMail"/>
                </property>
                <property name="targetMethod">
            <value>setMessage</value>
        </property>
        </bean>
        <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
                <property name="jobDetail">
                        <ref bean="poolingDetail"/>
                </property>
                <property name="cronExpression">
                        <value>* 5 1 ? * MON-FIR</value>
                </property>
        </bean>
        <bean id ="schedulerFactory" lazy-init="false" autowire="no"
                class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

                <property name="triggers">
                        <list>
                                <ref local="cronTrigger" />
                        </list>
                </property>
                <property name="autoStartup" value="true"/>
        <property name="schedulerName" value="cronScheduler" />
        </bean>

 

经过这样的简单配置就可以让程序在周一到周五的凌晨1点5分的某个时刻执行程序了。(当然这里只是简单的system.out动作而已)

刚开始的时候,一直困扰我的是我在启动tomcat后,无法自动启动定时任务。但是我如果在java中直接获取spring配置文件,并获取我“schedulerFactory” 是可以运行的。

后来问题终于找到。是刚开始的时候,我没有落了几个需要设置的地方

1. bean “schedulerFactory“ 的lazy-init="false”。

2. bean “schedulerFactory“ 的两个属性

          <property name="autoStartup" value="true"/>
 

         <property name="schedulerName" value="cronScheduler" />

完成上面的设置后,程序就可以自动启动了。yeah。give me five。。

struts 1.x 学习 ---持续更新

最后更新:2009-5-12 

1.可以通过在web.xml这配置,统一管理项目中的error显示页面。强…… 

<error-page>
  <error-code>404</error-code>
  <location>/commom/404.jsp</location>
</error-page>

<error-page>
  <error-code>500</error-code>
  <location>/commom/500.jsp</location>
</error-page>

     如果需要对特定类型的error做显示,还可以设定 <exception-type>来达到效果 

<error-page>
  <exception-type>javax.servlet.ServletException</exception-type>
  <location>/commom/system_error.jsp</location>
</error-page>

<error-page>
  <exception-type>java.io.IOException</exception-type>
  <location>/commom/system_ioerror.jsp</location>
</error-page>

 

 

 

oracle 使用心得。持续更新

最后更新日:2009-5-6
  1.  在jdbc中使用sequence,需要在之前先使用query语句得到新的sequence值。例如:


SELECT 'schema'.'sequence's name'.nextval from dual;
--seqNum 就是上面得到的sequence新值
insert into table (id,name) values(seqNum, '
poker');

commit;

 

struts标签的使用

 strusts标签挺好用的,但是在使用过程中可能会遇到一些很莫名的问题。这里罗列一些出来,会持续更新... last update:2009/5/5

  1. 如果想在Struts标签中的 value属性中添加变量,不可以使用struts标签嵌套。因struts不支持标签嵌套,可采用EL来解决该问题。例如: 
<logic:equal name="charge" property="num" value="${business.num}">   
......
</logic:equal>

 

 

 

Philippe Kruchten's 4+1 view model

         在学习OReilly.Learning.UML.2.0.Apr.2006.chm的时候,看到有这部分的知识,不明白,但知道很重要,就上网查了一下。做了汇总。

        以下首先是 4+1 图。。。


Figure 1-1. Philippe Kruchten’s 4+1 view model

        由于Learning.UML中,在这个图下面就是几段文字性的叙述,感觉不够形象,还是心智图比较好用。在网上找的,贴上


Figure 1-2. Diagrams overview (created by FreeMind, http://freemind.sourceforge.net)

 

上面的两幅图片均来至:http://www.hooto.com/home/flyhigh/?cp=blog&op=blog&iid=1330

 

感谢Rui's log。。让我还找到了两个很棒的软件,

        1、Visual Paradigm for UML Community Edition http://www.visual-paradigm.com(制作UML)

        2、FreeMind, http://freemind.sourceforge.net(专门绘制心智图)

 

项目开发模型(Software Development Process)

 第一种:瀑布模型(Waterfall Model)

        发现项目开发中最经常用的开发模型


  所有过程模型的鼻祖。---- Royce,1970 

  瀑布模型把软件开发过程划分成若干阶段,每个阶段的任务相对独立,便于不同人员分工协作,从而降低了整个软件开发工程的困难程度。在软件生存期的每个阶段都采用科学的管理技术和良好的方法与技术,而且每个阶段结束之前,都从技术和管理两个角度进行严格的审查,经确认之后才开始下一阶段的工作。---- 项目是按照一定的顺序执行。

  瀑布模型是文档驱动的,各个阶段不连续也不交叉。 

特点: 

  1.阶段间具有顺序性和依赖性。 

  2.推迟程序的物理实现。 

  3.质量保证:每个阶段必须完成规定的文档;每个阶段结束前完成文档审查,及早改正错误。 

  4.易于组织,易于管理:因为你可以预先完成所有计划。 

  5.是一种严格线性的、按阶段顺序的、逐步细化的过程模型(开发模式) 

适用场合: 

  1.当有一个稳定的产品定义和很容易被理解的技术解决方案时,纯瀑布模型特别合适。 

  2.当你对一个定义得很好的版本进行维护或将一个产品移植到一个新的平台上,瀑布模型也特别合适。 

  3.对于那些容易理解但很复杂的项目,采用纯瀑布模型比较合适,因为可以用顺序方法处理问题。 

  4.在质量需求高于成本需求和进度需求的时候,它尤为出色。 

  5.当开发队伍的技术力量比较弱或者缺乏经验时,瀑布模型更为适合。 

缺陷: 

  1.在项目开始的时候,用户常常难以清楚地给出所有需求;用户与开发人员对需求理解存在差异。 

  2.实际的项目很少按照顺序模型进行。 

  3.缺乏灵活性:因为瀑布模型确定了需求分析的绝对重要性,但是在实践中要想获得完善的需求说明是非常困难的,导致“阻塞状态”。反馈信息慢,开发周期长。 

  4.虽然存在不少缺陷,瀑布模型经常被嘲笑为“旧式的”,但是在需求被很好地理解的情况下,仍然是一种合理的方法。

 

 第二种:迭代模型(terative Model)

 

迭代模型是RUP(Rational Unified Process,统一软件开发过程,统一软件过程)推荐的周期模型。在RUP中,迭代被定义为:迭代包括产生产品发布(稳定、可执行的产品版本)的全部开发活动和要使用该发布必需的所有其他外围元素。所以,在某种程度上,开发迭代是一次完整地经过所有工作流程的过程:(至少包括)需求工作流程、分析设计工作流程、实施工作流程和测试工作流程。
  实质上,它类似小型的瀑布式项目。RUP认为,所有的阶段(需求及其它)都可以细分为迭代。每一次的迭代都会产生一个可以发布的产品,这个产品是最终产品的一个子集。迭代的思想如下图所示。

迭代模型迭代式模型

        从开发人员角度进行的迭代模型如下图:

 

        对众多的开发模型和过程方法,及权威机构的看法,企业应选择什么样的开发模型,应慎重对从以下几方面进行考虑:

  1、RUP虽然内容极其丰富,定义了选起、精化、构建、产品化4个阶段和业务建模、需求、分析设计、实现、测试、部署等9个工种,提供了一大堆的文档模板,但极易让人误解是重型的过程,实施推广有一定难度。

  2、再次,在质量管理方面:以实现系统架构、核心功能目标的迭代产品生的工作成果作为质量控制重点。每次迭代进行系统集成、系统测试,达到对软件质量的持续验证。每次系统测试,需要回归测试前一次迭代遗留发现的问题。每次迭代发布的小版本组织客户(包括内部客户、外部客户)进行评价,通过演示操作等方式,评价该次迭代是否达到预定的目标,并以此为依据来制定下一次迭代的目标。

  3、最后,在其他方面:每次迭代成果须进行配置管理,版本控制很重要。在整个迭代过程中风险无处不在,建议每周作一次风险跟踪。同时通过重点关注进度、工作量、满意度、缺陷等数据收集,关注每次迭代情况。

  总之,选择一个合适的生命周期模型,并应用正确的方法,对于任何软件项目的成功是至关重要。企业在选择开发模型应从项目时间要求、需求明确程度、风险状况等选择合适的生命周期模型。

 

迭代模型- 迭代模型的选择使用条件

  1、在项目开发早期需求可能有所变化。

  2、分析设计人员对应用领域很熟悉。

  3、高风险项目。

  4、用户可不同程度地参与整个项目的开发过程。

  5、使用面向对象的语言或统一建模语言(Unified Modeling Language,UML)。

  6、使用CASE(Computer Aided Software Engineering,计算机辅助软件工程)工具,如Rose(Rose是非常受欢迎的物件软体开发工具。)。

  7、具有高素质的项目管理者和软件研发团队。

迭代模型- 迭代模型的优点

  与传统的瀑布模型相比较,迭代过程具有以下优点:

  1)降低了在一个增量上的开支风险。如果开发人员重复某个迭代,那么损失只是这一个开发有误的迭代的花费。

  2)降低了产品无法按照既定进度进入市场的风险。通过在开发早期就确定风险,可以尽早来解决而不至于在开发后期匆匆忙忙。

  3)加快了整个开发工作的进度。因为开发人员清楚问题的焦点所在,他们的工作会更有效率。

  4)由于用户的需求并不能在一开始就作出完全的界定,它们通常是在后续阶段中不断细化的。因此,迭代过程这种模式使适应需求的变化会更容易些。

 

 第三种:敏捷开发模式(ASD: Agile Software Development)

这个是一个概念性的东西,网上也普遍引用敏捷宣言(Manifesto for Agile Software Development)。对于上上来的两个比较有见解的文章,这里剪辑一下。

文章出处:

         1.http://www.ruby-lang.org.cn/forums/thread-604-1-1.html

                作者是bryanzk。很有思想。

        2.http://www.cybersoftek.com/index.php?option=com_content&task=view&id=8&Itemid=1

                里面的内容是转载自吴穹博士的文章。

敏捷软件开发是一个概念意义上的框架,用来取代软件工程项目的概念;它强调在项目的整个生命周期中拥抱并促进由于软件进化式的发展所带来的变化
Agile software development is a conceptual framework for undertaking software engineering projects that embraces and promotes
evolutionary change throughout the entire life-cycle of the project.
  这段定义来自wikipedia,我认为是我接触ASD以来,对ASD最精辟的论述。
  请注意其中的三个关键词:
  在项目的整个生命周期中:这就涉及到了【敏捷项目管理】、【敏捷需求获取】、狭义的【敏捷软件开发】三个主要的领域和过程。要注意的是,上述三个过程并不是互相分开的,而是你中有我,我中有你。
  拥抱并促进变化:世界上唯一不变的是变化。不论在任何领域,漠视、甚至否认、抗拒变化,都不是一个理性,严肃的人所应有的态度。学会如何识别变化的大势,并在可能的时候,促使变化向好的方向发展。这才是面对变化的正确应对之法。
  软件进化式的发展:虽然上面提到促进变化的发展,但是软件的演化过程,我相信是有其自身内在逻辑的,存在一些根本规律和指导方针;并不是完全以人的主观意识为主导。

 

关于敏捷软件开发,在Wikipedia中找到了下面的定义:

        (Agile Software Development is a conceptual framework for software development that promotes development iterations, open collaboration, and adaptability throughout the life-cycle of the project.) 

        在这个定义中,指出了敏捷的三个要素:迭代开发、坦诚合作和自适应性,下面我们分别对这三个要素进行以下分析。

下面是Wiki里有关敏捷和其他迭代开发方法的异同:

        绝大多数敏捷方法都沿用了迭代和增量式的开发方法,强调在短时间段内构建可以发布的软件。敏捷开发与其他开发模型的不同之处在于:时间段是按周而不是按月进行度量的,工作也以高度协作的方式展开。同样区分于其他模型的是,绝大多数敏捷方法将时间段限制为严格定死的时间盒。 


      (Most agile methods share other iterative and incremental development methods' emphasis on building releasable software in short time periods. Agile development differs from other development models: in this model time periods are measured in weeks rather than months and work is performed in a highly collaborative manner. Most agile methods also differ by treating their time period as a strict timebox.) 

     坦诚合作其实才是敏捷的精髓,如Ivar所说,敏捷其实是有关Social Engineering的。敏捷的主要贡献在于他更多地思考了如何去激发开发人员的工作热情,这是在软件工程几十年的发展过程中相对被忽略的领域。如何将敏捷融入到整个软件工程的体系当中,这将是下一篇文章讨论的内容。 

      自适应性其实是一种后退,但是一种明智的、合理的后退。长期以来,人们经常试图将成功应用于建筑、机械等其他领域的项目管理方法强加到软件身上。这些方法往往非常强调可预测性,但由于软件本身的特性,往往给开发过程增加了不必要的成本。正如Walker Royce所说,开发软件其实更象拍电影,所以管理的挑战更大。敏捷提出的自适应性其实是减低了对项目可预测性的不合理要求,解放团队让他们关注与交付客户价值。 

      自适应方法关注快速适应不断变化的现实情况。随着项目需求的变化,自适应的团队也会随之而变。自适应团队很难准确预测未来的状况。(来自Wikipeida)

      Adaptive methods focus on adapting quickly to changing realities. When the needs of a project change, an adaptive team changes as well. An adaptive team will have difficulty describing exactly what will happen in the future. (from Wikipedia)

      重温一下敏捷宣言---Agile Manifesto:

  • 人和交互重于过程和工具。
  • 可以工作的软件重于求全责备的文档。
  • 客户合作重于合同谈判。
  • 随时应对变化重于循规蹈矩。
  • Individuals and interactions over processes and tools.
  • Working software over comprehensive documentation.
  • Customer collaboration over contract negotiation.
  • Responding to change over following a plan.

      以下十一条是Agile Manifesto背后的基本原则,其实你可以看到前四条是有关迭代的, 后六条是有关团队合作的,最后一条是有关自适应性的。

  1. 通过快速,持续地交付可用的软件让客户满意。
  2. (按周而不是按月)频繁交付可用的软件。
  3. 可用的软件是首要的进度度量标准。
  4. 欢迎需求变更,即使来的很晚。
  5.  业务人员和开发人员要尽量做到每天在一起紧密工作。
  6. 面对面的谈话是最好的沟通方式,(在同一地点工作)。
  7. 参与项目的人员都应该抱有积极的态度。而且他们也得到信任。
  8.  持续关注技术的卓越程度和良好的设计。
  9. 简单至上。
  10. 构建自组织的团队。
  11. 让团队有规律地适应不断变化的环境。(以上11条来自Wikipedia)
  1. Customer satisfaction by rapid, continuous delivery of useful software.
  2. Working software is delivered frequently (weeks rather than months).
  3. Working software is the principal measure of progress.
  4. Even late changes in requirements are welcomed.
  5. Close, daily cooperation between business people and developers.
  6. Face-to-face conversation is the best form of communication (Co-location).
  7. Projects are built around motivated individuals, who should be trusted.
  8. Continuous attention to technical excellence and good design.
  9. Simplicity.
  10. Self-organizing teams.
  11. Regular adaptation to changing circumstances.(from Wikipedia).

 

        面对时下火热的敏捷运动,首先是不要神化,不要迷信,不要迷失自我。存在即合理,你先有的开发流程和工作方式一定有它的合理性,应渐进地采纳敏捷中一些合适的实践,仔细审视各种工件的合理性和必要性,要防止借敏捷的旗号来偷工减料(尤其是放弃必要的设计和架构工作)。同时,也应该充分利用这次文化运动所产生的变革力量和热情,推动一些最佳实践(如迭代开发),打破一些对软件开发活动不合理的束缚。总之,要弄潮,而不要被潮水裹挟,Be Smart。

 

 

 

 

 

 

正则表达式符号列表

正则表达式符号列表

字符 描述
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 '\\' 匹配 "\" 而 "\(" 则匹配 "("。
^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
{n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。
{n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'。
. 匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
(pattern) 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 '\(' 或 '\)'。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
(?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
x|y 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
[xyz] 字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\cx 匹配由 x 指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\d 匹配一个数字字符。等价于 [0-9]。
\D 匹配一个非数字字符。等价于 [^0-9]。
\f 匹配一个换页符。等价于 \x0c 和 \cL。
\n 匹配一个换行符。等价于 \x0a 和 \cJ。
\r 匹配一个回车符。等价于 \x0d 和 \cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\t 匹配一个制表符。等价于 \x09 和 \cI。
\v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
\W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,'\x41' 匹配 "A"。'\x041' 则等价于 '\x04' & "1"。正则表达式中可以使用 ASCII 编码。.
\num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1' 匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用。如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
\nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。

将Struts 应用程序移植到 JSF

 将Struts 应用程序移植到 JSF

为了将 Struts Web 应用程序与 JSF 集成,遵循以下步骤:

  • 将 struts-faces.jar 文件与特定于 JSF 的 JAR(jsf-api.jar、jsf-ri.jar) 添加到 Web 应用程序的 WEB-INF/lib目录中。 
  • 如果准备使用 JSF 和 JSTL,则将特定于 JSTL 的 JAR(jstl.jar、standard.jar)添加到 WEB-INF/lib 文件夹中。这一步只有在部署到常规 Tomcat 时才会需要。JWSDP 已经提供了这些 JAR。 
  • 修改 Web 应用程序部署描述符 ( /WEB-INF/web.xml)以便有一个 Faces Servlet 项, 如清单 5 所示。 
  • 修改 JSP 页面以使用 JSF 和 Struts-Faces 标记而不是 Struts 标记。特别是用 Struts-Faces 相应标记替换 html、b ase、 form 和 errors 标记。用 JSF 相应标记替换 text 、 textarea 和 radio 标记。Struts-Faces 没有单独针对这些的标记。尽管没有要求,但是您可能还会考虑用 JSTL 标记替换 Struts Logic 标记。 
  • 对于每一个使用 JSF 标记的 JSP,修改 struts-config.xml 文件以在指向该 JSP 的 Action Mapping 中的 global-forwards和 local-forwards中加入前缀 /faces。 
  • 如果 Web 应用程序使用了任何您创建的自定义组件,那么您就需要用 JSF 实现的默认 RenderKit 注册它们。可以通过在 WEB-INF 文件中创建一个 faces-config.xml 文件、并增加每一个组件和 renderer 的项做到这一点。不过,要记住 faces-config.xml 文件已经绑定在 struts-faces.jar 文件中了。您必须从 struts-faces.jar 文件中提出它、加入自己的内容并将它放到 WEB-INF文件夹中。


清单 5. 在 web.xml 中声明 FacesServlet

 

<!-- JavaServer Faces Servlet Configuration -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- JavaServer Faces Servlet Mapping -->
<servlet-mapping>
  <servlet-name>faces</servlet-name>
  <url-pattern>/faces/*</url-pattern>
</servlet-mapping>

集成 Struts-Faces 和 Tiles 的挑战

Struts-Faces 库提供了 Struts 与 JSF 之间的一个高效的桥梁,使得在 J2EE Web 应用程序中拥有丰富的表示层成为现实。您可以通过在组合体中添加 Titles 使表示层更丰富,这样不仅得到了 Struts 和 JSF 组合的好处,而且还可以高效地重复使用不同的 JSP 页面,因为它们将由可以根据需要添加或者删除的组件部分或者 tiles 所构成。

本文已经展示了 Struts 和 JSP 的集成,您会想将 Tiles 加入到组合中只是小事一桩,是不是?

不幸的是,JSF 仍然处于早期阶段,还没有给出最后的发布。基于这一考虑,Struts-Faces 集成软件开发仍然在不断地发展以包括 JSF 的不同的功能,并且还没有支持 Tiles。

Struts 和 Tiles 可以无缝地共同工作,但是在集成之路上您会遇到路障。在下面几小节中,您会看到在与 Tiles 共同使用 Struts-Faces 集成库时经常遇到的问题的汇总。对于每一个问题,我们详细说明了一个修改 Struts-Faces 类的解决方案。我们将用一个航班搜索示例解释这个解决方案。

清单 6 展示了航班搜索页面的布局。注意我们称它为航班搜索页面而不是 FlightSearch.jsp。这是因为 FlightSearch JSP 是用户在 foobar 旅行 Web 站点看到的合成页面的主体。

现在,我们保持实际的 FlightSearch.jsp 不变。我们将随着进展改变它。在您这边,也需要用航班搜索页的定义创建一个 Tiles 定义文件。清单 7(紧接着清单 6)展示了 Tiles 定义文件中航班搜索页的一项。注意对带有 extends 属性的主布局模板的重复使用。

在清单 6 和 7 后是每一个可能的挑战。


清单 6. 航班搜索例子的 Tiles 布局

 

<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-faces"prefix="s" %>

<!-- Layout component parameters: header, menu, body, footer -->
<s:html>
<head>
  <title> <tiles:getAsString name="title"/></title>
  <s:base/>
</head>
<body>
  <TABLE border="0" width="100%" cellspacing="5">
    <tr>
     <td><tiles:insert attribute="header"/></td>
    </tr>

    <tr>
     <td><tiles:insert attribute="body"/></td>
    </tr>

    <tr><td><hr></td></tr>

    <tr>
     <td><tiles:insert attribute="footer" /></td>
    </tr>
  </TABLE>
</body>
</s:html>
<!-- Master Layout definition  -->
<definition name="foobar.master-layout"
  path="/faces/layout/MasterLayout.jsp">

      <put name="title"  value="Welcome to Foo Bar Travels" />
      <put name="header" value="/faces/common/header.jsp" />
      <put name="footer" value="/faces/common/footer.jsp" />
      <put name="body"   value="" />
</definition>

  <!-- Definition for Flight Search Page -->
<definition name="/foobar.flight-search"
  extends="foobar.master-layout">

      <put name="body"   value="/faces/FlightSearch.jsp" />
</definition>

响应已经提交

这是您在试图访问航班搜索表单时马上会看到的第一个问题。小心查看堆栈跟踪。您会看到问题出在类 com.sun.faces.lifecycle.ViewHandlerImpl 上。这是一个实现了 ViewHandler 接口的 JSF-RI 类。

图 2展示了 ViewHandler 所扮演的角色。这是一个将请求转发给下一页的类。在转发请求时,它不在转发前检查响应的状态 -- 这只有在使用 Tiles 时才会发生,因为 Tiles 内部将 JSP 页面包括在响应内,而 JSF-RI 在第一次转发后提交响应、然后试图再次转发给下面的包括 JSP 的 Tiles。

要解决这个问题,必须创建一个自定义的 ViewHandler 实现,它将检查响应的状态以确定它是否提交过。如果响应没有提交过,那么请求就转发给下一页,否则,就加入请求并显示相应的 JSP。我们将创建一个名为 STFViewHandlerImpl 的类,它实现了 ViewHandler 接口并实现了所需要的方法 renderView()。 清单 8 展示了 STFViewHandlerImpl 中的 renderView() 方法:


清单 8. STFViewHandlerImpl 中的 renderView()方法

RequestDispatcher rd = null;
Tree tree = context.getTree();
String requestURI = context.getTree().getTreeId();
rd = request.getRequestDispatcher(requestURI);

/** If the response is committed, include the resource **/
if( !response.isCommitted() ) {
   rd.forward(request, context.getServletResponse());
}
else {
   rd.include(request, context.getServletResponse());
}

现在您实现了自己的 ViewHandler ,如何通知 JSF-RI 使用您的 ViewHandler 而不是默认的实现呢?要回答这个问题,就必须理解 FacesServlet 的工作过程。

在 Faces 初始化过程中, FacesServlet 会让 LifecycleFactory 实现返回 Lifecycle 类的一个实现,如清单 9 所示:


清单 9. FacesServlet 中 Faces 的初始化

 

//Get the LifecycleFactory from the Factory Finder
LifecycleFactory factory = (LifecycleFactory)
  FactoryFinder.getFactory("javax.faces.lifecycle.LifecycleFactory");

//Get the context param from web.xml
String lifecycleID =
getServletContext().getInitParameter("javax.faces.lifecycle.LIFECYCLE_ID");

//Get the Lifecycle Implementation
Lifecycle lifecycle = factory.getLifecycle(lifeCycleID);

Lifecycle 实现对象拥有在呈现响应阶段要使用的 ViewHandler 。您可以通过对 Lifecycle 实现调用 setViewHandler 方法让自己的 ViewHandler 实现成为默认的。

现在问题变为如何得到默认 Lifecycle 实现?回答是不需要这样做。只要创建一个新的实现并用一个惟一 ID 注册它,如清单 10 所示:


清单 10. 注册自定义 ViewHandler 和 Lifecycle

 

//Get the LifecycleFactory from the Factory Finder
LifecycleFactory factory = (LifecycleFactory)
  FactoryFinder.getFactory("javax.faces.lifecycle.LifecycleFactory");

//Create a new instance of Lifecycle implementation -
//com.sun.faces.lifecycle.LifecycleImpl
//According to the documentation, factory.getLifecycle("STFLifecycle")
//should work, but JSF-RI has a defect.
//Hence this workaround of creating a RI class explicitly.
LifecycleImpl stfLifecycleImpl = new LifecycleImpl();

//Create a new instance of our STFViewHandler and set it on the Lifecycle
stfLifecycleImpl.setViewHandler(new STFViewHandlerImpl());

//Register the new lifecycle with the factory with a unique
//name "STFLifecycle"
factory.addLifecycle("STFLifecycle", stfLifecycleImpl);
您可以看到 lifecycleId 硬编码为 STFLifecycle 。实际上不是这样。当您回过头分析 清单 9时就会清楚。 FacesServlet 从在 web.xml 文件中声明的上下文参数中得到名为 javax.faces.lifecycle.LIFECYCLE_ID 的 lifecycle ID,如下所示:
    <context-param>
        <param-name>javax.faces.lifecycle.LIFECYCLE_ID</param-name>
        <param-value>STFLifecycle</param-value>
    </context-param>

因为 FacesServlet 取决于其初始化时的 Lifecycle 实现,在 清单 10中展示的代码应该在 FacesServlet 初始化之前执行。通过创建另一个 servlet 并在 FacesServlet 之前初始化它而做到这一点。

但是一种更聪明的办法是实现一个 ServletContextListener 接口。这个类声明两个方法: contextInitialized() 和 contextDestroyed() ,在 Web 应用程序被创建及 Web 应用程序被销毁之前会分别调用它们。因而 清单 10中的代码在 contextInitialized() 方法中执行,而自定义 ViewHandler 已经用标识名 STFLifecycle 注册到 Lifecycle ,并且可被 FacesServlet 使用。 ServletContextListener 类本身是在 web.xml 文件中声明的,如下所示:

<listener>
  <listener-class>foo.bar.stf.application.STFContextListener
  </listener-class>
</listener>

这不是注册一个带有自定义 ViewHandler 的 Lifecycle 惟一方法。事实上 FactoryFinder 实现了自己的发现算法以发现 Factory 对象,包括 LifecycleFactory 。这些机制按照顺序包括在系统属性中查看工厂实现类名的机制、faces.properties file、或者 1.3 Services 发现机制( META-INF/services/{factory-class-name} )。不过,我们讨论的这种机制是最容易的,也是最不具有破坏性的一种。

404 Resource Not Found

在解决了提交响应的问题后,单击任何一个 Tiles 特定的链接或者输入一个会呈现 Faces 响应的 URL。在这里,可以输入显示 FlightSearchForm 的 URL。

在这样做了以后,您会得到一个 foobar.flight-search - 404 Resource Not Found 错误。 foobar.flight-search 是航班搜索页面的 Tiles 定义的名字。 FacesRequestProcessor 不能处理 Tiles 请求(因为它扩展的是RequestProcessor 而不是 TilesRequestProcessor ),所以会得到错误。

为解决这个问题,我们将创建一个名为 STFRequestProcessor (表示 Struts-Tiles-Faces Request Processor)的新的请求处理程序。现在我们将拷贝 FacesRequestProcessor 的所有代码到这个新类中。惟一的区别是 STFRequestProcessor 继承的是 TilesRequestProcessor 而不是继承常规的 RequestProcessor 。这个新的 RequestProcessor 可以处理 Tiles 请求。清单 11 详细列出了这个 STFRequestProcessor :


清单 11. STFRequestProcessor.java

正如您所知道的, Struts 框架的 RequestProcessor 是在 struts-config.xml 文件中指定的。将下面的项添加到 struts-cinfig.xml 文件中后, STFRequestProcessor 就成为处理程序:

<controller processorClass="foobar.stf.application.STFRequestProcessor" />

表单提交显示返回同一个表单

由于 STFRequestProcessor 的作用,这时您就可以浏览并查看航班页面了。不过,在提交航班搜索表单时,您会得到返回来的同一个表单,而且没有页头和页脚!并且没有验证错误。事实上,根本就没有进行验证!

为了了解到底发生了什么事情,我们用浏览器回到航班页面并检查 HTML 源代码。您会看到像下面这样的一项:

 

<form name="FlightSearchForm" method="post"    action="/flightapp/faces/FlightSearch.jsp">  

 

注意表单 action 是指向 JSP 页而不是一个 .do 的。啊哈!这就是问题!这不是由于同时使用 Tiles 和 Struts-Faces 而带来的新问题,Struts-Faces 的默认行为是让 JSP 与表单 action 有同样的名字。这种行为在有单一的 JSP 页(如在前面的 Struts-Faces 例子中)时没有问题。 清单 3展示了原来的 FlightSearch.jsp,让我们继续并像下面这样修改 action:

 

<s:form action="/listFlights.do> 

 

当然,光有这种修改并不能解决问题。作了这种改变后,您就会发现 STFRequestProcessor 不能找到 ActionForm 。显然还需要其他的改变。

不过,在继续往下之前,看一下图&#160 5。它显示了在呈现负责 Struts-Faces 表单的 faces 时相关的一系列事件。这与 图 3相同,除了在 FormComponent 中突出显示的方法 createActionForm()。 由 Struts-Faces API 提供的 FormComponent 类是 javax.faces.component.UIForm 的特殊子类,它支持请求或者会话范围的表单 Bean。

图 5. 呈现 Struts-Faces 响应 
单击这里以查看该图。

正如您所看到的, createActionForm() 方法使用 action 名以从 Struts 配置文件中得到 ActionMapping 。因为没有对于 /listFlights.do 的 ActionMapping ,所以 Struts 不能找到 ActionForm。

这个问题的解决方法是使用 org.apache.struts.util.RequestUtils 。 RequestUtils 中的 static 方法 getActionMappingName() 具有足够的智能解析映射到正确 ActionMapping 的路径( /x/y/z)或者后缀( .do)。

清单 12 以粗体显示对 createActionForm 方法的改变。我们没有对 Struts-Faces 中的 FormComponent 作这些改变,而是通过继承 FormComponent 并覆盖 createActionForm() 方法创建了一个新的 STFFormComponent。


清单 12. FormComponent 中修改过的 createActionForm() 方法

// Look up the application module configuration information we need ModuleConfig moduleConfig = lookupModuleConfig(context);  // Look up the ActionConfig we are processing String action = getAction(); String mappingName = RequestUtils.getActionMappingName(action); ActionConfig actionConfig = moduleConfig.findActionConfig(mappingName); .... .... 

 

对新的 STFFormComponent 还要作一项改变。Struts-Faces 将 action 名本身作为表单名。这需要改变,因为 action 带有后缀 .do,而表单名没有后缀 .do。所以我们在 STFFormComponent 上增加一个名为 action的新属性,并覆盖 getAction() 和 setAction() 方法。

FormRenderer 的改变

必须对 FormRenderer (以 HTML 格式呈现 Struts-Faces 表单的类)的 encodeBegin 方法进行类似于 清单 10所示的修改。

同样,通过继承 FormRenderer 做到这一点。此外,还必须改变写出到 HTML 的表单 action。清单 13以粗体详细列出了这些改变:


清单 13. FormRenderer 的改变

protected String action(FacesContext context, UIComponent component) {      String treeId = context.getTree().getTreeId();     StringBuffer sb = new StringBuffer        (context.getExternalContext().getRequestContextPath());     sb.append("/faces");      // sb.append(treeId); -- This is old code, replaced with      // the two lines below.      STFFormComponent fComponent = (STFFormComponent) component;     sb.append(fComponent.getAction());          return (context.getExternalContext().encodeURL(sb.toString())); } 

 

FormTag的改变 
正如您已经知道的,当组件和 renderer 改变时,标记也必须改变。在这里,通过继承 Struts-Faces 中的 FormTag 创建一个新的标记: STFFormTag 。不必改变任何功能,只要覆盖 getComponentType() 和 getRendererType() 方法。清单 14 展示了从 STFFormComponent 覆盖的方法:


清单 14. FormTag 的改变

public String getComponentType() {     return ("STFFormComponent"); }  public String getRendererType() {     return ("STFFormRenderer"); }  

 

修改 faces-config.xml 文件

自定义组件和 renderer 必须在 faces-config.xml 文件中声明,这样 JSF 框架才可以初始化并使用它们。现在我们已经创建了一个新组件 STFFormComponent 和一个新 renderer STFFormRenderer 。

现在我们将在 faces-config.xml 文件中增加一个声明,如清单 15 所示。 component-class 是组件的完全限定类名。 component-type 指的是在 STFFormTag ( 清单 12)中用于标识组件的名字。以类似的方式发现和解释 renderer。注意 faces-config.xml 文件是在 struts-faces.jar 文件中的。从 struts-faces.jar 文件中取出这个文件并将它放到 Web 应用程序的 WEB-INF文件夹中并修改它。


清单 15. 在 faces-config.xml 中声明自定义组件和 renderer

<faces-config>    <!-- Custom Components -->   <component>      <component-type>STFFormComponent</component-type>     <component-class>       foobar.stf.component.STFFormComponent     </component-class>   </component>   ..   ..   ..   <!-- Custom Renderers -->    <render-kit>      <renderer>       <renderer-type>STFFormRenderer</renderer-type>       <renderer-class>         foobar.stf.renderer.STFFormRenderer       </renderer-class>      </renderer>     ..     ..     ..   </render-kit> </faces-config> 

 

修改 struts-faces.tld 文件

您不会在这个示例 Struts-Faces 应用程序中看到 struts-faces.tld 文件,它打包到了 struts-faces.jar 文件中。打开并分析这个文件。它声明了一个名为 org.apache.struts.faces.taglib.LifecycleListener 的类,这个类实现了 ServletContextListener 并初始化 FacesRequestProcessor 。

因为希望使用新的 STFRequestProccessor ,所以必须将这个文件从 struts-faces.jar 文件中删除,将它放到 Web 应用程序的 WEB-INF 文件夹中,并删除侦听器声明。如果让这个 tld 文件保持原样,那么在初始化这个 Web 应用程序时,除了 STFRequestProcessor ,还会实例化一个 FacesRequestProcessor。

修改 base href 标记 
现在,您已经完成了 Struts、Tiles、JSF 集成的最困难的部分了。您甚至可以浏览航班搜索页面,并输入搜索标准查看航班列表。现在试着从航班列表页面返回航班搜索表单。您会得到一个 HTTP 400 错误。这个错误的原因是 HTML base href 标记。它被设置为 Master Layout 页面。

 

<base href=   "http://localhost:8080/stf-example/faces/layout/MasterLayout.jsp" />            |_________|       |_____________________|               Context               Servlet Path 

 

程序所有页面浏览都是相对于布局页面计算的。如果加入的 base href 标记只达到 Web 应用程序上下文则会很方便,像这样:

 

<base href="http://localhost:8080/stf-example/" />  

 

我们可以通过定制 Struts-Faces BaseTag 做到这一点。这个类中的改变相当微不足道。只须在 base href 中去掉 HttpServletRequest.getServletPath() 。

因为这些改变是与显示相关的,所以为它创建了一个名为 STFBaseRenderer 的新 renderer。这个新标记称为 STFBaseTag ,它声明 STFBaseRenderer 作为其关联的 renderer。不需要新的组件。

有了这些信息,通过继承 BaseTag 并覆盖 getRendererType 方法创建新的 STFBaseTag ,如下所示:

 

public String getRendererType() {     return ("STFBaseRenderer"); } 

 

到目前为止所作的改变

恭喜!经过这些相对较小的修改,您已经成功地集成了 Struts、Tiles 和 JSF,并保留了您以前在这些技术上所做的所有投资。本文演示了如何将 JSF 强大的前端能力、 Tiles 的内容格式编排优势以及 Struts 控制器层的灵活性结合在一个包中,使得创建一个 J2EE Web 应用程序成为一项更容易的任务。

我们讨论了定制 Struts 类以便与 JavaServer Faces 和 Tiles 框架形成紧密集成的工作关系,包括下面这些修改和增加:

  • 新的 ViewHandler ,用于检查提交的响应。 
  • 新的 ServletContextListener ,用于创建新的 Lifecycle 实现并注册这个定制的 ViewHandler。 
  • 新的 RequestProcessor ,用于处理 Tiles 请求。 
  • 修改过的 web.xml 文件,声明新的 ServletContextListener 和 JSF Lifecycle ID。 
  • 新的 FormTag、 FormComponent 和 FormRenderer 类。 
  • 新的 BaseTag 和 BaseRenderer 类。 
  • 修改过的 faces-config.xml 文件,它声明了新的组件和 renderer。
  • 修改过的 struts-faces.tld 文件,不声明侦听器。

希望它可以概括本文中使用的复合技术,最重要的是,我们为您提供了将 Struts、Tiles 和 JavaServer Faces 结合到用于构建 Web 应用程序的一个强大而灵活的机制中的一个令人信服的路线图。

参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
     
  • 下载本文的 例子和代码,并遵循 README.txt 中给出的编译和部署的说明。 

     
  • Ant 用于对例子进行编译,可以从 Apache Ant 项目Web 站点下载它。 

     
  • 有关 Struts 和 Tiles 的更多内容,包括可下载的教程、文档、二进制文件和源代码,可从 Apache Jakarta Project StrutsWeb 站点获得。 

     
  • 可以将 JSF Early Acess Release 4 (EA4) 作为 Java Web Services Developer Pack Version 1.2 的一部分下载 -- 它带有自己版本的 Tomcat。 

     
  • 可以从 Jakarta 站点下载 Struts-Faces integration library的 0.3 和 0.4 版本。 

     
  • 可以从 Java Web Services Developer Pack 1.2 下载 JSF-RI。 

     
  • “ Struts, an open-source MVC implementation”( developerWorks, 2001年2月)介绍了 Struts,这是一个使用了 servlets 和 JavaServer Pages 技术的模型-视图-控制器实现。 

     
  • “ Struts and Tiles aid component-based development”( developerWorks,2002年6月)解释了为什么结合 Struts 和 Tiles 可以成为创建 Web 应用程序的出色软件包,并展示了如何使用它,侧重于 Struts 0.9 之后的改变。 

     
  • “ Struttin' your stuff with WebSphere Studio Application Developer, Part 2: Tiles” ( developerWorks,2002年11月)是一个教程,主要关注在使用 WebShpere Studio Application Developer 作为开发环境时结合 Struts 使用 Tiles 模型框架。 

     
  • “ Architect Struts applications for Web services”( developerWorks,2003年4月)展示了如何基于 MVB 设计模式用 Struts 建立 Web 服务应用程序。 

     
  • “ A JSTL primer”( developerWorks,2003年2-5月),这是一个分为四部分的系列,提供了有关 JSTL 的所有内容,包括如何使用 JSTL 标记以避免在 JSP 页面中使用脚本元素、如何通过删除表示层中的代码简化软件维护、以及 JSTL 的简化的表达式语言,它使得无需使用全功能的编程语言就可以为 JSTL action 指定动态属性值。 
     
  • 学习用 JSF 开发 Web 应用程序的基本内容。在其教程“ UI development with JavaServer Faces” ( developerWorks,2003年9月)中,Jackwind Li Guojie 探讨了 JSF 生命周期、输入验证、事件处理、页面浏览和国际化。 

     
  • Sun 的 JSF Web 站点是另一个很好的学习有关 JavaServer Faces 技术的起点。 

     
  • ServerSide.com J2EE 社区是查找有关 J2EE 的资源及参加开发者论坛的理想地点。 

     
  • 在 Java Community Process站点可以迅速得到有关 JavaServer Pages 1.2 规范的内容。 

     
  • 在 developerWorks Java 技术专区 可以找到关于 Java 编程各方面的数百篇文章。 

 

 

 

 

 

一个简单的jQuery插件ajaxfileupload实现ajax上传文件例子

 页面代码:

 

 <script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/ajaxfileupload.js"></script>

<script type="text/javascript">
function ajaxFileUpload(){
 $.ajaxFileUpload({
         url:'update.do?method=uploader',//需要链接到服务器地址
         secureuri:false,
         fileElementId:'houseMaps',//文件选择框的id属性
         dataType: 'xml',          //服务器返回的格式,可以是json
         success: function (data, status)
         {     
                         $('#result').html('添加成功');
         },
         error: function (data, status, e)
         {
                         $('#result').html('添加失败');
         }
                        });
        
}
</script>
<form method="post" action="update.do?method=uploader"
        enctype="multipart/form-data">

        <input type="file" id="houseMaps" name="houseMaps" />
        <input type="button" value="提交" onclick="ajaxFileUpload()" />
</form>
<div id="result"></div>
服务器代码:
public class UpdateAction extends DispatchAction {

        public ActionForward uploader(ActionMapping mapping, ActionForm form,
                        HttpServletRequest request, HttpServletResponse response) {
                UpFormForm upFormForm = (UpFormForm) form;
                FormFile ff = upFormForm.getHouseMaps();
                try {
                        InputStream is = ff.getInputStream();
                        File file = new File("D:/" + ff.getFileName()); // 指定文件存储的路径和文件名
                        OutputStream os = new FileOutputStream(file);

                        byte[] b = new byte[1024];
                        int len = 0;
                        while ((len = is.read(b)) != -1) {
                                os.write(b, 0, len);
                        }
                        os.close();
                        is.close();
                } catch (Exception e) {
                        e.printStackTrace();
                 }
                 return null;
        }
}