2009年7月31日星期五

CMSEntity之六,内容提交和产品定价的规则

两个重要概念:
1. 定价方法(price method):
  • 免费试用(Demo):用户免费使用
  • 购买(Purchase):一次性购买
  • 订购(Subscription):包月

2. 定价基础(price basis):
  • 使用次数(number of uses):应用可使用的最大次数
  • 使用期限(expiration data):到期截止日
  • 使用天数(number of days):可使用的天数
  • 使用时长(elapsed time):应用在终端上可以使用的时长,以分钟为单位

产品以这两个概念为定价的基础,剩下两个辅助概念,一个是price,一个是price value,这些就是四元组。price表示实际的费用,例如某个产品是5元10次,那么price就是5元,而price value就是10。

规则:
  • 三种定价方法相互独立,也就是说给某个内容定价时,可以使用其中的一个,两个,或者三个。
  • 选择免费试用(Demo)方法时
    • 只有三种定价基础可以用,即使用次数,使用天数,使用时长。使用期限不可用
    • 在三种可用的定价基础中,只能选择其中一种,其缺省值为不超过10次,不超过10分钟或不超过一天。
  • 在选择订购(subscription)定价方法时
    • 定价基础是可以忽略的
  • 在选择购买(purchase)定价方法时
    • 四种定价基础(使用次数,使用期限,使用天数,使用时长)全部可用
    • 虽然四种定价基础都可用,但是只能选择其中的一种使用,例如,如果选用使用次数,那就不能再选择使用天数
    • 对 于某个被选择的定价基础,最多可用三个price value(这一块可用参考代码,常量PURCHASE_PRICE_OPTION_SIZE应该就是这么来的),例如:5元包10天,10元包60天, 和20元无限使用的三个产品是允许的。但是,5元包10天,10元包60天,15元包100天和20元无限使用的四个产品是不允许的。
    • 无限使用的购买适用于任意一种定价基础。例如:使用次数(无限次),使用天数(无限天),使用时长(无限时长),使用期限(无限期限)。

合法产品示例:

内容1有4个产品:

* 产品11:免费试用(Demo)1天
* 产品12:2元包月
* 产品13:2元包10天
* 产品14:20元无限使用

内容2有5个产品:

* 产品21:免费试用(Demo)10分钟
* 产品22:3元包月
* 产品23:2元包5次
* 产品24:5元包15次
* 产品25:20元无限使用


非法产品的示例:

内容3有4个产品:

* 产品31:免费试用(Demo)15次
* 产品32:2元包月
* 产品33:2元包10天
* 产品34:20元无限使用

内容4有4个产品:

* 产品41:免费试用(Demo)10分钟
* 产品42:3元包月
* 产品43:2元包5次
* 产品44:5元包10天

内容5有5个产品:

* 产品51:3元包月
* 产品52:2元包1小时
* 产品53:5元包10小时
* 产品54:10元包40小时
* 产品55:20元无限使用

2009年7月30日星期四

CMSEntity之五,price plan的生成

接昨日:
prepareMfgPricePlan用于构建manufacture price plan文件,它会调用createMfgPriceMethod方 法,前面提到了,方法createPriceMethod会创建price method,这些信息会被manufacturer price plan文件和operation price plan文件使用,当createPriceMethod创建好price method后,这些信息保存在ProductPriceMethodVO,查看ProductPriceMethodVO的成员可以,其实里面放的就是四元组:price method,price value,price basis和price。同时createPriceMethod也会调用setPriceMethod将这些信息保存到ProductVO(ProductVO有一个ProductPriceMethodVO型的成员priceMethod)。

再 回到prepareMfgPricePlan,它调用createMfgPriceMethod方法,createMfgPriceMethod其实就是 从ProductVO中得到ProductPriceMethodVO信息,然后将这些信息分别填到MfgPriceMethodTypeVO中去。 prepareMfgPricePlan然后就是一个逻辑:因为调用createMfgPriceMethod后得到一个 MfgPriceMethodTypeVO对象mpmo,然后用这个对象的Method_type作为一个key,去mpmoMap里去查找,看是能查 到,如果能查到,将查到的值赋给prevMpmo。

为了清楚地描述这个逻辑,现在做下面的定义:
当前的MfgPriceMethodTypeVO对象:mpmo
原有的MfgPriceMethodTypeVO对象:prevMpmo(如果原有的对象存在,这是用Method_type作为一个key,去mpmoMap里查找的)
四元组:price method,price,price value,price basis
price mothod可能的值有:Demo,Purchase,Subscription
price valude可能的值有:数值(1,2,3等),UNLIMITED
price basis可能的值有:USES,TIME,DAYS
OPTION:是Manufacturer price plan文件的一个tag,它包含四元组的price和price value两项。

逻辑:
1,如果当前的product value是UNLIMITED,那么将当前的productVO和method type存放到一个map里:productsBasisUnlimited.put(key, pv);

2,判断是否有prevMpmo,如果有,进入步骤3,如果没有,直接到步骤5。

3.a, 当prevMpmo存在时,如果当前的method type是Purchase,将optionSize赋值为3:optionSize = Constants.PURCHASE_PRICE_OPTION_SIZE;(这里需要解释的是为什么常量 PURCHASE_PRICE_OPTION_SIZE为3而不是别的值,今天看到“关于BREW内容提交和产品定价的规则说明”邮件才知道,这是 BREW规定的。“对于任意一个被选中的定价基础,BREW允许最多三个定价基础值”,所以常量PURCHASE_PRICE_OPTION_SIZE的 值为3).

3.b,当prevMpmo存在时,如果当前的method type不是Purchase,将optionSize赋值为1:optionSize = Constants.DEMO_PRICE_OPTION_SIZE(解释见上)。

4,判读mpmo和prevMpmo的price basis是否相等,相等进入4.1,不等进入4.2

4.1.a,获取prevMpmo的OPTION的size,如果大于optionSize,报错

4.1.b, 如果小于optionSize,调用方法checkDuplicatePriceValue判断prevMpmo和mpmo是否相等(也就是判断两者包含 的四元组是否完全相等),如果相等就抛出异常。如果不等,将当前mpmo的OPTINO加入到原有prevMpmo的OPTION 中:prevMpmo.getOPTION().addAll(mpmo.getOPTION());

4.2.a,price basis不相等,调用方法updateBasisUnlimited,这个方法判断prevMpmo和mpmo的price basis是否是UNLIMITED,如果是就将其price basis赋给另一方,如果都不是,返回false。

4.2.b,如果updateBasisUnlimited返回true,将当前mpmo的OPTINO加入到原有prevMpmo的OPTION中:prevMpmo.getOPTION().addAll(mpmo.getOPTION());否则报错。

5,将当前mpmo加入map:mpmoMap.put(key, mpmo);

逻辑完后,prepareMfgPricePlan会调用ProductParserImpl类的方法prepareMfgPricePlanMeta去生成meta文件,到此为止。

prepareMfgPricePlan 执行完毕后,返回到processCntProductAssocList,它会调用submitMfgPricePlan方法,这个方法最终会调用 BMCClientImpl类的createMfrPricePlan,这个方法的功能是:This method will invoke the BMC service and submits the manufacturer price plan。到此为止,manufacturer price plan文件的生成及提交就完成了。

processCntProductAssocList继续调用prepareOprPricePlan来生成operation price plan文件。prepareOprPricePlan调用createOprPriceMethod来 说生成price method,这个方法和createMfgPriceMethod(如前述)的作用是一样的。prepareOprPricePlan后面的工作就很简 单了。这里有个问题,相对于prepareMfgPricePlan,prepareOprPricePlan的逻辑非常简单,根本原因就是 MfgPriceMethodTypeVO和OprPricePlanMethodTypeVO的成员虽然很相似,都是四元组,但是两者有一个很大的区别 是:OprPricePlanMethodTypeVO的PricePointTypeVO变量是一个值,而MfgPriceMethodTypeVO中 是一个PricePointTypeVO的List。这导致了prepareMfgPricePlan比prepareOprPricePlan要简单得 多。

2009年7月29日星期三

CMSEntity之四,三类文件的处理

ProductHandler类是真正从product,product content和charging info转换为price plan,入口可以认为是processPricePlans方法。

首 先它调用getUnprocessedCntProductAssoc,这个方法的功能是:1.Get Unprocessed Content and Product associate VO from DB 2.Sort the unprocessed list。这个方法涉及到了ContentProductAssocVO,这里有个机制去判断一个ContentProductAssocVO是否已经被 处理过,逻辑上应该是先比较几个ID:IsmpContentId,IsmpProductId和OpFlag,如果相同,再比较时间戳。但是具体的逻辑 没细看。

获得Content and Product associate VO list后,processPricePlans继续调用processCntProductAssocList, 这个方法的功能是:Fetch all the content and product pair in the content_product_mapping table for this particular content_id。注意这里面提到了CONTENT_PROD_MAP table,这个表很重要。processCntProductAssocList方法调用fetchContent来获得ContentItemVO,注意fetchContent的参数是contentID。

然后processCntProductAssocList调用fetchProcducts, 这个方法有点复杂,它用参数返回好几个map(cntProducts,deletedCntProdMap和omittedCntProdMap,把哪 些product放到哪一个map里是更加opflag来判断的,另外,处理出错的情况都放到omittedCntProdMap里去。)。这个方法会调 用createPriceMethod,这个方法非常重要,它用来生成price method,在manufacture price plan和operation price plan里都能看见(method标签),它的参数有ChargingPolicyVO和ProductVO,前者保存的是电信的收费信息。该方法首先得到charg info的收费方式,在电信文档的附录D中有描述,包括BREW系统原有计费策略和与之相应的ISMP产品计费策略。这些策略一共有九种,但是在createPriceMethod里只处理了5种情况,不知道为什么。

在 fetchProducts的最后有一个逻辑,当OpFlag为delete时,把product放入deletedCntProdMap,如果 OpFlag不是delete,那就是add,但是在之前还要进行判断,如果要add的项也被包含在deletedCntProdMap(两者的 productID相同,也就是一个product既已经被包括在deletedCntProdMap,现在又要将其add),那么就要判断add的项和 delete的项两者的时间戳,如果要add项的时间戳要早于要delete项的时间戳,那就什么也不干(表示这个product最终是要被删除的),反 之,就将给product加入到cntProducts中。

processCntProductAssocList会调用fetchProducts两次,然后processCntProductAssocList调用prepareMfgPricePlan方 法,它的功能是:Prepare the MFG Price plan and generate the meta file. The ISMP price plan to BMC price plan mapping logic are locate inside this method. 说明这个方法很重要,manufacturer price plan文件就是由它构建的。

2009年7月28日星期二

CMSEntity之三,谈话整理

昨天下午谈话的整理:
电信这边的产品信息包括:product info,product content和charging info。电信的收费信息是针对product而言的,也就是一个产品它有计费信息,而不是一个内容或者一个电信item有它的计费信息。而BREW这 边,计费信息是针对manufacturer partner number的,每个manufacturer partner number下面包含有多个item,一个manufacturer partner number就相当于一个组,包含多个BREW item,计费信息就是针对这个组的。目前是把manufacturer partner number和电信那边的content视为等价,但其实两者是不同概念。

price plan是BREW这边的概念,它是由product info,product content和charging info这三个文件导出的,四元组:price basis,price method,price value和就包含在price plan中,所以price plan是非常重要的。

Meta.xml和item.zip一起再压缩成一个zip给BMC(这个Meta.xml和item.zip最好能看看里面的内容)。

数 据库的几个表,ISMP_Content,ISMP_Item等等(ISMP Content number什么的,作为key,这个也不知道是什么东西),这个没搞清楚,还有对应product info,product content和charging info每个文件都有一个表,这些表要弄清楚。

对BREW手机和ADS来说,catalog是有多个 的,每个catalog是针对语言来说的,例如英语有英语的一个catalog,汉语有汉语的一个catalog,其它语言有其自己的catalog。每 个catalog可以想象其为一颗树,和pc机上的目录结构是一样的,catalog下有category,也有subcatalog,catalog下 也可以有item,subcatalog下有item等等。

manufacture price plan和operation price plan是高通的遗留东西,但是现在还在使用,两者的区别是operation price plan有change list,但是manufacture price plan没有。

在CMSEntity里的dao包中,有关的表有:
PRODUCT_INFO table,PRODUCT_CHARGE_INFO table,PRODUCT_CHARGE_MODE table,CONTENT_PROD_MAP table,ISMP_ITEM table,ISMP_ITEM_MODELS table,ISMP_ITEM_LANGUAGES table,CHANGE_LIST table,Ismp_Region table等等,目前重点关注PRODUCT_INFO table,PRODUCT_CHARGE_INFO table,PRODUCT_CHARGE_MODE table,CONTENT_PROD_MAP table。

2009年7月27日星期一

CMSEntity之二

com.qualcomm.bss.bsg.cms.job.ContentProcessorJob.execute会调用 com.qualcomm.bss.bsg.cms.process.ContentHandler.invokeProcess,我觉得 ContentHandler应该是真正干活的,它来解析map文件并把内容保存到数据库中。但是 ContentHandler.invokeProcess很简单,它调用了 ContentHandler.commonStorageHandling,在这个方法中对ISMPItemVO,ContentItemVO进行了一 番操作,但是也没看出头绪。

TerminalInfoLoadJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.TerminalInfoLoadJob.execute->com.qualcomm.bss.bsg.cms.job.TerminalInfoLoadJob.loadData

ContentFetcherJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.ContentFetcherJob.execute->com.qualcomm.bss.bsg.cms.process.ContentAdaptor.invokeProcess
->com.qualcomm.bss.bsg.cms.CDParserImpl.parseCDFile

ContentProcessorJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.ContentProcessorJob.execute->com.qualcomm.bss.bsg.cms.process.ContentHandler.invokeProcess->
com.qualcomm.bss.bsg.cms.process.ContentHandler.commonStorageHandling

ProductInfoFetchJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.ProductInfoFetchJob.execute->com.qualcomm.bss.bsg.cms.process.prodAdaptor.processProductFiles

ProductAdaptor里的方法大有可为,很多事情都是在ProductAdaptor里 面做的,看ProductAdaptor类前面的解释,它的功能包括:1,获取product info文件并保存到本地文件夹;2,解析product info文件并保存到数据库;3,获取product content文件并保存到本地文件夹;4,解析product content文件并将数据保存至数据库;5,获取charge info文件并保存到本地文件夹;6,解析charge info文件并将数据保存至数据库;
由此可见,三个map文件都是在ProductAdaptor类里处理的,所以有必要对ProductAdaptor类中的每个方法都细读。

ProductAdaptor.processProductFiles, 这个方法的功能是用ftp获取三个map文件,即product info,product content和charging info。这个方法里用到了类ProductFtpHandler,并对ProductParser类进行了实例化。ProductFtpHandler 是用来获取相应的map文件的,调用它的fetchProductInfoFilesfetchProductContentFilesfetchChargingInfoFiles就能获得相应的文件。每当获得相应的文件后,processProductFiles会调用方法来解析获得的文件,这些方法包括processProductInfoFileprocessProductContentFilesprocessChargingFiles,从名字就很好判断它们的作用。

前面提到processProductFiles实例化了ProductParser,这个类顾名思义,是用来解析product info文件的,但是看这个类的解释,又不仅仅是。ProductParser类只是一个接口,它的实现类是ProductParserImpl, 这个类最前面的注释说:“This class is used to parse product info ,Product content mapping , charging policy info files. Also creates price plan xml files.”,从这段注释可知这个ProductParserImpl不仅仅是解析product info文件,它把三个map文件都解析了,同时生成了price plan文件。前面提到的processProductInfoFileprocessProductContentFilesprocessChargingFiles方法都使用了ProductParser的实例来解析相应的文件。

ProcessProductInfoFile 先是调用了ProductParserImpl.parseProductFile解析product info文件,它的返回值是ProductInfoVO,然后ProcessProductInfoFile再调用 ProductDAO.addProductInfo将ProductInfoVO的信息写入到数据库中。
processProductContentFiles 处理product content文件,它的处理过程和ProcessProductInfoFile差不多,先是调用 ProductParserImpl.parseProductContentFile解析product content文件,返回值是ProductContentVO,在将ProductContentVO的信息保存到数据库之 前,processProductContentFiles还调用了applyProductContent方法,这个方法是“used to apply products to contents”,不知道是什么意思。但是看代码,这里是生成product和content的关系,这些关系都存放在contentProductAssocVO,这是一个很重要的对象,后面的操作要经常使用这个对象。然后ProcessProductInfoFile再调用ProductDAO.applyProductContent将ProductContentVO的信息写入到数据库中。
processChargingFiles的流程和前两个相似,不赘述。

ProductProcessJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.ProductProcessJob.execute->com.qualcomm.bss.bsg.cms.process.ProductHandler.processPricePlans

ProductHandler类是生从电信的那一套(也就是product,content,item和charging info)转换成BREW识别的一套(item和price handle)的关键。具体的实现与数据库非常相关,目前暂时还没研究。

ReleaseProcessJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.ReleaseProcessJob.execute->com.qualcomm.bss.bsg.cms.process.ReleaseHandler.invokeProcess

MasterSwitchJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.scheduleMasterSwitch->

FeedBackFileUpLoaderJob:
com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.scheduleFeedBackUpLoadProcess->com.qualcomm.bss.bsg.cms.job.FeedBackFileUpLoaderJob

2009年7月24日星期五

CMSEntity之一,各种Job和VO

contentFetcherJob
ContentProcessorJob

ProductInfoFetchJob
ProductProcessJob

com.chinatelecom.ismp.contentpublish.req包的类ContentSyncNotifyReq

com.chinatelecom.ismp.contentpublish.ContentPublishedReqAdapter
com.chinatelecom.ismp.contentpublish包里的方法ContentPublishedReqAdapterSoapBindingImpl::contentSyncNotify

前 面的关系有点乱,到现在还没理清,按流程应该是ISMP先给BSG发一个contentSyncNotify(一个SOAP call),BSG收到这个notify后去解析它的内容,获得content description文件的存放地址和目录,但是现在还不知道是在哪里处理这个notify的,本以为是在 com.qualcomm.bss.bsg.cms.servicelaye.BSGWebServiceServlet处理这个notify的,但是看 了这个类的doGet方法,感觉这个方法没有处理notify。CMSWebServices和CMSEntity两个工程里面都没找到,这个暂且放下, 以后再找找。

目前看com.chinatelecom.ismp.contentpublish。ContentPublishedReqAdapterSoapBindingImpl::contentSyncNotify是处理notify的(但不知道它是如何被调用的,好像用了一个axis2的东西,google了一下,这是Apache的一个包,但是它是干什么用的没细看)。
方法contentSyncNotify使用了contentSyncNotifyReq, 这个类其实无甚特别,但是其中有一段代码搞得我很迷惑:static{... ...},这段代码和org.apache.axis.description有关,不知道是干嘛的,留到后面再研究。然后 contentSyncNotify调用saveContentSyncInfo将得到的信息保存到数据库中,这个应该是content description文件的存放地址和目录。

com.qualcomm.bss.bsg.cms.parser是一个接口,类CDParserImpl实现了这个接口,里面有个方法:parseCDFile,这个方法就是用来解析content description文件的。
com.qualcomm.bss.bsg.cms.process.ContentAdaptor类中的方法:invokeProcess,调用了parseCDFile。
然 后com.qualcomm.bss.bsg.cms.job.ContentFetcherJob的方法execute中会调用 contentAdaptor.invokeProcess。这个ContentFetcherJob是从StatefulJob继承的类,也就是它是一 个Quterz的类,周期性地执行,
在com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet的init方法中会创建ContentFetcherJob,并对它进行调度。

所 以这几步的流程 是:com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init->com.qualcomm.bss.bsg.cms.job.ContentFetcherJob.execute->com.qualcomm.bss.bsg.cms.process.ContentAdaptor.invokeProcess
->com.qualcomm.bss.bsg.cms.CDParserImpl.parseCDFile。

在 ContentAdaptor类里有个方法fetchFile,这个方法其实就是从给定的地址中用FTP去取文件,invokeProcess调用 fetchFile去获取content description文件,content description文件的地址,在前面提到,content description文件的存放地址和目录已经保存在数据库中了。

content description文件解析完后,ContentAdaptor::invokeProcess会调用fetchZipFile去获取zip文件,这 些zip文件就是product_info.map,content_product.map和charge_info.map文件了。文件被取回 后,ContentAdaptor::invokeProcess还调用了:
batchStatusVO.setProcStatCodeID(Constants.BATCH_STATUS_SAVED);
updateBatchStatus(batchStatusVO);
这 两个调用貌似很重要,因为zip文件取回后就要进入下一步的处理,即将这三个map文件解析并将相应的内容填到数据库中,但是处理三个map文件也是一个 job,即ContentProcessJob,这个job也是在 com.qualcomm.bss.bsg.cms.servicelayer.BSGCMSServlet.init被创建初始化并调度的。也就是说, 它一直周期性地试图去处理三个map文件,但是并不是时时刻刻都有map文件需要处理(因为不是每时每刻都有产品要提交到BMC上去),所以当有新的 map文件被取回后,必须要有一种方法通知ContentProcessJob,现在有新的map文件需要处理了。这个方法应该就是 ContentAdaptor::invokeProcess在取回map文件后,向数据库里写了某些信息,就相当于置了某些标志位,然后 ContentProcessJob在处理过程中首先是查询数据库,获得信息得知是否有新的map文件需要处理。

com.qualcomm.bss.bsg.cms.job.ContentProcessorJob
com.qualcomm.bss.bsg.cms.process.invokeProcess
com.qualcomm.bss.bsg.cms.process.getSuitableBatch

ISMPItemVO
ContentItemVO

2009年7月22日星期三

ISMPEntity之三,2.x和3.x对包月非包月业务的处理流程

包com.qualcomm.bss.bsg.ismpentity.ismpcore中的类ProductPriceObject值得关注,它里面包含了几乎所有重要信息:priceHandle,productID, priceMethod,priceBasis,optionPrice(就是pricevalue),regionId,contentId和optionValue。所以得到ProductPriceObject的实例就可以知道productID等信息。
在 包com.qualcomm.bss.bsg.ismpentity.devices的类Device中有个方 法:getProductPriceObject,可以用它获得ProductPriceObject,getProductPriceObject其实 是去查询数据库来获得ProductPriceObject:
productPriceObject = operationalDataDAO.getPriceHandleMap(String.valueOf(priceHandle), itemId, regionId);

如 果在数据库里查找不到,那么getProductPriceObject会去ISMPConfig里查找postactivition的地 址:String strPostACtivationIPANdURL = ISMPConfig.postActivationURL,根据这个地址,调用方法submitRequest,实际上是去这个地址去访问,返回值是个 字符串,里面包含了ProductPriceObject的所有信息。

3.x手机,下载非包月应用,处理的流程:
1,用户选择好应用后,点击下载
2,手机发出下载鉴权请求给ADS
3,ADS将请求发给BSG
4,经过BSG的router,最终ISMPEntity收到鉴权请求。
5,RequestHandler::doGet->ISMPEntity::invokeService->Locator::locateDeviceHandle->Device::handleRequest
6,Device::handleRequest根据请求的类型来做相应处理,有三种类型的请求:
public enum RequestTypeStatusCodes {
PurAuth,
MsgDownloadAck,
DeleteAck
}
因为是非包月下载请求,所以这里是PurAuth。
7,用Device::getProductPriceObject获得ProductPriceObject对象,该包含了pricehandle,productID等等信息。
8,进入PurAuth处理流程,判读是否包月(这里是非包月),进入非包月处理分支,获得PurchaseAuthorizationHandlerImpl对象。
9, 进入PurchaseAuthorizationHandlerImpl::handleRequest方法,生成 messageID(messageID是联系request和response的桥梁,ISMPEntity会有多个不同的request放在 queue里并发送给ISMP,同时ISMP也会有多个不同的response,request和response必须成对出现,有request必须有 response,某对request和response之间就靠messageID联系,它们必有相同的messageID)并和其它信息 (ismi,itemID,pricehandle等)一起保存到数据库。
10,调用preparePDU构建PDU,然后调用PDURequestHandlerInterface::sendPDU发送PDU。

3.x手机,下载包月应用,处理的流程:
前7步和3.x非包月相同。
8, 进入PurAuth处理流程,判读是包月,进入包月处理分支,获得CreateSubscriptionHandlerImpl对象。
9,进入CreateSubscriptionHandlerImpl::handleRequest方法,包月的创建是用SOAP的,所以调用了方法prepareSOAPRequest去构建并发送SOAP request。

当 ISMP返回成功后,3.x下载包月应用的流程应该和下载非包月的流程一样,再次进入 PurchaseAuthorizationHandlerImpl::handleRequest方法去发送PDU,但是3.x如果再次进入 PurchaseAuthorizationHandlerImpl的处理流程,目前还没有看出来(参考电信文档第8.3.2节"有下载鉴权的包月应用下 载",BSG和ISMP之间是先用SOAP发送了createSubscriptionReq和createSubscriptionResp,然后再和 下载非包月应用一样,发送AuthPrice request和response。我估计因为SOAP还是建立在http协议之上的,所以当ISMP发送createSubscription response给BSG后,ISMPEntitiy还是先进入RequestHandler::doGet,后面的流程就和下载非包月一样了)。

2.x手机,下载非包月应用,处理流程:
前10步和3.x非包月相同。
11,AuthPrice的request PDU发送后,PurchaseAuthorizationHandlerImpl::handleRequest返回到TwoXDeviceImpl::handleRequest。
12, 调用Device::duplicateMsgDownloadAckCheck,这个方法是用来验证MsgDownloadAck是否是已经发送过的 (duplicated),如果是,表明MsgDownloadAck已经被处理过,不用再处理。如果不是,到13步。
13,进入DownloadConfirmationHandlerImpl,调用DownloadConfirmationHandlerImpl::handleRequest方法,发送AuthPricecnfm的PDU。

2009年7月21日星期二

ISMPEntity之二,PDU收发

PDU(Protocol Data Unit 协议数据单元),可以理解为传输协议数据的包,因为电信定义了ISMAP协议,必然有相应格式的包来传输协议的数据,这很好理解,每种协议有其各自的帧格 式。这个可以查看中国电信的文档,附录里有比较详细的介绍。PDU主要分消息头和消息体,消息头包括:PDU的长度,命令ID和序号。命令ID有好多种, 其中有:id_BindReq,id_EnquireLinkReq,id_AuthPriceReq等等,从这些命令ID的名字就一目了然,知道它们是 的作用是什么。

在com.qualcomm.bss.bsg.ismpentity.pdu包中,PDU的基类是BasePDU,是个抽 象类,它的作用也就是给PDU搭个框架,把PDU的公共部分都设置了,剩下和各个命令相关的部分都放到特定命令里去设置。PDU的派生类众多,不过应该都 是大同小异,例如派生类:BindResponsePDU,EnquireLinkRequestPDU等等,从名字就知道它们是构造何种PDU的。

包 com.qualcomm.bss.bsg.ismpentity.ismaphandler有点搞头,里面有些类值得关注,首先是接口 PDURequestHandlerInterface,它包含了一些方法,它们用于发送异步PDU给ISMP,这说明ISMPEntity与ISMP之 间是用异步PDU进行通信的?这个还有待证实。类PDURequestHandlerImpl实现了接口 PDURequestHandlerInterface,具体的实现没细看。

类PDUConnection,对socket进行了封装, 从它的方法:connect和disconnect就很明显,不过这个类的职责并不明晰,它不但包括了对socket的封装,而且它还会发送bind和 unbind的PDU,这一点我决定没设计好,这两个应该是不相关的。

类PDUTransmitter是用来发送PDU的,它用到了 PDUConnection,这是理所当然,它还用到了一个类:PDUWrapper,这个类我没看太明白,不知道有什么用,不过它比较简单,可以暂且放 在一边。PDUTransmitter有个方法generateSeqId产生所谓的sequence ID,我估计这个sequence ID就是电信文档里提到的“用于请求和响应间保持联系的序号”,不过这还是有待证实。在发送的时候用方法 associateSeqToPDUWrapper将sequence ID和PDUWrapper进行绑定,它们是一个键值对,每个sequence ID都对应唯一的PDUWrapper。这个键值对是有用的,在类PDUReceiver中要用到的。

类PDUReceiver和PDUTransmitter做相反的事。

PDU 这一块现在还是有点迷糊,目前看,PDU的操作其实也可以分为两层,上层包括PDURequestsHandlerInterface和 PDURequestsHandlerImpl,这两个类一个是接口一个是实现,它们是给上层用的,其中有个方法是sendPDU,这个方法用来发送 PDU。而其实待发的PDU加入到一个队列中PDUQueue,最终将某个PDU发送出去的是类PDUTransmitter实现的。

调用的大致流程:
RequestHandler::doGet->ISMPEntity::invokeService->Locator::locateDeviceHandle->Device::handleRequest->HandlerInterface::handleRequest->PDURequestHandlerInterface::sendPDU->pduQueue::addToQueue-
>PDUTransmitter->sendPDUBytesToISMP

2009年7月20日星期一

ISMPEntity之一

ISMPEntity的主要作用:
与ISMP进行绑定或者解绑定(bind,unbind),并维持心跳(heartbeat);管理BDS(BREW Delivery System)与ISMP之间的通信信道,BDS的实时流(用户下载,订购等信息)都是通过ISMPEntity转达给ISMP的。

request 实时流的入口:RequestHandler.java: public void doGet(HttpServletRequest request, HttpServletResponse response),doGet中会调用ISMPEntity.java的方法invokeService,这个方法比较重要,所有手机过来的实时 request,都要经过这个方法,该方法根据它的参数来确认发送request的手持终端的类型,通过这个类型,从类Locator获得相应设备的句 柄,调用相应设备的方法handleRequest来处理终端的request。
其实这里使用的就是多态,设备有多种类型,具体设备都是从一个基类派生的,invokeService根据它的参数确定相应的设备,然后调用设备的方法来处理request,实质上就是使用多态来实现。

前面提到的设备的基类就是DeviceInterface,是位于com.qualcomm.bss.bsg.ismpentity.devices包里的,它是一个接口,里面只有一个方法handleRequest, 抽象类Device实现了这个接口。这个类里面的一些方法有必要关注,但目前还没细看。类TwoXDeviceImpl和 ThreeXDeviceImpl都是从Device派生的,这两个类的名字就很明显了,老谭曾经提到,目前高通有两个平台:较老的2系列和较新的3系 列,这两个类就分别对应的2系列设备和3系列设备。

类HandlerInterface也颇为重要,位于 com.qualcomm.bss.bsg.ismpentity.handler包,它也是个接口,和DeviceInterface本质是相似的,它 对应的是服务的概念,类AppDeletionHandlerImpl,CreateSubscriptionHandlerImpl和 DownloadConfirmationHandlerImpl从HandlerInterface派生,根据三个派生类的名字也可以大概明白它们的作 用:一个处理删除,一个处理包月的创建,一个处理下载确认。注意HandlerInterface接口中也有个很重要的方法叫handleRequest,这个和前面提到的设备的handleRequest不要混淆。在HandlerInterface接口的handleRequest中对收到的request进行了解析,request参考电信文档第八章和第九章,里面有详细request格式介绍。handleRequest按照这个格式进行解析,因为要取出相应的信息来构建PDU

2009年7月17日星期五

CommonProcess之二

commonce process模块是用Quartz进行调度的,网上说它是个“作业调度集”,因为common process模块是用轮询的方式去获得三类文件的,所以考虑把对三类文件的轮询看作是各个作业,用Quartz来进行调度。 BSGCommonJob.java里的类BSGCommonJob实现了接口StatefulJob,这个接口也是Quartz里的。

有 了GSGCommonJob类,还要有一个调度类来调度作业,根据Quartz框架,这就涉及到触发器,有两个最常用的是SimpleTrigger和 CronTrigger,在代码里用的是SimpleTrigger,调度类是BSGCommonScheduler.java的 BSGCommonScheduler,其中的方法scheduleJob使用了SimpleTrigger。

在 BSGCommonScheduler里使用了JobConfig,这个类包含了所有关于轮询获取三类文件业务的属性和参数,这些属性和参数是从一个配置 文件CommonConfig.xml读取的,这个文件中设置了文件存放的ftp地址,轮询的间隔等等关键的参数。BSGCommonScheduler 要根据这些信息来调度。

servicelayer包里面的两个文件目前看还不太清楚作用,ConfigServlet.java应该是从 CommonConfig.xml里读出配置项并设置。前面提到的调度BSGCommonScheduler是在StartupServlet.java 里的init方法里设置的。

BSGCommonProcess及其三个派生类是真正干活的,各个××××process类几乎包含了所有 操作所需要的内容,包括:数据库,ftp,文件的解析(xml文件的解析是用SAX方式)等等。从ftp相应的地址处获取文件是在方法 initiateFetch完成的,它从ftp地址获取所有的文件。文件被取回后,文件名和时间戳等信息会被存放到数据库的相应的表中,这些操作是用 “VO”包中的类完成的。所有的已经处理或者未处理的文件的名字应该都存储在数据库的相应的表中,这样就可以清楚地知道当前文件处理的状态。文件的处理是 在方法processFiles中,处理的细节有点像状态机,文件有好几个状 态:BSGCommonConstants.FILE_START,BSGCommonConstants.FILE_PROCESSED,BSGCommonConstants.FILE_CORRUPT 和BSGCommonConstants.FILE_FINISH,不同的状态有不同的方法处理,最终使得文件到达 BSGCommonConstants.FILE_FINISH状态。

handler包里面的代码都是用来处理相应xml文件的,因为common process是使用SAX的方式解析xml的,所以类BSGCommonHandler是从DefaultHandler派生的,

request实时流的入口:public void doGet(HttpServletRequest request, HttpServletResponse response)

2009年7月16日星期四

CommonProcess之一

目录D:\Qualcomm\Design\Dec - 05 - 2008\index.htm存放了类图,目前来看是老谭机器上最新的了。

首先我看的是commonprocess的代码,因为commonprocess只是和ftp和DB打交道,看起来可能轻松些。结合类图看代码,可能是因为文档比较老的缘故,和代码并不太一致。BSGCommonJob.java里的类BSGCommonJob, 目前看可能是commonprocesss的入口,至少是一个入口,它就像一个分发器(dispatch),里面有是switch,因为 commonprocess从NMSC里获得三类文件,所以switch里分别处理了四类宏,相对应的处理方法是 ImsiMsisdnProcess,NumberSegmentationProcess和TerminalInfoProcess。

BSGCommonPress是个基类,有几个派生类从它继承,实现相应的文件处理,例如ImsiMsisdnProcess从它派生,处理IMSIMSISDN文件。

凡是带“DAO”的文件,都是和数据库相关的。目前暂时没有详细地研究。

2009年7月15日星期三

中国电信信息园

今天第一次去电信信息园,熟悉了下deploy的流程。也就是如何将war文件安装到WebSphere上。

前期准备:因为3G实验室里的网络要设置成DHCP的,和wipro的不一样,所以首先是要把网络配置好,在桌面的command文件夹中有个bat文件,set_ip_SHRI.bat,用它就可以将网络配置好。

步骤:
1,登录到WebSphere的控制台:地址是http://192.168.26.*:9060/ibm/console/login.do (其中*表示要登录到哪台服务器,比如*可以是8,10等等,9060表示端口号)。登录的用户是qualcomm,密码是test123

2,登录后进入“application->enterprise applications”选项,将会把已经安装的war文件都列出,可以看到各war文件的状态,例如已启动或者未启动。

3,找到需要安装的war文件,这需要登录到相应的服务器上,例如要安装BSGRouter-1.0.0.10.war,首先要知道这个war文件在何处,这需要登录到相应的服务器上去查找,例如登录到服务器:192.168.26.10上,BSG的文件,在所有的服务器上都放在相同的目录下。
命令:进入cmd,用telnet 192.168.26.* (*的意思和前述相同,表示某服务器ip的最后一个数字)。然后进入/home/qualcomm/bsg,到该目录下后,用ls命令查一下,有个叫 “builds”目录或者别的目录(具体的目录名记不清),存放着war文件,找到war文件后,需要将war文件下载到本地。

4,用ftp将war文件下载到本地,下载需要使用ftp协议。
命令:
a,用在命令行里用ftp 192.168.26.*登录到存放war文件的服务器上。
b,用命令:lcd来指定war文件存放的本地路径,例如 lcd c:\表示将war文件存放到本地的c盘根目录下。
c,用bin命令告知服务器在进行ftp文件传输时用二进制方式进行传输,因为war文件是二进制文件,如果不用bin命令,ftp默认的是用ASC码传输的,这样下载的war文件是有问题的。
d,用get命令下载war文件,例如get BSGRouter-1.0.0.10.war,下载BSGRouter-1.0.0.10.war到lcd命令设置的本地目录下。
e,用quit退出ftp

5,下载完成后到本地目录下去check,理论上下载完成后就没有问题了,但是有时候会出现下载到本地后,用WebSphere安装时会报错,所以当把war 文件下载到本地后,最好做一下检查。最主要的检查项是web.xml文件。war文件可以看着是一个zip文件,所以可以直接把war文件的后缀改成.zip,然后解压,找到web.xml文件,简单地查看一下它的内容,没什么明显的问题即可。

6,检查了war文件后就可以回到WebSphere的控制台了,在其中点击Install按钮,开始安装,具体的步骤可以参考training ppt文档,第二步要选择要按照的war文件,这时只要在本地选中即可,后面的步骤基本上只要next就行了,到最后一步貌似有一个“save”的步骤,如果在最后一步发现“save”,点击即可。

7,安装后即可以到WebSphere里去查看,正常的话应该显示war文件已被按照,但是他的状态是一个×的图标,表示改war还没有被start。此时不能立即执行start,因为还需要有配置文件才可以启动。配置文件需要到相应的服务器上去取,他的下载过程和war差不多,不同的是配置文件不被下载到本地,而是直接放到部署war文件的服务器上,例如前面提到的BSGRouter-1.0.0.10.war被安装在服务器192.168.26.10上,那么配置文件也应该被放在10上的相应目录下。

8,假设BSGRouter-1.0.0.10.war的配置文件在服务器192.168.26.8上,先在命令行里用telnet登录到 192.168.26.10,然后用ftp命令ftp 192.168.26.8登录到8上,用户名是qualcomm,密码是test123。登录后用lcd设置存放配置文件的目录,这个目录应该是 /home/qualcomm/bsg/config,设置好后,用bin命令告知ftp用二进制方式进行文件传输(因为配置文件是xml文件,按说不用 bin也应该没问题,但是为了保险起见,还是用bin最安全)。然后用get命令开始文件传输。

9,文件传输完成后,用quit退出ftp,当10的相应的目录下查看,用ls -tsr命令查看,这个命令可以将文件的日期等详细信息列出来,便于更好地查看。

10,check配置文件无误后,就可以在WebSphere的控制台里点击start按钮来启动相应的war了。