同事说,在台北部署一个项目时遇到一个ClassNotFound的问题,而开发环境就不会发生,问我可能的原因,我想可能是环境classpath没有包含jar或者有class重复之类的问题。 他检查了一下,除了开发环境是简体系统,部署环境是繁体系统之外,所有配置设定都一样。当时想java的项目,跟简体繁体应该没关系吧,应该还是其他地方的问题,但因为自己有事情忙没有帮他看这个问题。

第二天他告诉我找到原因了:ClassNotFound的问题是那个类初始化的时候会加载国际化内容,而项目中有配置简体国际化,但繁体的国际化没配置,导致读取国际化失败,类加载失败。添加了繁体国际化就解决了!

不管程序设计上是否有问题,单从找问题的思路上,我也犯了错误。因为一般情况遇到ClassNotFound的问题都是classpath的问题,在此遇到这样的问题,习惯性的认为就是这样的原因造成。没有更进一步思考发生错误可能的原因还有其他情况,比如类加载失败等; 另外是当得知只存在简体繁体OS之差的情况,没有从这个关键点去思考,没有去想程序逻辑是否跟系统的语言版本有关系。 看来以后遇到问题的时候,还要对问题进行深入的了解,了解问题本质以及尽可能多的原因。另外程序运行会存在依赖关系,在问题模拟时,存在任何差异都应该要引起注意,多思考一下这样的差异是否和程序逻辑有关系。

说到找问题,程序员往往都会犯一个错误:对自己写的程序盲目自信!觉得自己写的代码绝无BUG,出了问题,简单检查一下程序说没问题,就开始怀疑是不是framework有问题?更有甚者会怀疑JDK有bug,OS有bug。 当然也不是framework、JDK、OS就不可能有问题,要明白framework、JDK、OS都是经过长时间的验证,且大家都在使用,其他人都没有遇到问题,让你发现问题的几率还是很小的。项目实践证明,绝大多数问题都是程序逻辑的问题,framework的问题很难遇到,JDK、OS的问题几乎是没有!

租房的地方比较旧的安置房小区,房东安装的电灯都是挂丝的,之前已经换过多次节能灯,用不了多久就烧了!这次两个房间都烧了,去五金店买已经买不到挂丝的节能灯了,老板说现在都不用挂丝了,挂丝太危险,金属暴露在外,用摧丝的都包在塑料壳内,更安全!好吧!于是我买了两个节能灯和两个节能灯头!

今天一早,架起桌子凳子忙活起来!准备好螺丝刀、钳子、刀子!爬到桌子上的凳子上,把天花板上的挂丝头拆掉,但原来留下的线很短,很难操作!必须另外找铜线延长。屋里找了半天没找到,后来想起凉衣服的线是电线,于是从上面取下一节,扒去塑料皮,再截断成10厘米一小段。用钳子把这段铜线和原来天花板露出的一点线拧起来转几圈,将起伸到摧丝塑料壳内,然后穿进扣控中,再用螺丝刀把扣控螺丝旋紧,最后塑料壳旋紧,这样摧丝口就换好了! 节能灯旋上接口即可,相当简单!

一切顺利完成,但这个过程还是挺难的,要一直举着手做事情,而且脚还不能动,弄出一身汗水! 不过打开电闸,打开开关,电灯亮起的那一刻还是相当有成就感!

今天收到spring推出spring io platform的邮件,它是一个基于maven(或Gradle)的依赖版本管理的定义,它定义了常用的企业开发所需的java库的依赖版本,使得开发者不再花心思研究各个java库相互依赖的版本,只需引用spring io platform的定义。特别是引用的java库要升级的时候,还需要所有相关的java库都要检查一下,往往需要漫长的验证过程才能最终确定一个可用的版本。 如果使用spring io platform,依赖的版本关系已经被验证测试过了,所以可以放心使用,升级也是简单升级spring io platform即可,它会升级所有相关依赖的java库。

公司中我也创建了一个类似spring io platform的版本依赖定义的项目,其他多个开发项目都在用。后续将考虑修改此依赖定义项目,先引入spring io platform,如果有特别需要的java库,再另外添加! 以后java库升级就不那么麻烦了!

<!-- import spring io platform definition -->
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.spring.platform</groupId>
                <artifactId>platform-bom</artifactId>
                <version>1.0.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

参考:Spring IO Platform Reference Guide

呵呵,这明显是标题党了,勿喷!

当今是互联网爆发的时代,所有行业都采用各种方式挤入互联网,不管餐饮小店还是小作坊,都知道要在互联网做宣传。互联网已经是新时代的广告平台!很多网站都是靠网络广告作为其主要收入来源。但要获得好的收入,首先网站的PV/UV量要比较高,才会有广告主找上门。或者你本身是需要做广告,通过广告提高网站PV量,从而提高销售额。提高了销售额,就提高了营收,也就可以提高网站的估值,最终把钱赚入腰包!

PV就是金钱,提高PV就是赚钱!

为了提高PV,是否只能做广告呢?当然不是,广告的目的是把你的信息发布到各个地方,让很多人看到,而新闻也有同样的作用!
新闻相对广告而言更具优势,因为广告是被动接收的,而新闻是人们主动要索取的信息。而且新闻具有广泛的传播平台,而且一段时间内还会成为大家的谈资,推广效果更好! 如果一个爆炸性的新闻中包含你的广告信息(当然是隐秘的间接的,比如公司名),传播不需要一分钱,能给你带来大量的PV,那是不是一件再好不过的事情呢?

然而,哪来这样爆炸性的新闻而其中又包含你的广告信息呢? 自己制造吧!如何制造呢?花钱吧!

  1. 花100万去做一个慈善,叫上各家媒体来做宣传;
  2. 或节省一点,花1万买上万个茶叶蛋声势浩大的在街上发放,表示你对白领吃不起茶叶蛋言论的不满,同时告之各路记者来采访;

但有没有不花钱的呢? 有!用你的声誉来制造新闻吧! 当然这些声誉损失不能影响你的未来发展,不然就得不偿失了!
比如你是某某集团网站的CEO,如果能够爆出和一个比自己小年龄相差很大的名气女神约会,而且是女神倒追你!这样的新闻是不是很爆炸啊!这个新闻足够大家谈论一段时间!而之前很大一部分人不知道某某集团的,开始访问你的网站,给你带来大量的PV,而其中一部分人会成为你的客户,给你带来不俗的交易量,让你赚取100万,1000万或更多,你是否会欣喜的接受呢? 你问女神愿意吗?女神说我会因此变得更有名气!

如果我再告诉你,互联网信息爆炸的今天,你的新闻很快就会被大家忘记!用你的声誉换100万,你是否愿意?
你问我愿意吗?我愿意但怕没有女神会愿意!

最后说明,此文纯属个人想法,切勿当真!

-- tablespace
CREATE tablespace MYPROJECT datafile 'D:\oracle\product\10.2.0\oratablespace\MYPROJECT.dbf'  SIZE 5m  autoextend ON NEXT  10m maxsize unlimited;

-- temporary tablespace
CREATE TEMPORARY tablespace MYPROJECT_TEMP tempfile 'D:\oracle\product\10.2.0\oratablespace\MYPROJECT_TEMP.dbf' SIZE 5m autoextend ON NEXT 10m maxsize unlimited;

-- USER SQL
CREATE USER MYUSER IDENTIFIED BY password
DEFAULT TABLESPACE "MYPROJECT"
TEMPORARY TABLESPACE "MYPROJECT_TEMP";

-- ROLES
GRANT "CONNECT","RESOURCE","DBA" TO MYUSER ;

-- SYSTEM PRIVILEGES

-- QUOTAS

整理一种Spring Security Web认证实现方案,其核心组件及认证过程说明如下:

实体设计说明:
1. Function,权限对象,和Role对象为多对多关系;
2. Role,角色对象,实现GrantedAuthority和ConfigAttribute接口;Autority和Attribute都返回RoleId;
3. User,用户对象,实现UserDetails接口,和Role对象为多对多关系;

服务设计说明:
1. FilterSecurityInterceptor继承AbstractSecurityInterceptor,需注入AccessDecisionManager、AuthenticationManager、SecurityMetadataSource;
2. SecurityMetadataSource需根据请求信息获得ConfigAttribute信息;具体实现类根据请求URI获得Function,再根据Functon获得ConfigAttribute(Role)列表;
3. AuthenticationManager需注入UserDetailsService,对提交信息进行认证,并获得UserDetails(User)的GrantedAuthority(Role)列表;
4. AccessDecisionManager实现类中decide方法,对以上获得的ConfigAttribute(Role)列表和GrantedAuthority(Role)列表进行对比,判断是否包含相同的项,若有则有权限,否则无权限;

一个可以抓图和录像的ActiveX控件,详情如下:

1. 网址:http://www.videocapx.com/

2. 其lite版包含大部分基本功能,售价99美元,standard版售价199美元( 1,236.32 人民币);

3. 其支持不同的视频编码和音频编码模式;使用视频MJPEG Compressor压缩模式,用200万高清摄像头录制10秒的影像录音文件大小大概为3M;

4. 此产品产商为欧洲产商,提供support网站进行支持;
其他联系方式:
Phone: 00387 30 513 858
Fax: 00387 30 513 858
Email: fathsoft@fathsoft.com

5. 产品特性如下:
1) Full access to all WDM-compatible devices
2) Support for multiple video inputs
3) Capture live video and audio
4) Text/Logo/Image overlay on video
5) Full multimedia player functionality
6) Capture live audio directly to MP3
7) Pause and resume capture without frame loss
8) Motion detection
9) Easily build video-surveillance , quality control or video-conferencing apps
10) Save video frames as JPG or PNG
11) Full access to RGB pixel data
12) Chroma-key effect (live or post-process)
13) Video-processing filters available
14) Resize, crop or zoom video
15) Samples with full source code included
16) Add video-capture to your web pages
17) Control PTZ camera, set brightness, saturation, hue, contrast
18) Draw graphics on video
19) Supports VC++, VB6, VB.NET, C#
20) Compatible with Windows 98, 2000, XP, Vista, Windows7 (32-bit and 64-bit)
21) Preview live video or playback video files to DeckLink card output
22) Video preview, capture and motion detection from IP/network cameras
23) Transfer video over network. Video-chat functionality.
24) Broadcast live video to Windows Media clients
25) Ti+A1:B25me Unlimited, Royalty-Free License

定义一个javascirpt方法,传入需要加载国际化消息key键数组,获得这些消息加载到页面中。

/*
 load i18n resource through javascript
 */

function loadi18n(keys) {
    if (keys == null || keys.length == 0) {
        return;
    }
    if(!window.i18n){var i18n = window.i18n = {}}
    jQuery(document).ready(function(){
        var method = keys.length > 20 ? "POST" : "GET";
        jQuery.ajax({
            url : context_path + "/js/resource",
            data:{"key[]":keys},
            type:method,
            cache:true,
            dataType:"script",
            success:function(data){}
        })}
    )
}

以下是一个Spring Controller根据传入key键返回国际化消息:

@RequestMapping(value = "/js/resource")
public void getMessages(@RequestParam(value = "key[]") String[] keys, HttpServletRequest request, HttpServletResponse response, Locale locale) {
    ResourceBundle resourceBundle = ResourceBundle.getBundle("message/messages", locale, JsI18nController.class.getClassLoader());
    response.setCharacterEncoding("utf-8");
    response.setContentType("application/javascript");
    StringBuilder contents = new StringBuilder();
    if (keys != null && keys.length > 0) {
        for (String key : keys) {
            String value = resourceBundle.getString(key);
            contents.append("i18n['" + key + "']=\"" + value + "\";");
        }
        try {
            response.getWriter().write(contents.toString());
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }  
}

以下是javascript动态调用获取国际化例子:

loadi18n([ "sure.delete", "success.delete" ]);
loadi18n([ "failure.delete" ]); //根据需要可多次反复调用

function delUser(userId) {
    if (confirm(i18n['sure.delete'])) {
        // to delete the user
    }
}

框架对关键的电子商务流程提供一个可配置的工作流的功能。如结算、支付、定价、购物车等的操作。这些工作流通过spring xml配置文件进行定义。一般其概况,broadleaf提供一个默认的配置,包括结算和支付这些基本的步骤,只需使用到简单的一些模块。大多数用户需要覆盖部分或全部步骤以满足实际商业需求。先讲解默认配置,再讲解如何客制化需求。

1.工作流概览

每个工作流实际上是一个SequenceProcessor 的实例。它管理顺序性的流程活动,以及处理异常状况。一个工作流主要有三个配置,process context factory、activities list、rollback handler。

<bean p:order="1000" id="blPaymentActivity" class="org.broadleafcommerce.core.payment.service.workflow.CompositeActivity">
    <property name="workflow" ref="blAuthorizeAndDebitWorkflow"/>
</bean>
<bean id="blPaymentWorkflow" class="org.broadleafcommerce.core.workflow.SequenceProcessor">
    <property name="processContextFactory">
        <bean class="org.broadleafcommerce.core.payment.service.workflow.SimplePaymentProcessContextFactory"/>
    </property>
    <property name="activities">
        <list>
            <ref bean="blPaymentActivity" />
        </list>
    </property>
    <property name="defaultErrorHandler" ref="blDefaultErrorHandler"/>
</bean>

1.1 ProcessContextFactory
processContextFactory 属性必须是一个实现ProcessContextFactory接口的类。此类负责创建一个ProcessContext实例。此例SimplePaymentProcessContextFactory创建了一个SimplePaymentContext实例。

ProcessContexts一般指和某一特殊的工作流有关,所以不同的工作流会有不同的ProcessContextFactory。一个特例是购物车工作流的新增、删除、更新等操作使用同一个ProcessContext 和ProcessContextFactory 。

1.2 Activities
activities 属性包含一个或个活动列表。每个活动只处理单一的工作(如计算税费、订单价格加总)。以上例子,提供了一个组合活动。组合活动可以包含一个“子工作流”,这样可以创建复杂嵌套的工作流。这个特殊的组合活动处理认证和借贷的组合功能。组合活动后面会再讲。

1.3 Error Handler
defaultErrorHandler属性设置异常处理器,它是一个ErrorHandler的实例。默认设置blDefaultErrorHandler,框架提供的一个简单的异常处理器,它只记录异常信息到system.out然后再抛出异常—停止工作流和当前线程正在处理的活动。稍后会再讲异常处理器。

2.Activity Ordering(活动顺序)

上例中的活动配置:

<bean p:order="1000" id="blPaymentActivity" class="org.broadleafcommerce.core.payment.service.workflow.CompositeActivity">
    <property name="workflow" ref="blAuthorizeAndDebitWorkflow"/>
</bean>

blPaymentActivity 有一个p:order属性,这是设置此活动的顺序,框架根据此属性决定各个活动执行的先后顺序。这让框架合并活动列表到同一个bean中时非常便捷。看一个更复杂的例子:

<bean p:order="1000" id="blVerifyCustomerMaxOfferUsesActivity" class="org.broadleafcommerce.core.offer.service.workflow.VerifyCustomerMaxOfferUsesActivity"/>
<bean p:order="2000" id="blPaymentServiceActivity" class="org.broadleafcommerce.core.checkout.service.workflow.PaymentServiceActivity"/>
<bean p:order="3000" id="blRecordOfferUsageActivity" class="org.broadleafcommerce.core.offer.service.workflow.RecordOfferUsageActivity"/>
<bean p:order="4000" id="blCompleteOrderActivity" class="org.broadleafcommerce.core.checkout.service.workflow.CompleteOrderActivity"/>

<bean id="blCheckoutWorkflow" class="org.broadleafcommerce.core.workflow.SequenceProcessor">
    <property name="processContextFactory">
        <bean class="org.broadleafcommerce.core.checkout.service.workflow.CheckoutProcessContextFactory"/>
    </property>
    <property name="activities">
        <list>
            <ref bean="blVerifyCustomerMaxOfferUsesActivity" />
            <ref bean="blPaymentServiceActivity" />
            <ref bean="blRecordOfferUsageActivity" />
            <ref bean="blCompleteOrderActivity" />
        </list>
    </property>
    <property name="defaultErrorHandler" ref="blDefaultErrorHandler"/>
</bean>

在Broadleaf工作流中,每个活动顺序都会定义大于1000,这样允许你在其间插入你自己的活动。例如增加一个活动在记录促销使用情况和标记订单完成两个活动之间,可以在自己的spring配置文件applicationContext-mycompany.xml中进行如下定义:

<bean id="blCheckoutWorkflow" class="org.broadleafcommerce.core.workflow.SequenceProcessor">
    <propety name="activities">
        <bean p:order="3500" class="com.mycompany.core.workflow.DecrementInventoryActivity" />
    </property>
</bean>

需要注意:如果2个活动的顺序一样(例如都是3000),那么将根据所处位置进行排序。意思是根据applicationContext配置文件中的被解析合并的顺序(在web.xml中的patchConfigLocations 中定义)。整合模块定义活动顺序应该以100为间隔(如3100,3200等),这样未来还可以在其间加入一些特殊的活动。所有框架的活动可以通过bean id覆盖同时改变p:order的值以改变活动顺序。如果没有明确声明顺序的活动将在流程最后执行(默认顺序是Ordered.LOWEST_PRECEDENCE)。

3.Rollback Handlers(回滚处理器)

回滚处理器为活动提供了注册状态的途径,可以在之后的某一个时间执行一个回滚动作。此例,结算流程第一步可以是一个信用卡借贷。信用卡支付后,第二步是改变购物车状态。假如此时因某些原因导致购物车状态更新失败,应有一个标准的方式将之前信用卡支付进行退还或者作废。这就是回滚处理器的作用。
回滚处理器能处理任何客户代码,也能根据传入状态进行处理。总之,回滚操作无需向支付网关一样需要一个外部服务。可以认为是一个compensating transaction(补偿事务)恢复DB到执行活动之前的状态。这是一个重要的差别,大多是工作流在JDBC事务中无法执行,因为工作流的生命周期长度让保证事务开启状态变得不切实际。既然整个工作流并没有在单一的某一个事务中,那么遇到异常的时候就不能立刻自动回滚DB状态,意味着回滚处理器需要明确的将DB设置回想要的状态。
最简单回滚处理器可通过在配置在spring活动定义中。看一个例子:

<bean id="blTestRollbackHandler" class="org.broadleafcommerce.core.workflow.state.test.TestRollbackHandler"/>

<bean p:order="1000" id="blVerifyCustomerMaxOfferUsesActivity" class="org.broadleafcommerce.core.offer.service.workflow.VerifyCustomerMaxOfferUsesActivity"/>
<bean p:order="2000" id="blPaymentServiceActivity" class="org.broadleafcommerce.core.checkout.service.workflow.PaymentServiceActivity">
    <property name="rollbackHandler" ref="blTestRollbackHandler"/>
</bean>
<bean p:order="3000" id="blRecordOfferUsageActivity" class="org.broadleafcommerce.core.offer.service.workflow.RecordOfferUsageActivity"/>
<bean p:order="4000" id="blCompleteOrderActivity" class="org.broadleafcommerce.core.checkout.service.workflow.CompleteOrderActivity"/>

<bean id="blCheckoutWorkflow" class="org.broadleafcommerce.core.workflow.SequenceProcessor">
    <property name="processContextFactory">
        <bean class="org.broadleafcommerce.core.checkout.service.workflow.CheckoutProcessContextFactory"/>
    </property>
    <property name="activities">
        <list>
            <ref bean="blVerifyCustomerMaxOfferUsesActivity" />
            <ref bean="blPaymentServiceActivity" />
            <ref bean="blRecordOfferUsageActivity" />
            <ref bean="blCompleteOrderActivity" />
        </list>
    </property>
    <property name="defaultErrorHandler" ref="blDefaultErrorHandler"/>
</bean>

此例,blPaymentServiceActivity配置rollbackHandler 为blTestRollbackHandler,它将在任何子活动发送异常的时候执行。注意,一个活动发生异常时,它自己的回滚处理器不会被执行,只执行那些在工作流中已经处理过的活动的回滚处理器。如果需要,当活动失败的时候,活动有职责修正自身的状态。此例test rollback handler只是简单记录log,但在实际生产环境需要调用支付网关将支付退还或作废。

3.1 RollbackHandler Interface(回滚处理器接口)
所有回滚处理器实现RollbackHandler接口,它只有一个方法。

public void rollbackState(Activity<? extends ProcessContext> activity
    , ProcessContext processContext
    , Map<String, Object> stateConfiguration) throws RollbackFailureException;

接口参数包含所有补偿事务所需的信息,包括活动对象activity和ProcessContext以及stateConfiguration。stateConfiguration包含前面活动执行相关操作的信息,可用于反转之前的操作。stateConfiguration是活动activity执行的时候提供的,后面会再讲。

3.2 ActivityStateManager(活动状态管理器)
ActivityStateManager是一个灵活的组件,用来注册rollback handler回滚处理器和其他附随的状态信息。它可在工作流任何位置直接调用(Activity, Module, ErrorHandler, 等).如下获得其实例:

ActivityStateManagerImpl.getStateManager();

ActivityStateManager接口提供多个方法注册RollbackHandler(回滚处理器)和state(状态)。实际上,你可以重载版本的registerState(其接收region参数)改善回滚行为。Region允许给一个活动多个RollbackHandler添加一个群组标签,这样提供了一个方式可选择性的回滚一部分已注册rollback handler。ActivityStateManager也提供方法清除状态或执行回滚,这些方法可以在任何时间调用执行(或者让系统在之后自动调用这些方法)。
以下实际的例子,PaymentActivity执行完后注册状态。

if (getRollbackHandler() != null && automaticallyRegisterRollbackHandlerForPayment) {
    Map<String, Object> myState = new HashMap<String, Object>();
    if (getStateConfiguration() != null && !getStateConfiguration().isEmpty()) {
        myState.putAll(getStateConfiguration());
    }
    myState.put(ROLLBACK_ACTIONTYPE, seed.getActionType());
    myState.put(ROLLBACK_PAYMENTCONTEXT, paymentContext);
    myState.put(ROLLBACK_RESPONSEITEM, paymentResponseItem);

    ActivityStateManagerImpl.getStateManager().registerState(this, context, getRollbackHandler(), myState);
}

此代码片段看是否在context配置了静态的state。接着将当前支付信息加入到state配置中。最后注册Activity、ProcessContext、RollbackHandler和state MAP对象到ActivityStateManager单例中。

3.3 Implicit vs Explicit Registration (隐式或显式注册)
默认需在代码中(如上例)显示注册RollbackHandler,因为RollbackHandler需要注册特殊线程状态以便获得可行的回滚状态。但对于简单的rollback handler,无需特殊线程状态完成回滚,系统可以配置为隐式(自动的)回滚注册类型。例如:

<bean id="myActivity"
     class="com.test.MyActivity">
    <property name="rollbackHandler" ref="blTestRollbackHandler"/>
    <property name="stateConfiguration">
        <map>
            <entry key="testProperty" value="testValue"/>
        </map>
    </property>
    <property name="automaticallyRegisterRollbackHandler" value="true"/>
</bean>

此例,声明了rollbackHandler和一些静态状态,让系统自动注册RollbackHandler。因为这里我们不关系线程特殊状态,注册RollbackHandler到ActivityStateManager单例中,这些就是所有需要的配置。

3.4 Implicit vs Explicit Rollback (隐式或显式回滚)
默认,工作流执行异常时会自动执行注册的RollbackHandler。但有时可能更希望是通过代码中通过ActivityStateManager明确的调用回滚。要启动“显式回滚”,需在runtime property文件中声明配置:

workflow.auto.rollback.on.error=false

注意,此配置会被每个workflow工作流的autoRollbackOnError属性覆盖。

4.ProcessContext(执行容器)

ProcessContext是一个容器对象,作为activity的传入传出参数。它一般包含workflow工作流相关信息。支付workflow工作流用SimplePaymentContext,它包含PaymentSeed。而PaymentSeed中包含多个PaymentInfo、Order和PaymentResponse这些处理订单请求的对象。另外 ProcessContext提供stopProcess和isStopped方法设置和检查工作流状态。调用stopProcess可不用抛出异常停止工作流继续执行。

public class SimplePaymentContext implements ProcessContext {

    public final static long serialVersionUID = 1L;

    private boolean stopEntireProcess = false;
    private PaymentSeed seedData;

    public void setSeedData(Object seedObject) {
        this.seedData = (PaymentSeed) seedObject;
    }

    public boolean stopProcess() {
        this.stopEntireProcess = true;
        return stopEntireProcess;
    }

    public boolean isStopped() {
        return stopEntireProcess;
    }

    public PaymentSeed getSeedData() {
        return seedData;
    }

}

4.1 Conditional Activity Execution(活动条件执行)
Activity接口根据ProcessContext中的信息决定是否要跳过执行当前活动。Activity再调用前都会调用此方法进行判断,避免工作流重复定义,因为或许一个较大的工作流配置使用同一个ProcessContext的多个配置。

5.Activities(活动)

一个活动是Activity接口的实例,提供执行活动和获取error handler的方法。大多活动对象继承BaseActivity虚类。如PaymentActivity,可获得PaymentContext对象,也可加入到PaymentService中完成支付交易。
一个Java范型定义活动只针对特定的工作流(及只能在特定的ProcessContext中执行),例如下面这个针对blPricingWorkflow工作流的活动:

public class TotalActivity extends BaseActivity<PricingContext> {

    @Override
    public PricingContext execute(PricingContext context) throws Exception {
        Order order = context.getSeedData();
        //compute all totals for the order
        return context;

    }
}

针对blCheckoutWorkflow工作流的活动:

public class CompleteOrderActivity extends BaseActivity<CheckoutContext> {

    @Override
    public CheckoutContext execute(CheckoutContext context) throws Exception {
        CheckoutSeed seed = context.getSeedData();

        seed.getOrder().setStatus(OrderStatus.SUBMITTED);
        seed.getOrder().setOrderNumber(new SimpleDateFormat("yyyyMMddHHmmssS").format(SystemTime.asDate()) + seed.getOrder().getId());
        seed.getOrder().setSubmitDate(Calendar.getInstance().getTime());

        return context;
    }

}

组合活动也是继承于BaseActivity,所以也可加入到工作流活动list列表中。但他们配置上有差异,组合活动需配置一个子工作流。组合活动由父工作流调用,传递到子工作流,子工作流的活动也会依次被调用。所有子工作流和所有异常都绑定到同一个ProcessContext对象,调用stopProcess方法将停止所有嵌套的工作流。使用组合活动,可以方便实现复杂嵌套的工作流。

6.Error Handlers(错误处理器)

Error handlers错误处理器都实现ErrorHandler接口。但发送异常时,可以通过Error handlers做一些必要的事情。如记录异常日志,或释放资源等。之前提过,所有工作流默认使用DefaultErrorHandler,它只是将异常信息输出到System.out后再抛出异常。

public interface ErrorHandler extends BeanNameAware {
    public void handleError(ProcessContext context, Throwable th) throws WorkflowException;
}

7.Removing a Broadleaf Workflow(删除工作流)

如果因为需要扩展功能而需要删除框架默认定义的工作流,可通过定义一个EmptySequenceProcess实例的工作流覆盖默认配置(但我们不建议这样做)。如因你已继承了OrderService覆盖了performCheckout()方法,你想去掉blCheckoutWorkflow工作流,可以通过如下定义去掉:

<bean id="blCheckoutWorkflow" class="org.broadleafcommerce.core.workflow.EmptySequenceProcessor" />

8.Provided Workflows(已提供的工作流)

以下是一些框架提供的工作流:

blAddItemWorkflow 添加商品到购物车流程
blUpdateItemWorkflow 更新购物车商品流程
blRemoveItemWorkflow 删除购物车商品流程
blPricingWorkflow 被 blPricingService(被OrderService调用)使用 来计算订单价格
blCheckoutWorkflow 订单中被blCheckoutService 调用以完成订单结算(支付、标记减少存货、改变状态为SUBMITTED等等)
blPaymentWorkflow blCheckoutWorkflow 中的 CompositeActivity ,其运行多种支付方式

订单支付有多种发生情况也有多种支付方式。blPaymentWorkflow 是一个活动组件,可将默认的支付配置流程进行包装(即blAuthorizeAndDebitWorkflow)。一般情况,如果在订单送货之前无需支付,可将blPaymentWorkflow 的workflow属性改为blAuthorizeWorkflow ,它只会简单的通过支付提供者对客户的支付信息进行认证。当到快递包装的时候再执行blDebitWorkflow完成支付。支付相关流程列举如下:
broadleaf_framework_workflow

9.参考

1. Workflows and Activities
2.bl-framework-applicationContext-workflow.xml

Merge configuration是通过web.xml处理的,broadleaf的merge功能智能的合并一个或多个spring配置文件。合并的最终版本才被传递给spring处理。为了让broadleaf知道要merge那些文件,你必须在web.xml中声明。首先,我们提供一个patchConfigLocation的参数,表示你的spring context文件列表。你可以任意添加,它们将被合并到broadleaf配置中。

<context-param>
    <param-name>patchConfigLocation</param-name>
    <param-value>
        /WEB-INF/applicationContext-custom.xml
        /WEB-INF/applicationContext-email.xml
        /WEB-INF/applicationContext-search.xml
        /WEB-INF/applicationContext-security.xml
    </param-value>
</context-param>

其次,需声明org.broadleafcommerce.common.web.extensibility.MergeContextLoaderListener这个listener。它的职责是负责初始化broadleaf内部context信息和你声明的context文件直接的merge。

<listener>
    <listener-class>org.broadleafcommerce.common.web.extensibility.MergeContextLoaderListener</listener-class>
</listener>

当merge遇到有冲突的bean声明的时候,broadleaf知道如何处理合并或覆盖。
如,你声明了一个id为blOrderService的bean,broadleaf将使用你声明的bean。但是,某一些情况不会覆盖,而是会合并bean的内容。如以下blWebTemplateEngine声明:

<bean id="blWebTemplateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
    <property name="dialects">
        <set>
            <ref bean="thymeleafSpringStandardDialect" />
            <ref bean="blDialect" />
        </set>
    </property>
</bean>

如果你想添加你自己的dialect,你可以简单的在你的context文件中添加以下声明。

<bean id="blWebTemplateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
    <property name="dialects">
        <set>
            <ref bean="myCustomDialect" />
        </set>
    </property>
</bean>

我们已经表明这个bean的属性为可merge的属性,而不是覆盖。所以最终,被spring处理的bean将是如下的样子:

<bean id="blWebTemplateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
    <property name="dialects">
        <set>
            <ref bean="thymeleafSpringStandardDialect" />
            <ref bean="blDialect" />
            <ref bean="myCustomDialect" />
        </set>
    </property>
</bean>

参考

1.Broadleaf’s Unique Application Context Merge Process Explained
2.Broadleaf Merge Process 2.0
3.Merge Configuration