`
liwenshui322
  • 浏览: 511275 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Openbravo3.0 体系结构

 
阅读更多

   在Openbravo 3.0中,我们由一个传统的servlet结构转换到了一个全新的富因特网应用程序结构(Rich Internet Application (RIA))。在这种富客户端结构中,请求页面与请求数据是分开的。第一次点击的时候,请求到了页面,如果不关闭这个页面的话,这个页面一直可以重用,只需要继续请求你需要的数据填充到页面即可。与js框架jquery,ext,还有flex雷同,只是openbravo里面用的是另外一种技术叫做smartclient。

 

         一. 体系构架图

 

         1. JSON模块

         用于客户端与服务器端数据交换,是一种约定的数据交换的格式。

 

         2. Weld模块

         该模块提供独立的注入与组件的的管理。

 

         3. kernel模块

         该模块专注于一些基础任务,比如:请求的处理,事件的处理,压缩信息,缓存。

 

         4. datasource模块

         该模块使用json模块提供的数据,将这些数据整理成客户想要的结果。

 

         5. smartclient模块

         提供smartclient用户客户端库类

 

         6. application模块

         该模块管理导航栏,表格,单据等客户端组件的生成以及服务器端代码

 

 

 

         二. 操作界面主要概念: 控件和控件提供者

 

         openbravo3.0前台界面是由各种控件组成的,控件可以是一个多选框,一个文本框,或者仅仅是一个布局或者一个表格。

         控件是在模块里面实现的。在模块中,控件是由控件提供者管理的,它负责创建控件,并且将控件提供给Openbravo kernel模块。控件提供者也负责一些静态内容的注册,下面会提到。

 

          控件可以通过一个URL获取,比如下面一个URL可以获取销售发票窗口的javascript脚本:

http://localhost:8080/openbravo/org.openbravo.client.kernel/OBUIAPP_MainLayout/View?viewId=_167

 

         OBUIAPP_MainLayout 标志请求的是客户端模块,模块中的控件提供者知道怎么通过剩下的那段URL:View?viewId=_167来创建控件,也就是请求的javascript。下面这个图演示了这个流程:

 


 

        整个请求流程有如下步骤:

 

        1. 一个控件请求(包含控件类型和ID)到达客户端Kernel模块

 

        2. 基于控件类型,kernel模块找到负责处理这类请求的模块(后面称之为A模块),将这个请求转发过去。于是A模块根据控件ID来创建模块。

 

        3. A模块从数据库表,或者其他资源读取到控件的定义,然后实例化这个控件。

 

        4. 这个控件会创建返回客户端的形式,如果是一个模板控件,这个控件会调用一个模板程序来创建一个模板。

 

        5. 返回的“视图”是由模板创建的或者由java后台代码生成的一段字符串。

 

        6. 返回的视图被客户端kernel模块接收,kernel模块会检查结果的语法和压缩要返回的结果。

 

        7. 将压缩的结果返回给请求者。

 

 

        三. Weld模块介绍

 

        Weld 是一个实现了JSR-299标准的框架,JSR-299定义了java上下文依赖注入的标准。Openbravo使用weld框架来实现独立注入,控件管理以及业务事件管理。首先,定义控件和控件的生命周期。然后,将定义的控件注入其他控件当做。需要注意的是,如果要使用weld独立注入功能,我们不可以通过原始的构造器来创建对象。

 

         1. 生命周期定义

          控件的生命周期可以有三种类型:ApplicationScope,SessionScope,RequestScope。生命周期是通过声明的形式表达的。比如:

 

 

@SessionScoped
public class MenuManager implements Serializable {
....
}
 

 

          2. 注入

          定义好的控件可以被自动注入到其他的控件里面,通过使用注入声明。比如:

 

 

@Inject
private MenuManager menuManager;
 

 

          3. Weld实例化对象

          weld框架在新建对象的时候通常要注入一些控件,也就意味着不能通过new 这样的方式创建对象。其实,通常我们不需要直接通过new的方式创建对象,对象一般都是通过注入的方式来传递的。不过如果你的对象不能够注入的方式传递,Openbravo提供了一个工具方法来帮助你:

           org.openbravo.base.weld.WeldUtils.getInstanceFromStaticBeanManager(Class<T> type);

           会返回一个实例化的对象。

 

          4. 分析路径

           Weld 根据路径来找到哪些控件有声明为注入方式(路径被定义在META-INF/beans.xml),为了避免搜索所有的class文件,beans.xml文件定义了哪些路径不进行搜索。

 

 

           四. Etags,Caching(缓存)和Compressing(压缩)

 

           为了提高用户的访问速度和体验,我们需要在浏览器端和服务端提供缓存机制。

           Openbravo实现了几种不同类型的缓存在不同的应用层次,它利用了模块管理的优点,一个模块是否在开发状态,缓存机制是不同的。如果一个模块在开发状态,会阻止使用缓存,因为我们不想要缓存影响了我们的测试结果。但是,如果不是处于开发状态,就会最大化使用缓存机制。

 

            1. 静态JS文件缓存与更新

             一个静态的JS文件包含了控件使用的一个文本库或者一个标准的控件。通常浏览器会缓存这个JS文件,不过与这个JS文件相关的模块更新时,这个静态的JS文件也会更新。过程如下:

            JS静态文件是由模块提供的,模块通过它的控件提供者(ComponentProvider)发布它的静态资源,ComponentProvider通过GetGlobalResources方法获得资源,Openbravo将所有相关联的静态资源整合到一个大的JS文件当中,整合的顺序与根据模块间的关系有关. 这个JS文件的名称是一串字符串,比如:088afd247a8fe06c91a654891a1358a2.js 名称与内容相关。如果内容发生改变,名称也会变,这样浏览器也就会重新加载。这个静态的JS文件生成在web/js/gen下面。(如果模块属于开发状态,JS文件将不会被压缩)

 

             2. 控件的缓存与更新

             控件是动态的,包含运行时从数据库读取的数据。所以控件是在用户请求的时候生成的,缓存在服务器端。服务器端可以验证一个控件是否已经改变,从最后一次请求开始。在客户端,是不可以进行验证的。

             为了验证服务器端缓存的控件是否与客户端的缓存的一致,我们使用了一个概念Etag,Etag类似于一个hashcode。用于验证,服务端缓存的控件的内容是否改变,自从浏览器最后一次请求之后。

             Etag生成方式:

             a. 模块不属于开发状态,Etag=用户名称+模块版本

             b. 模块属于开发状态,Etag=用户名称+当前时间(精确到毫秒)

 

             3. 压缩与语法检查

             模块输出的内容在发给客户端前,可以进行语法检查和压缩。压缩用的是jsmin, 语法检查用的是JSLint和JSLint4Java. 如果模块属于开发状态,会进行语法检查但是不会压缩。但是,如果模块不属于开发状态,只进行压缩,不进行语法检查。

 

 

             五. 模块的代码构架

 

              一个模块在openbravo里面的代码结构是固定的,参考如下图:


          

 

        1. java代码放在src目录下面

        2. 静态的JS文件放在:web/[modulepackage]/js

        3. 样式文件(css, images, js)所在目录:web/org.openbravo.userinterface.smartclient/3.00/[modulepackage]             

 

         六. 控件提供者

 

          每一个module都会实现一个控件提供者,一个控件提供者(componet provider)完成如下二个任务:

          1. 用户的请求从kernel模块发过来后,它负责创建用户请求的控件(componet)

          2. 它负责将module中的注册静态的资源到openbravo kernel。

 

          控件提供者(componet provider)是一个weld控件,一个控件提供者类必须有2个声明:

          1. @ApplicationScoped标志, 标明这个类是一个单例。

          2. @ComponetProvider.Qualifier 为这个类注册一个唯一的标识

          比如:

 

@ApplicationScoped
@ComponentProvider.Qualifier(ExampleComponentProvider.EXAMPLE_VIEW_COMPONENT_TYPE)
public class ExampleComponentProvider extends BaseComponentProvider {
  public static final String EXAMPLE_VIEW_COMPONENT_TYPE = "OBEXAPP_ExampleViewType";

         PS:控件提供者类必须跟它的module处于同一个包下面。

 

        控件提供者还有另外一个事情,实例化一个控件。

        比如:

 

public Component getComponent(String componentId, Map<String, Object> parameters) {
  if (componentId.equals(ExampleViewComponent.EXAMPLE_VIEW_COMPONENT_ID)) {
    final ExampleViewComponent component = new ExampleViewComponent();
    component.setId(ExampleViewComponent.EXAMPLE_VIEW_COMPONENT_ID);
    component.setParameters(parameters);
    return component;
  }
  throw new IllegalArgumentException("Component id " + componentId + " not supported.");
}

         PS:这个方法是在你有动态内容的时候才有用处,如果你的module只有静态的JS/CSS,这个方法可以为空,或者直接抛异常。

 

         控件可以很简单,如果它集成系统的BaseComponet类,只需要实现generate方法即可。

 

         控件提供者最后一个事情是,注册静态资源(CSS,JS).这些静态资源会被Openbravo kernel 关联压缩。比如:

 

public List<ComponentResource> getGlobalComponentResources() {
  final List<ComponentResource> globalResources = new ArrayList<ComponentResource>();
  globalResources.add(createStaticResource(
      "web/org.openbravo.client.application.examples/js/example-view-component.js", true));
  globalResources
        .add(createDynamicResource("org.openbravo.client.kernel/"
            + ExampleViewComponent.EXAMPLE_VIEW_COMPONENT_ID + "/"
            + ExampleViewComponent.EXAMPLE_VIEW_COMPONENT_ID));
  globalResources.add(createStyleSheetResource(
        "web/org.openbravo.userinterface.smartclient/openbravo/skins/"
            + KernelConstants.SKIN_VERSION_PARAMETER
            + "/org.openbravo.client.application.examples/my-styles.css", false));
  return globalResources;
}
 

 

       七. 服务端处理客户端请求集散中心

 

       openbravo提供一种方便客户端发送给服务端请求的机制,叫做ActionHandler(请求控制中心)。

 

       服务器端代码示例:

 

 

package org.openbravo.client.application.examples;
 
....
 
public class MyActionHandler extends BaseActionHandler {
  /**
   * Needs to be implemented by a subclass.
   * 
   * @param parameters
   *          the parameters obtained from the request. Note that the request object and the session
   *          object are also present in this map, resp. as the constants
   *          {@link KernelConstants#HTTP_REQUEST} and {@link KernelConstants#HTTP_SESSION}.
   * @param content
   *          the request content (if any)
   * @return the return should be a JSONObject, this is passed back to the caller on the client.
   */
  protected JSONObject execute(Map<String, Object> parameters, String content) {
    try {
       // create the result
       JSONObject json = new JSONObject();
       json.put("result", "success");
 
       // and return it
       return json;
     } catch (Exception e) {
       throw new OBException(e);
     }
   }
}
 

       客户端代码示例:

 

// define the callback function which shows the result to the user
var callback = function(rpcResponse, data, rpcRequest) {
    isc.say('The result is : ' + data.result);
};
        
// and call the server
OB.RemoteCallManager.call('org.openbravo.client.application.examples.MyActionHandler', {}, {}, callback);
 

 

       八.  应用初始化

 

        当我们的一个应用启动的时候,我们通常要初始化一些事情。比如注册SQL当我们使用hibernate的HQL的时候。Openbravo提供一种机制来实现这种初始化工作。需要做到两点:

        1. 创建一个实现了org.openbravo.client.kernel.ApplicationInitializer接口的类 

        2. 声明这个接口为@ApplicationScoped

        比如:

 

@ApplicationScoped
public class KernelApplicationInitializer implements ApplicationInitializer {
 
  public void initialize() {
    OBDal.getInstance().registerSQLFunction("ad_column_identifier_std",
        new StandardSQLFunction("ad_column_identifier_std", StandardBasicTypes.STRING));
  }
}

        Weld框架将会自动找到这些初始化类,当初始化数据库访问层的时候。

 

 

 

 

 

 

 

  • 大小: 89.4 KB
  • 大小: 27.5 KB
分享到:
评论
1 楼 ithinkopen 2013-03-27  
     楼主甚是强大

相关推荐

Global site tag (gtag.js) - Google Analytics