给IBuySpy构建一个PlugIn系统

话说公元2003年12月17日,MSDN Library网站上悄无声息的多了一篇文章,介绍了关于构建一个PlugIn Framework的一些基础知识,于是,有了这篇随笔…

PlugIn,很COOL的特性,下面将演示如何给我们的IBuySpy定制一个Page Start PlugIn,这个PlugIn可以让用户自己来创建PlugIn,嵌入到IBuySpy的Page Start PlugIn里面,在网站页面载入的时候,会执行用户嵌入的PlugIn。

可我们为什么要给IBuySpy创建PlugIn接口呢?我们要实现同样的功能,可以直接的修改它的代码,岂不是直接很多?原因:IBuySpy只是用来演示PlugIn的,你可以把同样的技术应用到其他的WebForm甚至WinForm,而它们可能并不会像IBuySpy一样是免费的,我们交付的产品里面不会附上代码,如果我们提供了PlugIn的接口,无疑会让我们的产品更加具有扩展性。何况很多功能可能可以直接作成PlugIn来嵌入到原有的系统中,这个时候无需再改动原有的代码,再去编译它。

1、构建IBuySpy的PlugIn所需的接口,这些接口就是可以公开给用户代码的:

首先需要的是一个通用的IPlugIn接口,所有具体的插件将实现这个接口:

namespace ASPNetPortal.PlugIns {

 public interface IPlugIn {
  String Name {get;}
  String Version {get;}
  void DoAction(IPlugInArgs args);    
 }
}

这个接口有三个成员:
Name属性,公开插件的名称
Version属性,公开插件的版本
DoAction()方法,执行插件要做的操作,这个方法还有一个IPlugInArgs的参数,需要传递给方法所参数可以通过它传递出去。

然后就是这个IPlugInArgs接口:

namespace ASPNetPortal.PlugIns {

 public interface IPlugInArgs { 
  System.Web.HttpContext Context {get;}
  Object Data {get;}
 }
}

它有两个成员:
Context属性,一个HttpContext类型的对象,如果我们要让插件能够在页面上做些事,不给它HttpContext肯定不行。
Data属性,一个Object类型的对象,预留的,什么地方需要就什么地方用上。

接着是一个接口集合类:

namespace ASPNetPortal.PlugIns {

 public class PlugInCollection : CollectionBase {

  public Int32 Add(IPlugIn plugIn) {
   return this.List.Add(plugIn);
  }

  public IPlugIn this[Int32 index] {
   get {
    return (IPlugIn) this.List[index];
   }
  }
 }
}

很简单明了。用户可能不止嵌入一个插件。

PlugIn也可以有很多类型,比如我们这里要实现的Page Start PlugIn,是一个在页面载入的时候可以让嵌入的PlugIn执行的。当然你也可以创建各种类型的PlugIn。

namespace ASPNetPortal.PlugIns {

 public interface IPageStartPlugIn : IPlugIn {}
}

这个PlugIn接口不需要再做任何事,直接继承IPlugIn就可以了。

2、修改IBuySpy,让它支持执行PlugIn:

根据我们的需求,我们创建一个实际的插件参数类,这个类继承自IPlugInArgs:

namespace ASPNetPortal.PlugIns {

 public class PlugInArgs : IPlugInArgs {
  private System.Web.HttpContext _context;
  private Object _data;

  public PlugInArgs(System.Web.HttpContext context, Object data) {
   _context = context;
   _data = data;
  }

  public System.Web.HttpContext Context {
   get {
    return _context;
   }
  }

  public Object Data {
   get {
    return _data;
   }
  }
 }
}

我们需要地方来标示用户嵌入的PlugIn的列表,我们放在web.config里面的里面,我们加上一项,来表示我们要添上的Page Start PlugIn:

《ADD key=”PageStartPlugIns” value=”” /》

value里面可以写入嵌入的PlugIn的列表,格式像这样:Value = “插件一的类名, 插件一的程序集名; 插件二的类名, 插件二的程序集名”

然后,我们构建一个PlugInHelper类,来执行获取PlugIn、执行PlugIn的操作:

namespace ASPNetPortal.PlugIns {

 public class PlugInHelper {

 private PlugInHelper() {}

 public static PlugInCollection GetPlugIns(String plugInType) {
  PlugInCollection plugIns = new PlugInCollection();
  String sPageStartPlugIns = System.Configuration.ConfigurationSettings.AppSettings[plugInType];
  if ((sPageStartPlugIns != null) && (sPageStartPlugIns != “”)) {
   String[] asPlugInStr = sPageStartPlugIns.Split(‘;’);    foreach(String plugInStr in asPlugInStr) {
    plugIns.Add( (IPageStartPlugIn) System.Activator.CreateInstance(System.Type.GetType(plugInStr)));
   }
  }
  return plugIns;
 }

 public static void ExecutePlugIns(PlugInCollection plugIns, IPlugInArgs args) {
  foreach(IPlugIn plugIn in plugIns) {
   plugIn.DoAction(args);
  }
 }
 }
}

GetPlugIns()方法返回指定类型的PlugIn的列表,返回类型是PlugInCollection,ExecutePlugIns()用来执行参数中的PlugIn。

最后,我们要把执行插件的代码加入到页面的执行队列中。为了在每个页面开始的时候能够执行用户嵌入的Page Start Plug,标准方法是在Global.asa里面来实现,更标准的方法是构建一个httpModule,然后在这个httpModule中来执行这个PlugIn(关于构建自定义的httpModule,辣椒是个中高手)。我这里就偷懒了,因为IBuySpy几乎所有的内容页面都是DesktopDefault.aspx这个页面中载入,所以我们先在这个页面里面创建一个方法来执行插件:

private void PerformPlugIns() {
 PlugInCollection plugIns = PlugInHelper.GetPlugIns(“PageStartPlugIns”);
 PlugInArgs args = new PlugInArgs(Context, null);
 PlugInHelper.ExecutePlugIns(plugIns, args);
}

第一句得到所有的Page Start PlugIn,第二句创建一个传递给插件的PlugInArgs对象,第三句调用PlugInHelper.ExecutePlugIns()来执行第一句得到的PlugIn队列。

在DesktopDefault.aspx的Page_Init事件中调用上面的这个PerformPlugIns()方法就OK了。

3、演示如何创建一个Page Start PlugIn:

前面两步已经让IBuySpy可以嵌入用户自定义的Page Start PlugIn了,现在我们来做一个实际的PlugIn嵌进去。

启动VS,创建一个“C#类库”项目,引入IBuySpy的程序集Portal.dll,这是因为我们需要用到IBuySpy里面的PlugIn相关的那些接口,更好的方法是把IBuySpy中与PlugIn相关的公共接口放在一个单独的程序集里面。

namespace WelcomeMessage {

 public class ShowWelcome : ASPNetPortal.PlugIns.IPageStartPlugIn {

  public string Name {
   get {
    return “Show Page Welcome Message”;
   }
  }

  public string Version {
   get {
    return “1.0.0.1”;
   }
  }

  public void DoAction(IPlugInArgs args) {
   args.Context.Response.Write(“<script>alert(‘Hello, world!’);”);  // 故意写错了
,不然…
}
}
}

这个ShowWelcome类继承自IPageStartPlugIn,表示它是一个Page Start PlugIn,用来具体执行操作的DoAction()方法只做了一件事,从参数中得到页面相关的HttpContext对象,然后输出一段字符以在页面内容载入之前弹出一个“Hello,world!”的提示框。

然后我们修改IBuySpy的web.config里面相关的那句设定:

《ADD value=”WelcomeMessage.ShowWelcome, WelcomeMessage” key=”PageStartPlugIns” /》

OK了,编译,把生成的dll放到IBuySpy的bin目录,打开浏览器浏览IBuySpy网站,你会看到每次载入页面内容时,都会弹出一个“Hello,world!”的提示框。

感冒了

今天是上班第二天,结果却感冒了,十分郁闷。明天还要去体检,不知道会出现什么危险症状不?太恼人了!

从今天开始,要对InfoPath 2003进行一下研究,刚刚下载安装了InfoPath 2003的SDK,并且安装了里面的两个示例。是使用InfoPath来调用Web Service来得到/回填数据的。只不过由于是刚刚着手做,所以现在还没有实验成功。有哪位老大对这方面有研究的,希望能够不吝赐教一二笑脸

如果有对InfoPath应用感兴趣的朋友,可以点击左侧的Logo,查看相关的信息。如果对InfoPath开发感兴趣的朋友,可以到http://msdn.microsoft.com/office/infopath中下载相应的SDK文档。我们一起来研究一下吐舌笑脸

另外,博客堂杯征文活动已经告一段落了,准备与三位评委讨论一下大奖结果了。

昨晚上写的关于IBuySpy里面用户权限验证方面的东西

ASP.NET在页面的Context.User里面放了一个实现IPrincipal的对象,用来实现对已验证用户的管理。ASP.NET系统中,通常采用的方式就是扩展这个Context.User,让它里面保存定制的信息。

 

1、扩展方式

扩展方式基本上有两种:直接利用GenericPrincipal和自己写一个实现IPrincipal的类。IBuySpy用的前者,优点就是N简单。

 

Context.User = new GenericPrincipal(Context.User.Identity, roles);

 

roles是一个保存了当前用户的角色信息的String,各个角色间用“;”分隔,由前面的代码调用数据层中的UserDB.GetRoles()方法来得到。

 

自己写一个实现IPrincipal的类其实也是N简单,你只需要实现两个接口:Identity属性返回一个IIdentity的用户标识对象,IsInRole(String role)判断用户是否具有参数中的角色。下面是我写的一个替代IBuySpy中原有扩展模式的类:

 

 

public class IBSPrincipal : IPrincipal {

 

private IIdentity _identity;

private String[] _asRole;

 

下面是两个构造函数:

 

public IBSPrincipal(IIdentity identity, String roles) {

_identity = identity;

_asRole = roles.Split(‘;’);

}

 

public IBSPrincipal(IIdentity identity, String[] roles) {

_identity = identity;

_asRole = roles;

}

 

然后是用来实现IPrincipal的一个属性和一个方法:

 

public IIdentity Identity {

get {

return _identity;

}

}

 

public bool IsInRole(string role) {

    // 下面一行幸得“JGTM’2003”告知Array有一个static的IndexOf()

    // 原来是用循环查询_asRole….土不可及…

    return (System.Array.IndexOf(_asRole, role) > -1);

}

 

OK,然后我们就可以用下面这句替代上面IBuySpy原有的那句了:

 

Context.User = new IBSPrincipal(Context.User.Identity, roles);

 

看起来好像自己创建这个IBSPrincipal并不划算,它没有提供比GenericPrincipal更多的功能,但好处是我们以后可以随时扩展它,而且实现的成本也很低。

 

2、时机

我们在什么时候进行上面所说的这样的扩展行为呢?

 

IBuySpy选择在Global.asax里面的Application_AuthenticateRequest事件里面进行这些动作,只要ASP.NET程序需要进行用户验证,那么肯定要经过这里,这个地方不错。

 

另一个好地方就是在“页面基类”的Page_Init方法中,我们在构造网站的时候就可以先构建一个“页面基类”(即使一开始里面什么都没有),所有的页面都从这个基类继承下来而不是从默认的System.Web.UI.Page,这样做的好处是我们可以随时把类似的动作放在基类中。可惜IBuySpy本身并没有应用这种“页面基类”的方式。

 

3、FormsAuthenticationTicket

这个类是一个“验证票据”类,我们可以利用这个票据来保存用户的信息,并把这个票据写到客户端的Cookie里面,以后客户再来时,从Cookie中把这个票据取回来,就可以得到用户的信息了。

 

上面这一段说的就是IBuySpy做的,但是一定要注意,我们自己做这些操作,和ASP.NET内置的Forms验证没有任何关系,虽然它也是通过类似的保存票据到Cookie来实现的。

 

用这个票据来写到Cookie与我们自定义一个Cookie去写相比有什么好处呢,首先可以设定票据的过期时间、决定是否永久在Cookie保存票据、票据中可以写入自定义的信息(比如用户所具有的角色),而且,.NET内置的FormsAuthentication.Encrypt()方法和FormsAuthentication.Decrypt()方法可以把一个票据加密成一个字符串,和从一个字符串解密出一个票据,这样省掉了我们很多的工作了。

 

来看看IBuySpy里面的代码,创建一个票据并写到Cookie:

 

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(

1,  // version

Context.User.Identity.Name,  // user name

DateTime.Now,  // issue time

DateTime.Now.AddHours(1),  // expires every hour

false,  // don’t persist cookie

roleStr  // roles

);

 

// Encrypt the ticket

String cookieStr = FormsAuthentication.Encrypt(ticket);

 

// Send the cookie to the client

Response.Cookies[“portalroles”].Value = cookieStr;

Response.Cookies[“portalroles”].Path = “/”;

Response.Cookies[“portalroles”].Expires = DateTime.Now.AddMinutes(1);

 

从Cookie中读出票据,再从票据中得到用户具有的角色:

 

// Get roles from roles cookie

FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(Context.Request.Cookies[“portalroles”].Value);

 

//convert the string representation of the role data into a string array

ArrayList userRoles = new ArrayList();

 

foreach (String role in ticket.UserData.Split( new char[] {‘;’} )) {

userRoles.Add(role);

}

 

roles = (String[]) userRoles.ToArray(typeof(String));

MVP Webcast

Lori感冒了,没有出席,主讲人是MVP总监Sean O’Driscoll。Sean一开始大概说了一通对新老MVP(全球大约2000人)的感谢,然后是讲述MVP将从微软所得的奖品与benefits,包括MSDN/Tech Net的订购,每年的高峰会议,在线学院,安全网站等等。然后是他们自己对MVP项目的评估,非常实事求是。

接着谈到根据MVP的反馈,他们正在做的一些工作。譬如,为了保证MVP的质量,他们将把评选过程标准化,并重点争取在全球范围内做到水平一致,还把MVP Lead专职化。高度认可MVP的贡献,产品组将与MVP做更多的沟通,MVP将能更早地接触产品和其他资源,并更好地对当地语言提供支持。将对项目和服务专业化,质量为第一优先,达到全球的一致性和交流。

明年的高峰会将于4月4-7日在西雅图举行,Steve Ballmer已经确定将出席会议,MVP已经可以开始登记,希望大家明年早点去申请护照/签证。MVP将收到Learning Kits以及参加认证考试的礼券等,MVP将可以在社区知识库贡献自己的内容。

他们正在进一步提高内部职工对MVP项目的关注,还给MVP项目设计了新的Logo,和口号 “Phenomenal. And then some”,希望大家都在微软网站贴出跟自己有关的介绍/照片等等。MVP渐渐获得了业界的注意,譬如,微软给MVP开放源码曾获得了广泛的报道。很多MVP同时也是产品的Beta测试人员,Office产品组根据Office的MVP们的建议改变了界面设计。

Sean接着讲述他参加各地MVP高峰会议,还贴出了一些与MVP沟通的照片,包括在北京与中国MVP的合影。

最后,Sean回答了各地MVP的问题,包括MVP怎么更好地参与产品开发过程,提交bug等等,他提到Longhorn的开发,在适当时候Windows MVP将得到Beta1,其他的MVP将得到Beta2。

Sean还对很多中国MVP去年无法去西雅图参加高峰会表示了遗憾。有MVP曾问,高峰会议能否不在美国举行,他说很困难,因为得不到高级行政阶层,以及全公司产品组人员的支持,他还提到MVP的评选将成为季度性的评选。

流水账一篇,笑脸

蠕虫病毒袭击 Windows ATM

一家制造自动柜员机(ATM)的厂商周一称,两家银行的基于流行的 Windows 的 ATM 在八月被病毒感染,SecurityFocus.com 最先披露了这次感染事件,据信是计算机病毒第一次直接侵袭柜员机,而且据一些计算机安全专家预测,随着一些关键系统向 Windows 上的移植,将暴露出更多安全性问题。

制造 ATM 机的 Diebold 公司的管理人员说,在这个被称为 Nachi 蠕虫病毒传染期间不明数量的运行 Windows XP Embeded 的 ATM 被关停,但他们拒绝透露是那些客户受此影响。

在一月份, SQL Slammer 蠕虫病毒 曾经短暂导致美洲银行的 ATM 机的网络问题,但没有直接影响 ATM 本身。

Diebold的高级产品经理称,他们从 IBM 的 OS/2 转换到 Windows 平台上,是因为银行需要 Windows ,“他们要求我们装配带有具有图形处理能力的 Windows 的 ATM ,他们想让 ATM 和 Web-banking 具有一致的界面,而且他们熟悉 Windows”,他同时说,为了防止未来可能出现类似的问题,他们已经在 ATM 上配置了防火墙,来防止病毒和其它攻击。

看到这个消息,你的反应是什么? 有些银行内部的技术人员曾经说,“SNA 协议防病毒”,这个看似可笑的论点确是实实在在的事实,开放性、通用性、功能强大也许和安全永远是矛盾的两端,另外,用于特定领域或设备的专用的操作系统,是否有必要全部转向开放和通用?

来源:new.com

工作、SOA、MBF…

1、上周在部门的SharePoint项目主要里面加入了两个特性,“用户个人文档库”和“文档库多级权限”。

用户个人文档库很简单,就是给用户一个单独的“私人的”文档库,虽然可以让管理员手工给每个用户建立一个文档库,然后让这个文档库只能给一个用户访问,但步骤太繁琐,我把这个步骤做成了自动的。

SharePoint默认的文档库权限只针对于每个文档库,下面的文件夹是不能再单独授权不同权限的。有些用户使用时,一个项目使用一个文档库,但是有些项目文档是只能给特定用户访问的,所以这样的单级权限就做不到了。在建立子文件夹时,偶给用户两个选择,“普通文件夹”和“高级可管理文件夹”,后者是可以单独进行授权的(后面实际是用SPList来管理的…),建立是的默认权限直接继承上层的。

2、Microsoft Business Framework,微软正在做的一个东东,随Whidbey会发布一个可用的版本。

什么是MBF?MBF是一个包含了建立在.Net Framework之上的中间件层的在一个大的代码基础层之上的应用程序集(is that the suite of applications will be based on a global code base, with the middleware layer the Microsoft Business Framework, which builds on top of .Net Framework)。看看一篇专访

这里还有一篇,Microsoft Business Framework: The 10-year plan,嗯,看来微软是“蓄谋已久”了。

当然还有微软自己的ppt,Developing Applications Using the Microsoft Business Framework

3、SOA,面向服务的架构。第一次听说是从ccboy的blog上。ZDNet China上有几篇不错的文章。

微软已经作出了一个SOA的东东了,Shadowfax,现在的版本是Pre-Alpha,呵呵,怎么名字听着有点想《魔戒》里面甘道夫骑的那匹马的名字?

前几天看IBuySpy时记在OneNote里面的笔记

IBuySpy Portal 中使用 PortalModuleControl 这个继承自UserControl的类来作为站点中所有Module的基类,用户控件的工作方式是,当页面上实例化一个用户控件时,自动将它的所有子控件全部RenderHTML,然后输出,为了提高Module的工作效率,每个Module可以设置自己的缓存时间,在缓存时间内,系统不会再重复Render它的所有子控件,而是在第一次Render的时候,把结果HTML文本缓存起来,当下次需要的时候再直接输出。

 

实现缓存功能,IBuySpy是通过CachedPortalModuleControl实现的。

 

因为IBuySpy页面上的Module都是通过LoadControl()方法来动态载入到页面上的,像这样:

 

PortalModuleControl portalModule = (PortalModuleControl) Page.LoadControl(_moduleSettings.DesktopSrc);

 

portalModule.PortalId = portalSettings.PortalId;
portalModule.ModuleConfiguration = _moduleSettings;

 

parent.Controls.Add(portalModule);

 

当检测到一个ModuleCacheTime>0时,代码则:

 

CachedPortalModuleControl portalModule = new CachedPortalModuleControl();

 

portalModule.PortalId = portalSettings.PortalId;
portalModule.ModuleConfiguration = _moduleSettings;

 

parent.Controls.Add(portalModule);

 

就是说,代码不会再载入PortalModuleControl类型的控件了,而是载入CachedPortalModuleControl来实现的。

 

下面看看CachedPortalModuleControl是如何实现缓存的:

 

private String _cachedOutput = “”;

 

这里定义了一个String变量,保存缓存的内容

 

protected override void CreateChildControls() {

 

if (_moduleConfiguration.CacheTime > 0) {

_cachedOutput = (String) Context.Cache[CacheKey];

}

 

CreateChildControls()这个方法是在Control类每次被实例化时,都会执行的方法。首先检查这个Module是否启用了缓存,如果启用了,则从Context.Cache中寻找缓存,并载入到_cachedOutput中。

CachedPortalModuleControl实质上是一个Composite Control,有关复合控件的相关资料,参阅:

ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.2052/cpguide/html/
cpcondevelopingcompositecontrols.htm)

 

if (_cachedOutput == null) {

 

base.CreateChildControls();

 

PortalModuleControl module = (PortalModuleControl) Page.LoadControl(_moduleConfiguration.DesktopSrc);

 

module.ModuleConfiguration = this.ModuleConfiguration;

module.PortalId = this.PortalId;

 

this.Controls.Add(module);

}

 

如果_cachedOutputnull,那么说明还没有缓存,于是调用base.CreateChildControls(),然后用LoadControl()方法重新(真实的)载入控件,并把这个控件放入本控件的子控件树中。

 

protected override void Render(HtmlTextWriter output) {

 

if (_moduleConfiguration.CacheTime == 0) {

base.Render(output);

return;

}

 

现在到了Render方法,这个方法用于输出控件的HTML,首先检查是否启用缓存,如果没有,直接调用base.Render()直接输出,然后return

 

if (_cachedOutput == null) {

 

TextWriter tempWriter = new StringWriter();

base.Render(new HtmlTextWriter(tempWriter));

_cachedOutput = tempWriter.ToString();

 

Context.Cache.Insert(CacheKey, _cachedOutput, null, DateTime.Now.AddSeconds(_moduleConfiguration.CacheTime), TimeSpan.Zero);

}

 

如果启用了缓存,但是用来保存缓存内容的变量为null,那么就调用base.Render()方法,把所有应该输出的HTML输出到_cachedOutput变量中,然后把这个变量的内容放入Context.Cache中。

 

output.Write(_cachedOutput);

 

最后,把这个变量中的HTML内容输出。

ASP.NET 2.0 Page/Control方面的一些改进

陆陆续续地把这本<<A First Look at ASP.NET v.2.0>>读完了(沉思的辣椒处有电子书下载),除了那些大家提到的新功能,象什么新数据控件/MasterPage/Portal Framework/Personalization/Themes什么的外,ASP.NET 2.0还对Page Framework作了不少改进,譬如

1。网站上经常有人问怎么提交到另外一个webform去,因为如果是用server form的话,在ASP.NET 1/1.1里只能提交到当前页。常见的建议是,先把数据存起来,然后用Response.Redirect或用Server.Transfer(如果是同一网络应用的话),或是别用server form或用Javascript脚本在提交时改动form action。这些答案都不是很令人满意,ASP.NET 2.0提供了一个叫cross-page posting的机制。如果是同一应用的话,在第二页面的后端编码里还能访问第一页面的对象模型。

2。ASP.NET1/1.1里的验证模式是要么全验证要么全不验证,除非你回到老路自己写客户端验证编码或hack WebUIValidation.js。2.0提供了一个按组验证的方法。

3。ASP.NET 2.0还提供了Wizard类型的页面模式编程。

4。ASP.NET 2.0还提供了简单明了的URL影射机制。

5。还有一些客户端脚本方面的改进,譬如你可以在后端调用控件的SetFocus方法(当然,最终还是在客户端以脚本的方式来实现的),以及一些注册脚本编码方面的改进。

6。 ASP.NET 2.0还改变了页面以及控件的生命周期,在其中新加了几个事件。

其中一个改变是,新增了一个RaiseCallbackEvent步骤,允许页面在不postback的情形下调用服务器端的编码。经常看见有网友在网上问怎么在不postback的情形下更新页面,这个新改进应该能很好地满足这个要求而且其调用过程比较简单。

记得类似的方法一开始是用隐藏桢来实现的,后来出现了remote scripting(滑稽的是,它是用Java applet实现的),再后来在IE5里的MSXML组件里提供了一个XMLHTTP对象,以及后来的download行为,再到后来的比较时髦的webservice 行为。

2.0里的Callback在IE里实际上还是用XMLHTTP对象来实现的,但这个实现是可以在多个浏览器上用的,譬如在Netscape里, 所用的对象是XMLHttpRequest。

EAI 市场中的后起之秀

本来想找找除了 Collaxa 之外,还有那些产品对 BPEL4WS 提供支持,无意中发现一个新的产品: Vitria BusinessWare,在 Google 上 Search 一下,结果还真有点让人吃惊:

1、Vitria 的产品 BusinessWare 非常优秀

      解决方案傲视群雄Vitria赢得两项大奖,其它所获得的奖项都是 2003 度的,真是后起之秀。

2、又是一个华人,而且是位女士在硅谷创办的高科技企业

      张若玫:我要用科技改变世界

3、Vitria 开始进军国内市场

      东软-Vitria启动战略合作 强强联手  共拓中国E-Business市场

4、支持的标准极其丰富,尤其是在 Web Service 方面

      Vitria 的标准支持

看来,要想评估 EAI 的系统和方案,Vitria 是不能少了,和 Biztalk 有一拼哩,十分喜欢它这个架构图。 🙂

下载:Vitria:BusinessWare – 敏捷商务过程集成的领先平台
查看:
Vitria(远创科技)主页