2009年8月31日星期一

product,content和item

今天想弄清楚产品(product),内容(content)和item(item又分电信定义的item和高通定义的item)之间的关系。到现在对这些还不是很明了,主要是因为没有和实际联系起来,光看代码和配置文件只能了解皮毛。

例如,我知道product下可以包含多个content,每个content也可以包含多个item(电信定义的item),代码也是这样操作解析的,但是这三者到底对应现实中的什么?比如,一款游戏:魂斗罗,它到底算哪样?是product?content?还是item呢,这个到现在还是浆糊。这不弄清楚,对整个代码和系统就不能完全把握。

所以今天从电信的文档和配置文件开始。我先看了productinfo配置文件,和电信文档配合一起看,productinfo配置文件内容很简单,基本上没有什么有效信息,需要关注的只有这几个tag:productID,chargingPolicyID。还有一个serviceID不知道有什么作用。productID是该product的标识,很重要,很多地方都要用到。chargingPolicyID,计费策略,这是电信方面的概念,电信叫charging policy,高通那边叫price plan,本质上是一样的,名称不同而已,当然形式和操作上也有些区别,这个在电信文档的附录“附录 D: BREW 产品计费策略与 ISMP 内部处理关系”有详细的说明。chargingPolicyID标识一个charging policy,也是很重要的,有一个配置文件charginfo.xml,productinfo和charginfo之间要建立联系,就要通过这个 chargingPolicyID。

除了这两个tag,其它的基本上可以忽略。productinfo文件内容相对简单。而上面提到了 charginfo文件,其实charginfo和厘清product,content和item之间关系这个主题的关系不大,这里只是简单介绍一下。产品计费策略信息文件的内容,比如productinfo要稍多,但是值得关注的tag也不多:chargingPolicyID,baseFee,FeeType。chargingPolicyID不用说,FeeType很重要,在代码中就是根据它来解析的。

下面看内容描述文件,这个文件里包含了有关内容的信息。内容描述文件可以包含多个内容,每个内容的tag是“contentInfo”,在每个contentInfo下又包括几个很重要的tag:contentID,itemID,contentURL,modelList和itemBREWProperty,contentID 和itemID不用说,是content和item的唯一标识。从内容描述文件可以看出,一个content是可以包含多个item的。在 contentInfo这个tag下,可以关注一个问题,就是所有与item相关的信息都是放在“Item”这个tag下的,每个contentInfo 可以包含多个Item,标识每个content可以包含多个item,那么content和item到底对应现实中的什么呢?

从电信文档里对Item的解释可以看出端倪,文档里是这么解释的“ 同一content下的不同Item提供针对同一内容的不同终端类型适配的能
力” 从这段解释可以看出:item是针对于不同终端的。而从各自的配置文件内容,也可以印证。首先看productInfo,这个配置文件里面包含了 chargingPolicyID,说明product这个概念,是针对于收费策略的,也可以这样理解,有着相同收费策略的应用,我们都可以把它们看作是同一产品。

而对于content,它比product要小,他和收费策略没有直接关系,可以把它看作某个现实的应用,例如游戏魂斗罗。它可以看作一个content,但是item又是什么,更加电信文档里的解释,item是针对某个或者是某类终端的content,例如魂斗罗是一个 content,但是对于两个手机,姑且称为手机1和手机2,手机1和手机2的硬件不同,例如屏幕大小等等不一样,语言的选择不一样(一个使用英文,一个使用中文)等等不同。对于同一个内容:魂斗罗,不能在两个手机上都适用,所以针对手机1,有一个适用的应用,姑且称为魂斗罗1,这个就是item,而对手机2,同样有个魂斗罗2,这也是个item。所以魂斗罗这个content下面根据不同的手机终端,就有了两个item:魂斗罗1和魂斗罗2。这两个 item是属于魂斗罗这个content的。

在内容描述文件中,itemInfo这个tag下包含的东西值得关注,首先是itemID,这没什么好说的,然后是contentURL,这个很重要,它表示内容实体文件在远程FTP上的存放路径。另一个值得关注的是modelList标签,它表示这个item支持哪几款终端,例如W219,D06,C510,表示这个 item支持的终端有三种:W219,D06和C510。从这里可以看出,item和终端是紧密联系的。

到此,可以这样说,product,content和item,它们的作用范围逐渐减小,逐渐具体。它们的侧重点也是不一样的,product的侧重点是计费,content的侧重点是应用,item的侧重点是适用的终端类型。现举下面的一个例子来说明它们的关系:
有一个product,包括两个内容:魂斗罗和三国志。它们的计费策略都是一样,都是包月10块。如果还有一款应用也使用这个计费策略,也可以加入到这个产品中来。对于魂斗罗这个content,针对iphone,Gphone和Nokia N95,这三款终端,又有了三个item,分别是魂斗罗1,魂斗罗2,魂斗罗3。这三个item分别使用于三款手机,某个item不能被安装到其它两个终端上。

电信的系统是以product为中心的,围绕着product。而高通的系统,没有product,是围绕着item的。对于高通的item,现在还不了解,应该和电信的item有很大的相似处。但是要弄清楚的是,高通的计费策略是不是针对item的,或者说是于计费策略是和 item绑定的。因为电信的计费策略(charge info)是和product绑定的。

2009年8月14日星期五

PostActivation,price handle的获取

对于PostActivation,PostActivationServlet类的doGet方法到没多少内容,它调用PostActivationHandler.fetchItemDetails方法,这个方法返回OPCacheVO对象,查看OPCacheVO的内容,发现有不少东西,productID,contentID,四元组,price handle,还有语言信息,几乎包含了所有必要的信息。随后doGet调用fetchProductIDContentID方法将这些信息都保存到数据库中。

PostActivationServlet的init方法里初始化了两个job:PostActivationJob和NewDistributionJob。

PostActivationJob 是调用PostActivationHandler的invokeProcess方法,这个方法的功能是“This method is used for getting Pricehandle from BrewZone webservice and updating into database table, for generating Feedback file and ftping it to FTPSubsystem.”。貌似这个方法把所有的事情都做了,从BrewZone取得price handle,更新数据库,产生回执文件。

但是粗看了PostActivationHandler的invokeProcess方法,感觉里面用到的很多数据都是已经保存在数据库中了,所以我感觉应该是另一个job,也就是NewDistributionJob执行过后,得到并保存了数据,PostActivationJob才可以顺序地进行,所以决定先看NewDistributionJob。

NewDistributionJob调用了 NewDistributionHandler.invokeProcess方法,这个方法的功能是“This method is used for getting catalog info from BrewZone webservice and updating into database table”,也就是它从BrewZone获得数据并保存到数据库中。invokeProcess调用BREWZoneBase.getInfo方法,它的返回值是InfoVO对象,InfoVO类的成员很简单,值得关注的一个成员是catalogId。得到的InfoVO中的信息又被保存到 CatalogInfoVO对象中。CatalogInfoVO中也有catalogId和其它信息。最后把信息保存到数据库中。

回到 PostActivationHandler.invokeProcess方法,它回频繁地调用getCatalogInfo获得catalog的信息(主要是catalogId),这些信息就是NewDistributionHandler.invokeProcess保存到数据库中的。 invokeProcess调用getPriceHandles获得price handle,但是这个方法没看太懂,它的参数是url和catalogId,很明显,就是到url的地址去获取符合catalogId的price handle。但是细看这个方法的实现,发现好像没做什么,它先是调用brewZoneClient.getTopLevelCategories去获得List,但是奇怪的是虽然调用了getTopLevelCategories,但它的返回值并没有被保存到一个List对象中,也就是说这里调用getTopLevelCategories貌似没起作用。然后有调用OPCacheDAO.opCacheReload,这个方法也奇怪,就在数据库中设置了一下时间戳而已。

随后PostActivationHandler.invokeProcess调用getFeedBackInformation,它返回ProductsVO对象,ProductsVO类有必要关注一下它的成员:
ProductsVO { List product; }

ProductVO { String productId; List content; }

ContentVO { private String contentId; private List ItemDetail; }

ItemDetailVO { private String itemURL; private int opFlag; private String itemId; }

从这些类包含的成员,再对照PriceHandle_2009042900000001.xml文件,就可以发现,这些类成员和pricehandle的xml文件的tag是一一对应的。

当 getFeedBackInformation方法返回ProductsVO对象后,pricehandle的xml文档的内容基本上就确定了。随后 uploadFeedBackFile方法被调用生成feedback file,也就是PriceHandle_2009042900000001.xml文件。

就目前来看,对语言信息来说,虽然语言信息保存到了OPCatchVO中了,但是在代码里貌似并没有使用这个OPCatchVO,而是用ProductsVO来构建了price handle xml文件,xml文件里也没有包含语言信息。

2009年8月13日星期四

CMSEntity之九,提交item的逻辑

接昨日:
获取内容描述文件并对其进行初步分析后,ContentProcessorJob会调用ContentHandler类的方法做进一步处理。方法invokeProcess是入口,invokeProcess首先就调用getSuitableBatch去获得合适的batch,所谓合适的 batch,就是在上篇博客中提到的,处理完所有的item后,要将其状态更新,置为saved,以便于后面的操作。所以 getSuitableBatch就是将这些状态为saved的项取出来。

getSuitableBatch得到的是 List,之后invokeProcess调用commonStorageHandling来“handle the common storage”(不太明白这里的意思),昨天提到,可以通过batchId来得到content的信息,commonStorageHandling调用getContentByBatchID返回List。commonStorageHandling后面的处理让我有些摸不着头脑,它先是检查每个ContentItemVO的ProcStatCodeID,如果是CONTENT_STATUS_SAVED或者CONTENT_STATUS_TIMEDOUT,再去获得ISMPItemVO,也去挨个检查ProcStatCodeID,如果是 ITEM_FETCH_SUCCESS或者ITEM_STATUS_TIMEDOUT,然后到本地目录去获取内容实体zip文件,如果文件不存在,将 ISMPItemVO的ProcStatCodeID改为ITEM_STATUS_PARSED。这一段操作的目的还不清楚。 commonStorageHandling的返回值是boolean型,表示内容实体zip文件在本地目录是否存在。

commonStorageHandling返回到invokeProcess,invokeProcess根据返回值,如果为真,它调用方法processBatchNotify。

processBatchNotify也是首先用getContentByBatchID获得List。然后对每个ContentItemVO调用方法processContent,这个方法非常重要:“Iterate the item list of this content, prepare the MetaFile and generate the zip, Invoke the BMC web service to Submit the item. Use the return brew_item_id to update ContentItemVO object. update the ItemVO's zip file directory and metafile XML file and final zip file result.”。也就是说,这个方法生成了Meta文件,并将Meta文件和内容实体zip文件一起再生成一个zip文件,并将这个zip文件传给 BMC。

processContent是根据OPFLAG进行操作的,有三种OPFLAG:OPFLAG_ADD,OPFLAG_MODIFY和OPFLAG_DELETE。

先看OPFLAG_ADD的情况:
1. 查询数据库,用方法checkContentItemExists,这个方法的功能是“This is to check content is exist with current content id and partNo in BSG”。也就是查找在当前数据库中是否已经存在该内容。注意这里使用的参数是content ID,manufacturer partner no和content no,前面两个很好理解,最后一个content number不知道是什么。根据这三个来判断该内容在当前库中是否存在。如果存在,进入2.x,否则进入3.x。

2.1 调用方法checkItemExistsForTheContent,这个方法的功能是:“This is to check content is exist with current content id and partNo in BSG”。这个解释让人迷惑,为什么又去判断content是否存在?所以我判断这里的注释肯定不对,印度兄弟偷懒了,把 checkContentItemExists的注释又拷贝到这里。按checkItemExistsForTheContent这个方法名来判断,不好说这个方法是什么功能,它的返回值如为true,进入2.2,否则进入2.3。
2.2 用方法getItemVOList去获得List,然后对每个ISMPItemVO调用checkISMPItemExists,这个方法的功能是“This is to verify that Item is already exist or not”。返回true表示content里已经存在该item了,进入 2.2.a,否则进入2.2.b。
2.2.a 调用方法applyPlatformLanguage,这个方法首先调用ChangeListCreator.getUseableChangeList,现在不知道change list是干啥用的,它返回ChangeListVO,如果不为空,则调用 ItemSubmissionHandler.addOprItemPlatform方法,这个方法很长,这里先不细谈,它的功能是把该item的平台信息(支持的手机类型)保存。
2.2.b 调用方法processAsUpgradeItem,将该item作为新的item上传给BMC。这是通过ItemSubmissionHandler.processItem方法实现的。
2.3 用方法getItemVOList去获得List,对每个ISMPItemVO,调用ItemSubmissionHandler.processItem方法将其作为新的item提交到BMC。

3.1 当前的content不存在,首先调用checkContentExists,这个方法的功能是“This method checks whether content exists or not”。这里让人很迷惑,前面不是已经用checkContentItemExists检查过content是否存在了吗,为什么又检查一次?如果返回true,就调用markedForDelete,这个方法“This method is used for delete handling when only contentId exists”。
3.2 用getItemVOList得到List,对每个ISMPItemVO,调用checkItemExists,这个方法又搞不清楚,它的注释是“This is to check the Item exists or not”。迷惑的是:这里说的存在到底是存在于哪里?是存在于BSG的数据库中?还是存在于content中还是哪里?如果存在,进入3.3.a,否则进入3.3.b
3.3.a 用方法setProcStatCodeID将item的状态置为ITEM_STATUS_FAILED,表示item已存在,然后调用updateItemStatusNLoc更新状态。
3.3.b 调用ItemSubmissionHandler.processItem方法将其作为新的item提交到BMC。

OPFLAG_MODIFY的处理:
1. 调用modifyHandling方法,用方法getItemVOList去获得List,然后对每个ISMPItemVO调用checkISMPItemExists,这个方法的功能是“This is to verify that Item is already exist or not”。返回true表示content里已经存在该item了,进入2.1.a,否则进入2.1.b。
2.1.a 用ChangeListCreator.getUseableChangeList()去获取change list,如果成功,用addOprItemPlatform把该item的平台信息(支持的手机类型)保存。
2.2.b 用setProcStatCodeID将item的状态置为ITEM_STATUS_FAILED

OPFLAG_DELETE的处理:
1. 调用modifyHandling方法,用方法getItemVOList去获得List,然后对每个 ISMPItemVO调用checkItemAlreadyExist,“This is to check that item exists or a new item”,也是让人迷惑,搞不懂。

processContent返回到processBatchNotify,processBatchNotify调用setProcStatCodeID设置BatchStatusVO的状态。

2009年8月12日星期三

CMSEntity之八,内容描述文件的获取和初步处理

BSG获取内容描述文件并做初步处理,是在ContentFetcherJob中完成的,真正干活的是类ContentAdaptor,它用 ContentParser去解析内容描述文件,解析后的结果是ContentsDescVO,这个类不知道有什么意义,其实里面就只有 List,ContentVO和内容描述文件里面的tag可以说是一一对应的。然后ContentAdaptor调用 ItemSubmissionConverter.fromXMLVO将前面得到的ContentsDescVO转换为 List。比较令人关注的是在内容描述文件中,“languagesSupported”这个标签的处理,支 持的语言,这个信息的处理流程如下:

ItemBREWPropertyTypeVO.getLanguagesSupported()
ISMPItemLanguagesVO.setIsmpLanguageId()
ISMPItemVO.setItemLanguageVOList()
ContentItemVO.setItemVOList()

语言信息最终保存到了ISMPItemVO中,而ISMPItemVO又被包含在ContentItemVO。至于为什么要做ContentsDescVO到List的转换,目前还不知道动机和目的。

回 到ContentAdaptor,fromXMLVO完后,ContentAdaptor继续调用saveContentItemInfo将 ContentItemVO信息保存到数据库中,这里主要操作的是数据库中的表:ISMP_CONTENT table 和 ISMP_ITEM table。然后ContentAdaptor调用fetchZipFile去获得内容实体问题,在6号的博客里提到了内容实体文件是zip文件,内容实 体文件的地址保存在内容描述文件中。

最后还有个很重要的步骤,ContentAdaptor调用updateBatchStatus去更新前面处理的item的状态,将其置为saved。后面的操作都是基于item和content状态的。

BatchStatusVO 里面的内容不知道有什么作用,但是后面很大地方要用到它,一个比较关键的问题是前面提到的ContentItemVO与BatchStatusVO是如何 建立联系的,在ContentItemVO中有一个成员batchId,这个id在ContentItemVO和BatchStatusVO中都有保存, 所以可以通过这个id找到对方。这个过程可以参看ContentHandler的方法getContentByBatchID。

2009年8月6日星期四

CMSEntity之七,内容的提交

内容上传的流程今天才弄明白点,以前一种存在误解,直到前天和昨天看代码有疑问(为什么在product Info和product content map文件里面都没有具体的content和item信息,因为按以前的理解,在product info文件理应该要包含某个product下有多少content,每个content有包含哪些item这类信息,但是在product Info和product content map文件product Info和product content map文件里没有发现,而这些信息是在ICMS _SYNCYYYYMMDDNNNN.REQ文件里),然后昨天今天看电信文档才发现一直理解错误。

CMS的流程是:

1,ISMP或者NMSC发起contentSyncNotify的webservice给BSG
2,BSG处 理这个soap,获得content description file(内容描述文件),这个文件的文件名为:ICMS _SYNCYYYYMMDDNNNN.REQ,其中YMDN代表年月日时间。这个文件被存放在本地路径:/home/qualcomm/CMS/REQ, 这个信息可用从文件BSGCMSconfig.xml配置文件里查询到。
3,BSG开始处理内容描述文件,描述文件里有些信息很重要,值得注意的有:
operation项,它是一个整型的值,1表示增加,2表示修改,3表示删除。BREW要求这里必须是1,因为谢天曾经提到过,BREW只有添加操作没有别的,但是如果实现删除操作?对于某项内容,把要删除的项剔除,剩下的内容作为新的重新提交,这样实现的删除。
manufacturerPartNumber项,这一项在描述文件里就是用实体文件名的数字作为填充值,但是这个值是不对的,在后面的操作中manufacturerPartNumber要改成ContentID。
4,BSG将描述文件里的内容填写到相应的数据库表中,处理完后,下载内容实体文件,实体文件的下载地址是在描述文件里指定好的,在描述文件的contentURL项中,指定了到何处去取实体文件,实体文件是一个ZIP文件。
5,BSG会生成一个Meta文件(xml)和实体文件一起提交给BMC,Meta文件和实体文件存放在本地目录:/home/qualcomm/item_submit_zip (在BSGCMSconfig.xml配置文件的zipDirBase项)
6, 提交完内容后,BSG产生一个content synchronization feedback(内容同步反馈文件),保存到ISMP的指定FTP目录中。这个反馈文件在本地目录也有保存:/home/qualcomm/CMS /RSP/Archive (在BSGCMSconfig.xml配置文件的localFeedBackArchiveFolder项),到此内容提交结束。
7,BSG去轮询以获得product info,product content map和charging info文件提交产品信息及计价策略文件