Dorado是一个专注于web表现层界面展现的开发框架,能与各种后台业务逻辑层实现无缝集成,Remedy AR System作为BMC提供的一个ITSM开发平台,为了提供给用户更多的灵活性,提供了一套Java API,这样为Remedy和Dorado进行整合提供了可能.
Dorado 架构,原理,技术实现...省略字若干
Remedy AR System组成元素,系统原理,具体用法...省略字若干
I)Dorado操作后台数据库,包含核心典型代码。
首先这个提法有些问题,如果能直接用dorado来操作数据库了,那还要remedy AR System干嘛?所以应该是Dorado调用Remedy AR System后台业务逻辑层,我们知道所有涉及到数据库的信息管理系统,其业务逻辑归根结底都是对DBMS进行增加,删除,查找和修改操作(CURD), 作为开发平台Remedy AR System也不例外,只是它除了将所有操作的数据保存在数据库里面之外,它还将系统的所有的界面信息(这里主要指的是Forms),相关的操作信息(这里指的是Active Link, Filter, Escalation)也保存到了数据库中.
MVC(model-view-control)是我们进行软件开发时通常的分层方法和设计模式,不过在Remedy AR System中,这种做法没有得到很好的实现,即其View和Model的分层并不明显,所以我们需要做这个工作:找出该表单中的model信息,然后加以定义,比如对一个城市的维护form中,我们需要从form中分离出关于城市的model信息,然后生成成相应的JavaBean.
至此,我们将根据JavaBean信息以及Remedy AR System系统中的Form布局信息来创建Dorado所需要的view.xml文件(dataset+control),最终生成jsp文件.
界面创建完毕之后,接下来就是调用Remedy Java API,在Dorado将通过使用dataset的监听器类以及DataModel类的相应方法,来处理从前台页面发出的CRUD请求以及实现相应的权限控制,这里也是我们调用Java API完成与后台业务层集成的结合点.
Remedy提供的Java API调用一般分为三个部分:根据当前登录用户信息创建ARServerUser对象;然后使用Server Object Factory创建Remedy Server Object,并为Server对象设置相应的参数;执行Server对象的方法(增删查改操作);最后销毁ARServerUser以及创建的Server Object.
下面我们以Remedy自带的Sample中City信息的维护为例,来说明如何使用Dorado与Remedy AR System后台整合进行CRUD操作
(1)查询操作
在dataset监听器的onInit方法中,使用当前登录用户信息实例化一个ARServerUser对象:
java 代码
- public void onInit(Dataset arg0) throws Exception {
- System.out.println("In Dataset Listener Connecting to AR Server...");
- context = new ARServerUser();
- context.setServer("bstek-macro");
-
- context.setUser(new AccessNameID("Demo"));
- context.setPassword(new AccessNameID(""));
- try {
- context.verifyUser(new VerifyUserCriteria());
- } catch (ARException e) {
-
-
- System.out.println("Error verifying user: " + e);
-
- context.clear();
- System.exit(1);
- }
- System.out.println("In Dataset Listener Connected to AR Server.");
- }
接下来我们在afterLoadData()方法中根据jsp页面传过来的参数,构造操作参数调用Remedy AR System的Java API进行查询操作,Remedy Java API的查询参数非常类似于SQL语法,所以其用法相对来说也十分简单:
java 代码
- public void afterLoadData(Dataset dataset) throws Exception {
- ParameterSet parameters = dataset.parameters();
-
- String qualStr = "";
- String city = parameters.getString("city");
- String code = parameters.getString("airPortCode");
- boolean andFlag = false;
-
- if (StringUtils.isNotEmpty(city)) {
- qualStr = "( \'City\' like \"%" + city + "%\" )";
- andFlag = true;
- }
- if (StringUtils.isNotEmpty(code)) {
- qualStr += (andFlag ? " and " : "")
- + "( \'Airport Code\' like \"%" + code + "%\" )";
- }
-
- List list = queryRecordsByQual(qualStr);
-
- dataset.fromDO(list);
- }
- private List queryRecordsByQual(String qualStr) {
- List result = new ArrayList();
- System.out.println("Retrieving records with qualification " + qualStr);
- try {
-
-
- FieldCriteria fCrit = new FieldCriteria();
- fCrit.setRetrieveAll(true);
- FieldListCriteria fListCrit = new FieldListCriteria(formName,
- new Timestamp(0), FieldType.AR_ALL_FIELD);
-
- Field[] formFields = FieldFactory.findObjects(context, fListCrit,
- fCrit);
-
-
- QualifierInfo myQual = Util.ARGetQualifier(context, qualStr,
- formFields, null, Constants.AR_QUALCONTEXT_DEFAULT);
- FieldFactory.getFactory().releaseInstance(formFields);
-
- EntryListCriteria listCriteria = new EntryListCriteria();
- listCriteria.setSchemaID(formName);
- listCriteria.setQualifier(myQual);
-
- EntryListFieldInfo[] entryListFieldList = new EntryListFieldInfo[3];
-
- entryListFieldList[0] = new EntryListFieldInfo(
- new FieldID(FIELD_ID), 15, " ");
-
- entryListFieldList[1] = new EntryListFieldInfo(new FieldID(
- FIELD_CITY), 15, " ");
-
- entryListFieldList[2] = new EntryListFieldInfo(new FieldID(
- FIELD_CODE), 25, " ");
-
-
- EntryCriteria criteria = new EntryCriteria();
- criteria.setEntryListFieldInfo(entryListFieldList);
-
-
- Integer nMatches = new Integer(0);
- Entry[] list = EntryFactory.findObjects(context, listCriteria,
- criteria, false, nMatches);
- System.out.println("Query returned " + nMatches + " matches.");
- if (nMatches.intValue() > 0) {
- for (int i = 0; i < list.length; i++) {
- EntryItem[] items = list[i].getEntryItems();
- result.add(new City(
- (String) items[0].getValue().getValue(),
- (String) items[1].getValue().getValue(),
- (String) items[2].getValue().getValue()));
- }
- }
- EntryFactory.getFactory().releaseInstance(list);
- } catch (ARException e) {
- handleException(e, "Problem while querying by qualifier: ");
- }
-
- cleanup();
- return result;
- }
-
对于删除操作,则放到DataModel的实现类中处理,在DataModel实现类中(当然也可以放在dataset监听器中处理),与查询操作一样,在init()方法中实例化一个ARServerUser对象,删除操作在制定的方法中实现:
java 代码
- public void deleteCity(ParameterSet parameters,
- ParameterSet outParameters) throws Exception {
-
- Dataset dsCity = getDataset("dsCity");
- Record record = dsCity.getCurrent();
- if (record != null) {
- String id = dsCity.getString("id");
- try {
-
- EntryID entryID = new EntryID(id);
- EntryKey entryKey = new EntryKey(formName, entryID);
- Entry entry = EntryFactory.findByKey(context, entryKey, null);
-
- entry.remove();
- System.out.println("Record #" + id + " delete successfully.");
- } catch (ARException e) {
- handleException(e, "Problem while modifying record: ");
- }
- }
-
- cleanup();
- super.doUpdateData(parameters, outParameters);
- }
新增和修改与删除基本类似,这里就不做详细说明,主要代码如下:
java 代码
- public void afterUpdateData(Dataset ds) throws Exception {
- List list = new ArrayList();
- ds.toDO(list);
- City city = (City) list.get(0);
- String id = city.getId();
- if (StringUtils.isNotEmpty(id)) {
-
- try {
- EntryID entryID = new EntryID(id);
- EntryKey entryKey = new EntryKey(formName, entryID);
- Entry entry = EntryFactory.findByKey(userInfo, entryKey, null);
- EntryItem[] entryItems = new EntryItem[2];
- entryItems[0] = new EntryItem(new FieldID(FIELD_CITY),
- new Value(city.getCity()));
- entryItems[1] = new EntryItem(new FieldID(FIELD_CODE),
- new Value(city.getAirPortCode()));
- entry.setEntryItems(entryItems);
- entry.store();
- System.out.println("Record #" + id + " modified successfully.");
- } catch (ARException e) {
- handleException(e, "Problem while modifying record: ");
- }
- } else {
-
- try {
- EntryFactory entryFactory = EntryFactory.getFactory();
- Entry entryr = (Entry) entryFactory.newInstance();
- entryr.setContext(userInfo);
- entryr.setSchemaID(new NameID(formName));
-
- EntryItem[] entryItems = new EntryItem[2];
- entryItems[0] = new EntryItem(new FieldID(FIELD_CITY),
- new Value(city.getCity()));
- entryItems[1] = new EntryItem(new FieldID(FIELD_CODE),
- new Value(city.getAirPortCode()));
-
-
- entryr.setEntryItems(entryItems);
- entryr.create();
- System.out.println("Entry created, id #"
- + entryr.getEntryID().toString());
- } catch (ARException e) {
- handleException(e, "Problem while creating entry: ");
- }
- }
-
- cleanup();
- }
-
II)Dorado操作Remedy流程引擎,包含核心典型代码。
因为Remedy的流程处理是与业务相关的,因此属于后台部分,对于dorado来说不存在特殊处理,因此与前面的处理类似
III)Dorado整合Remedy后的性能表现:绝对响应时间以及和Remedy原有系统响应时间对比(后者数据可能目前没法获得)。
一套系统的系能瓶颈主要集中在两个方面:即一个是中间Web Server的并发处理能力,另一个就是数据库访问能力,而dorado展现层开发框架的性能经历过了多个大小项目的考验,事实证明了性能上的可行性,所以如果性能出现问题,只能出现在后台Remedy Java API的执行效率上,这个需要得到BMC公司提供相关的性能指标来加以参考
IV)整合过程的工作量评估,以几个典型界面的改造为例得出的工作量,以及对比传统MIS系统对应界面的开发工作量。
这里工作量的对比应该是相对于其他的改造方案的对比,与传统mis系统没有可比性.与ext2.0之类的纯客户端ajax解决方案来比,dorado不仅有开发工具支持,同时还对前后台通信进行了封装,整合的工作量我们会有明显的优势.
目前对于改造的工作量来说还无法准确进行准确评估,不过针对联通的电子运维系统中一个典型的应用来说,粗略估计周期在2周-4周之间,这个取决于合作双方的人员参与情况,我们的工程师熟悉Remedy AR System系统相关技术的过程,对方提供的相关的Remedy相关的技术支持和业务支持情况,还有就是取决于系统的复杂程度以及最终的改造目标的明确情况
V)可维护性:代码调试性、代码阅读性、流程和界面的松耦合性以及需求变更的解决方案。
因为是一个特殊的表现层改造项目,其特殊之处在于如果界面需求发生改变,需要同时维护两套代码:一套Remedy AR System中定义的Forms, Action Links界面元素,一套是Dorado的Java代码+配置文件,但这也是采用所有其他改造方案都无法避免的问题.所以我们只能通过重用将这个工作量降到最低
VI)附一个典型界面改造的开发步骤。
(1)根据Remedy AR System Forms定义JavaBean数据模型
(2)根据Remedy AR System Forms定义view.xml(如果需要的话module.xml)
(3)给view.xml添加DataModel实现类或dataset监听器实现类调用Remedy Java API实现与Remedy集成
(4)根据Remedy AR System Active Links的定义,转换成Dorado中相应控件的事件代码,完成客户端的操作处理
(4)生成jsp,调整布局得到最终的展现界面
(6)测试通过,模块改造完成
VII)下图是以Remedy 自带是sample中城市维护为例做的一个Remedy与Dorado整合的Demo效果
代码结构图
Remedy Web UI效果:
使用Dorado后的效果: