2008-08-27

The old classloader issue happened again when we use XStream with OSGi environment. A wrapper of the XStream parser function has been created to solve this issue. I also paste the code to XStream mailing list. Hopefully, they can use this new function to replace the old one.

The wrapper function is very simple.

public <T> T fromXML (String xmlString, Class<T> clazz) {
     this.setClassloader (clazz.getClassloader());
     return (T)super.fromXML (xmlString);
}

I believe the classloader of the corresponding class will be the best classloader to load the class.

Of course, if you do the alias mapping before the serialization and deserialization, there will be no more classloader issue.

2008 年 08 月 27 日 23:22 | ZhongHaoming | Comment 共0条留言
2008-07-22

Overview

In echo3’s definition, a service is an object who can process connections, parsing an HTTP request and producing an HTTP response.

A service composites of three functions: getId() , getVersion(), and service(Connection conn);

All services are registered in the ServiceRegistry stored in the WebContainerServlet. the mapping between URLs and Services are logically decided by the corresponding WebContainerServlet’s url with the sid parameter in HTTP requests. The UserInstance class provide the concrete method to generate the url string for related services.

<servletURL>?<WebContainerServlet.SERVICE_ID_PARAMETER>=<Service.Id>[&<argName>=<argValue>]*

UserInstance

The UserInstance is an interesting class. According to the nextapp’s document, it is a representation of a single user-instance of an application hosted in the web container. Means, it is the actual application context object, in which the class should only contain things related with the application instances. However, the class is a little bit messy. The class composites of those elements
  • Container for a group of application related objects
    • ApplicationInstance
    • ClientConfiguration
    • ClientProperties
    • componentToRenderStateMap
    • IdTable
    • initialRequestParameterMap
    • HttpSession
    • taskQueueToCallbackIntervalMap
    • UserInstanceUpdateManager
  • a group of utility functions
    • getClientRenderId(Component component)
    • getRootHtmlElementId()
    • getServiceUri()
  • Session life cycle management
    • implementation of HttpSessionActivationListener
    • implementation of HttpSessionBindingListener

WindowHtmlService

Within all services, the WindowHtmlService is the service used to handle the first connection and produce the initial HTML page to the client. The current implementation is just produce the empty HTML who only include all necessary java scripts and call the bootstrap function when the page is loaded in.

How to add a new service

Implement the service interface, and register it with the

2008 年 07 月 22 日 17:28 | ZhongHaoming | Comment 共0条留言
2008-07-17

Compare to Echo 2, the new release established a solid javascript framework to support the Application Models in client side. Therefore, the user can only work with the Client side lib to creat their AJAX applications independently from the echo 3 server side. However, from my opinion, there are some drawbacks for the echo3 client side and server side.

  • The client side

1. The new client side echo framework is a model oriented framework, not a presentation oriented framework. i.e., Pages are generated based on the component models defined by the js in memory rather than the tagged HTML pieces. Developers cannot benefit from the flexibilities and effectivities of HTML.

2. Heavy js coding is not easy, the js development environment is not mature enough to support complex client side js structures. That reduces the meanings of the “independency” for the Echo3 client side. To create applcation model in java with echo3 server side is still the best choice for Echo3 application developers.

  • The Server side

1. The server side still uses the internal service model to wrap all elements, not only the real “services”, but also the resources like scripts and images, and even the resource folders. That put a heavy loading on the Echo 3 core, and largely reduce the performance.

2. No REST support, too immersive client/server communication patterns, which break the basic rules of the internet. It looks llike the drawback of most richclient frameworks. (refering Bill Higgins’s article: Ajax and REST.)

3. The programming model is not open. It is impossible to establish a collaboration between applications from two different web server. i.e., it is hard for us to create a single page with contributions from applications running in different web servers. That also bring troubles for mashups.

Current, my conclusion is

1. At current stage, to use echo3, we’d better keep working on the server side programming model.

2. The direct HTML supporting must be provided 3. An open service/resource loading pattern must be introduced to support REST and the REST based mashups and collaborations.

2008 年 07 月 17 日 10:10 | ZhongHaoming | Comment 共0条留言
2008-07-16

Echo3新的独立前台AJAX应用框架给我们带来了一些惊喜,然而后台的Echo3看上去并没有多大的提高。资源和JS仍然是通过注册Service来获取。这种做法违背了REST的法则,有以下三个严重的缺陷

  • 所有对资源的请求必须由Echo核心框架来处理,而降低了系统效率。
  • 由于核心是动态处理资源请求,导致Last-Modified项全部是当前时间,网络Cache无法起到作用,进一步降低了系统效率。
  • 这种设置,导致无法使用更为开放的应用结构。连同OSGi这样的开放性平台整合都有问题,更不用说更大范围的Collaboration以及Mashup。Echo3依然被限制在单WebServeer的应用机制之下。

为了应用Echo3在更开放的Web体系之下,一个基于REST原则的,开放的URI和JS/Resource/Service之间的Mapping关系是必须的。幸运的是,新的前台框架带来了这种改变的可能性。Echo3 Client作为一个独立的组件,不再依赖Echo3 Server side。这对于我们来说或许是个好消息。

2008 年 07 月 16 日 17:30 | ZhongHaoming | Comment 共0条留言
2008-07-14

接下来,我们扩充一下Helloworld,让他能够展示更多的细节

首先是事件,我们向HelloWorldApp里面增加两个个新的Component: Column和Button。Column作为一个Contaienr Component,用来装载原来的HelloWorld Label以及新的Button。我们定义Button如下

HelloWorldApp.Button = Core.extend(EchoApp.Button,{
    $construct: function() {
        EchoApp.Button.call(this, {
            text: "Please click me!" 
        });
    }
});
然后重新写HelloWorldApp为
HelloWorldApp = Core.extend(EchoApp.Application, {
    label: null,

    $construct: function() {
        EchoApp.Application.call(this);
    column = new EchoApp.Column ();
        label = new EchoApp.Label({
            text: "Hello, world!" 
        });
        this.rootComponent.add(column);
    column.add(label);
    button = new HelloWorldApp.Button();
    column.add(button);
    }
});
这样,当页面显示的时候,就会有两行字符出现了。分别是label的”Hello, world”和Button的”Please click me!”。我们再为该Button增加一个事件响应函数。如下
HelloWorldApp = Core.extend(EchoApp.Application, {
    ...
    _pressButton: function () {
        label.set("text","pressed");
    },

    $construct: function() {
        ...
    button.addListener ("action",this._pressButton);
    ...
    }
});
重新载入页面之后,点击”Please click me!”, label里面的字符就会变成”pressed”. 接下来看Stylesheet。StyleSheet可以通过Application来进行绑定。我们初始化一个StyleSheet的实例如下
HelloWorldApp.StyleSheet = new EchoApp.StyleSheet({
    "Default": {
        "Button": {
        border: "1px outset #709bcd",
        foreground: "#000000",
        background: "#efefef",
        pressedBorder: "1px inset #709bcd" 
    }
    }
});
然后,在init函数里面设置HelloWorldApp的StyleSheet为HelloWorldApp.StyleSheet
init = function() {
    ...
    var app = new HelloWorldApp();
    app.setStyleSheet (HelloWorldApp.StyleSheet);
    ...
}
同时,我们可以在相应的控件里面设置其对应的StyleSheet 名称。这里我们使用HelloWorldApp.Button为例子
HelloWorldApp.Button = Core.extend(EchoApp.Button,{
    ...
    $construct: function() {
        EchoApp.Button.call(this, {
        ...
            styleName: "Default",
            ...
        });
    }
    ...
});
再次调入页面,你会发现button的显示已经被改变了。
2008 年 07 月 14 日 17:09 | ZhongHaoming | Comment 共0条留言
2008-07-11

Echo3 的官方 Helloworld

Echo3 官方的 Client-side application的Helloworld在这里 为了运行这个Demo,我们首先需要搭建相应的环境

环境 创建如下的目录结构

project/--- js --- corejs
               +-- echo
               `-- render

将Core.js, WebCore.js 复制到/js/corejs下面,将Application.js, Client.js, Serial.js, FreeClient.js复制到/js/echo下面,将ApplicationRender.js和所有的Render.*.js复制到/js/render下面

官方HelloWorld

在/js/下面创建你自己的javascript: DemoApp.js。我们先按照官方Helloworld的写法做一个。值得注意的是,官方的Helloworld的命名空间和我们从Beta1版本拿到的并不完全一致。譬如Echo.Application和Echo.Label在实际的代码中,应该是EchoApp.Application和EchoApp.Label。以及Echo.FreeClient,实际上是EchoFreeClient

init = function() {
    var app = new HelloWorldApp();
    var client = new EchoFreeClient(app, document.getElementById("rootArea"));
    client.init();
};

HelloWorldApp = Core.extend(EchoApp.Application, {

    $construct: function() {
        EchoApp.Application.call(this);
        var label = new EchoApp.Label({
            text: "Hello, world!" 
        });
        this.rootComponent.add(label);
    }
});

在项目根目录下创建demo.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <script type="text/javascript" src="js/corejs/Core.js"></script>
  <script type="text/javascript" src="js/corejs/WebCore.js"></script>

  <script type="text/javascript" src="js/echo/Application.js"></script>
  <script type="text/javascript" src="js/echo/Serial.js"></script>
  <script type="text/javascript" src="js/echo/Client.js"></script>
  <script type="text/javascript" src="js/echo/FreeClient.js"></script>

  <script type="text/javascript" src="js/render/ApplicationRender.js"></script>
  <script type="text/javascript" src="js/render/Render.js"></script>
  <script type="text/javascript" src="js/render/Render.Button.js"></script>
  <script type="text/javascript" src="js/render/Render.ArrayContainer.js"></script>
  <script type="text/javascript" src="js/render/Render.ContentPane.js"></script>
  <script type="text/javascript" src="js/render/Render.Grid.js"></script>
  <script type="text/javascript" src="js/render/Render.Label.js"></script>
  <script type="text/javascript" src="js/render/Render.List.js"></script>
  <script type="text/javascript" src="js/render/Render.SplitPane.js"></script>
  <script type="text/javascript" src="js/render/Render.TextComponent.js"></script>
  <script type="text/javascript" src="js/render/Render.WindowPane.js"></script>

  <script type="text/javascript" src="js/DemoApp.js"></script>

  <title>Echo3 Client-Side Hello World</title>
 </head>
 <body onload="init();">
   <div id="rootArea" style="position:absolute;width:100%;height:100%;"></div>
 </body>
</html>
2008 年 07 月 11 日 11:20 | ZhongHaoming | Comment 共0条留言
2008-07-10

Client.js

client是用来在抽象的Application和实际的DOM Element之间的一个桥梁对象。该对象连接一个Application和一个相应的DOM Element, 并且通过client之间的从属关系来建立层次化的结构。同时,client使用静态变量来存储并管理所有active的client, 以及resources, 并处理浏览器的windowsResize事件。

Client本身比较简单,但是他有两个很重要的实现,一个是RemoteClient,另一个是FreeClient,分别用来代表在服务器端运行的Application和在浏览器内运行的Application

FreeClient.js

FreeClient是一个简单的Client的实现,如果是前端的Application,则可以选择使用这个Client的实现来连接Application和Server端。

Serial.js

EchoSerial是一个用来解析客户端和服务端XML通讯的一个工具类。用来将XML的输出转换成相对应的元素
  • loadComponent (Client client, DOMElment componentElement, referenceMap) 生成一个componentElement所对应的Component对象并返回
  • loadStyleSheet (client, ssElement, referenceMap) 生成一个ssElement所对应的StyleSheet对象并返回
  • loadProperty (client, propertyElement, object, styleData, referenceMap)和storeProperty (client, propertyElement, propertyValue)则是分别将属性从XML读入和将属性输出至XML
为了提高效率,在做属性和XML之间的转换的时候,很多type都使用了单字符的缩写。EchoSerial里面_propertyTranslatorMap用来存放以对象类型名为索引的translator。他们的对应关系如下
缩写 类型
0 null
b boolean
f float
i integer
s String
d date
m map
Alignment 或 AL Alignment
Border 或 BO Border
Extent 或 X Extent
FillImage 或 FI FillImage
FillImageBorder 或 FIB FillImageBorder
Font 或 F Font
ImageReference 或 I ImageReference
Insets 或 N Insets
LayoutData 或 L LayoutData
Serial的Translator相关函数如下
  • addPropertyTranslator (String ClassName, propertyTranslator) 按照class name添加property Translator
  • addPropertyTranslatorByType (Class type, propertyTranslator) 按照type来添加property Translator
2008 年 07 月 10 日 17:14 | ZhongHaoming | Comment 共0条留言

Application.js

Application.js定义了前台的Application框架

EchoApp

一个简单的Application ID序列号生成器。唯一的公共函数generateUid()将返回一个递增的序列号

EchoApp.Component

前端的Component模型。在Echo3里面,Component就是一个聚合点,它连接了Parent component, Children component, Event Listener, 以及所属Application之间的关系。相应的函数有
  • Parent-child 关系相关的函数
    • add (component, index) 添加一个新的child
    • getComponent (index) 获取相应的child component
    • getComponentCount () 返回children的个数
    • indexOf (component) 返回相应component的index number
    • isAncestorOf (component) 判断当前Component是否是指定component的前辈
    • remove (componentOrIndex) 删除指定的component
    • removeAll () 删除所有的children
  • Event-listener关系相关的函数
    • addListener (String eventType, Function eventTarget) 注册指定的事件处理函数。该函数将会引发component所属Application的’listeners’事件。oldValue=null, newValue=eventType.
    • fireEvent (event) 引发指定事件
    • removeListener (String eventType, Function eventTarget) 删除事件处理函数。该函数将会引发component所属Application的’listeners’事件。oldValue=eventType, newValue=null
  • 与Application的从属关系
    • register (application) 一个Component只能注册在一个Application之下。如果传入的application为null/false,则执行unregister操作,将自身以及child components从所注册的application当中移除。如果传入的application属于有效的application, 则将自己注册为该application的component,并将所有child components也注册到该application之下。

除了这些关系之外,Component还管理一些自身属性,他们包括style, enabled/disabled, componentType, render

EchoApp.FocusManager

为Application提供Focus服务的类。提供基于focus order的管理功能。提供两个函数find和findInParent,前者按指定顺序在Application内查找下一个focus的component, 后者则在parent component内查找系一个focus的component。

EchoApp.LayoutDirection

layout的方向,从左到右,或者是从右到左。通常使用两个预定义的变量EchoApp.LayoutDirection.LTR和EchoApp.LayoutDirection.RTL

EchoApp.StyleSheet

管理Application的Stylesheet。根据style name和component type来管理styles。两个相类似的函数getStyle和getRenderStyle。区别在于前者只是检查所给出的Component type的style,如果不存在则返回null,而getRenderStyle则会检查父类的style,如果给定component type的相应style不存在的话。

setStyle(String name, String componentType, Object style),用来设置style的函数

EchoApp.Update

命名空间,无内容

EchoApp.Update.ComponentUpdate

用来记录对当前显示页面的component所作的修改。每个实体对应一次修改的记录。

EchoApp.Update.Manager

用来帮助Application管理从上次repaint到目前所有发生过的ComponentUpdate记录。

EchoApp.Application

一个虚类,所有的Application必须继承此类。Application利用UpdateManager和FocusManager来帮助管理update records和focus的顺序。每个Application有自己的StyleSheet, RootComponent用来存放root component,另外还内建render id到各个component的映射。函数包括

  • addComponentUpdateListener (Function l)/removeComponentUpdateListener (Function l) 增加/删除一个Component Update的事件处理函数
  • addFocusListener (Function l)/removeFocusListener (Function l) 增加/删除一个Focus changed的事件处理函数
  • dispose () 清除该应用,一旦调用,相关资源将被释放。该Application不能再被使用
  • focusNext (boolean reverse) 按照指定顺序设置下一个focused component
  • getFocusedComponent () 获得当前focused component
  • setFocusedComponent (Component newValue) 设置当前focused component
  • getComponentByRenderId (String renderId) 根据renderId获取相关的component
  • getLayoutDirection ()/setLayoutDirection (LayoutDirection ld) 获取/设置layout的方向
  • getModalContextRoot () 获取modal context的root component(不太清楚这个东西的用途)
  • getStyleSheet ()/setStyleSheet (StyleSheet style) 获取/设置StyleSheet
  • notifyComponentUpdate (Component parent, String propertyName, oldValue, newValue) 通知application相关component的更改 *

内建Component

Component --> AbstractButton --> Button
          |                  `-> ToggleButton --> CheckBox
          |                                   `-> RedioButton
          +-> AbstractListComponent --> ListBox
          |                          `-> SelectField
          +-> Column
          +-> Composite --> Panel --> ContentPane
          +-> Grid
          +-> Label
          +-> Row
          +-> SplitPane
          +-> TextComponent --> TextArea
          |                 `-> TextField --> PasswordField
          `-> WindowPane

Render.js

Render.js主要建立了两个框架,一个是DOM和Component同步peer的框架,另一个是管理peers的框架

EchoRender

EchoRender是一个静态对象,主要用来管理所有的Sync peers, 以及分派Rendering任务给相应的peers。EchoRender使用一个map来管理所有的peers。peers按照其所对应的ComponentType来存放。相关函数如下:
  • getPeerClass (Component component) 根据component的所属componentType返回对应的peer class
  • registerPeer (String componentName, peerObject) 这里两个参数名都有问题,第一个应该是componentType, 因为实际传入的应该是component type name。而第二个应该是peerClass,因为实际传入的应该是使用Core.extend定义的peer class
EchoRender的第二个任务就是作为rendering的总入口, 接受从外部传来的drawing/updating任务,并分派给相应的peer完成。相关的接口有
  • notifyResize (Component parent) parent的尺寸改变,该函数将重绘所有parent的children
  • processUpdates (EchoClient client) 更新client所对应的application的所有update记录到DOM
  • renderComponentAdd (ComponentUpdate update, Component component, Element parentElement) 将一个新的componet加入到已有的DOM元素parentElement中去。update是相应的update记录。完成之后,component.peer将被设置成相应的peer对象
  • renderComponentDispose (ComponentUpdate update, Component component) 与renderComponentAdd相反,该函数将一个已有的component从DOM tree中删除
  • renderComponentDisplay (Component parent) 该函数强制刷新指定的componet以及其内部的children

EchoRender.ComponentSync

Sync peer的虚类定义。定义了5个静态变量,FOCUS Permission标志,分别是UP,DOWN,LEFT,RIGHT,和ALL。两个变量,client标识所属的client。component标识所服务的EchoApp.Component。三个抽象函数,renderAdd(ComponentUpdate update, parentElement), renderDispose (ComponentUpdate update), renderUpdate (ComponentUpdate update)分别用于同步添加,删除,和修改操作。

另外还有三个虚函数,分别是getFocusFlags,renderFocus,renderDisplay。第一个是用来获取focus flag的,后面两个,是特殊情况下的render函数,仅对某些特殊Widget有效。

EchoRender.RootSync

Root Sync peer,被注册为Root的peer

2008 年 07 月 10 日 11:20 | ZhongHaoming | Comment 共0条留言
2008-07-09

从Echo 2转到Echo 3,nextapp是彻底转向AJAX架构了。服务器端,echo3基本上保持和了echo2一贯兼容的API,不过加强了客户端的结构设计。通信协议也改成XML了。所以结构分析就得从客户端入手了。

core.js

这个是核心结构。Core.js定义了一个灵活的对象框架,支持抽象和虚拟属性,并提供了检查函数来对子类进行检查。定义了一个类似于java.lang.reflect.Method类似的Method对象,使用Core.method()函数进行调用。定义了基本的事件管理机制。此外,core.js还定义了一些数组和Map相关的函数,以及一些调试使用的函数。

core.js里面定义的结构包括Core, Core.Debug, Core.Arrays, Core.Arrays.LargeMap, Core.ListenerList, Core.ResourceBundle,

Core

先说Core,Core定义了对象的基本定义和操作方式。Core里面最为关键的一个函数就是Core.extend()。这个函数可以用来定义一个类型,或者是从一个已有类型上扩展生成一个新的对象。从Core.extend的函数说明里面,我们可以了解Core对类型定义的一些特殊约定。

首先是特殊元素。所有开头为$的元素即为特殊元素,其中,某些预先定义的特殊函数有
  • $construct, 构造函数,在构造该类型的对象时会被调用
  • $load, 静态初始化函数,在该类型完成定义之后,会被调用一次
  • $static, 静态属性,属于该类型的元素
  • $abstract, 抽象定义元素,等于”true”或者是一个对象。如果定义为对象,则该对象的所有函数都必须被当前类型的子类所实现。如果为”true”的话,则意味着当前类型为抽象类型
  • $virtual, 只有在此对象中定义的函数才可以被当前类型的子类型所重载
  • $super,内定义属性,指向该类型的父类
  • $include, 定义Mixin类型,所有在include里面定义的类型,将被copy到当前类型的prototype当中去

Core.Debug

Core.Debug 用来输出debug信息的类型,定义了consoleWrite函数来输出任何消息。里面也定义了基于DOM的consoleElement用来连接到console的widget.

Core.Array

Core.Array 提供了一些基本的基于Array的操作,有
  • containsAll
  • indexOf
  • remove
  • removeDuplicates

Core.Array.LargeMap

Core.Array.LargeMap 第一个用Core.extend定义的类型。这个类的目的是为了弥补IE的memory leak漏洞。实现的很简单,就是每隔固定次数(缺省250次)的删除操作之后,将整个map拷贝到一个新的map当中去。

Core.ListenerList

Core.ListenerList 另一个使用Core.extend定义的类型。该类型定义了一个简单的event listener repository。用户使用addListener(String eventType, function eventTarget)来注册相应的eventListener. 使用fireEvent (event)来发起事件。其中event.type用来标识事件类型。

Core.ResourceBundle

Core.ResourceBundle 是用来管理多语言的ResourceBundle的工具类。

WebCore.js

Core.js之外,下一个比较重要的js文件就是WebCore.js。WebCore分为8个部分:WebCore.DOM, WebCore.Environment, WebCore.EventProcessor, WebCore.HttpConnection, WebCore.Library, WebCore.Measure, WebCore.Scheduler, 和WebCore.VirtualPosition。

WebCore.DOM

首先看WebCore.DOM。 在DHTML/Javascript/DOM模型里面,一个XML Element就可以对应一个相应的可视对象。WebCore.DOM就是用来帮助创建和管理页面可视对象的辅助工具类。这个类的主要目的是统一W3C DOM标准和IE标准里面对DOM和相应的Event处理不一致的地方。这些函数包括
  • 向对象添加事件响应 (addEventListener (eventSource, eventType, eventListener, useCapture))。
  • 删除已有的事件响应(removeEventListener(eventSource, eventType, eventListener, useCapture))。
  • 获取事件的鼠标位置(getEventOffset (event))。
  • 获得事件对象(getEventTarget(event))。
  • 获取时间相关对象(getEventRelatedTarget(event))。
  • 还有两个关于Event flow的控制函数stopEventPropagation和preventEventDefault。关于DOM Event flow的控制请参见W3C DOM标准
除了这部分之外,WebCore.DOM也提供了相应的DOM模型管理函数,他们是
  • createDocument (namespaceUri, qualifiedName) 在给定的命名空间下面创建一个名称为指定名称的DOM对象
  • focusElement (element) 将指定对象设为当前focus
  • getChildElementByTagName (parentElement, tagName)根据名字寻找第一个匹配的子对象
  • getChildElementsByTagName (parentElement, tagName)根据名字寻找所有匹配的子对象
  • isAncestorOf (ancestorNode, descendantNode) 判断ancestorNode是否是descendantNode的前辈节点(父节点,或者父父..父节点)
  • removeAllChildren (node)移去当前节点的所有子节点
  • removeNode (node) 从DOM tree当中移出指定节点

WebCore.EventProcessor

提供了统一各浏览器平台的事件处理函数的工具类。该类只提供了3个事件相关的公开函数,分别是
  • add (Element element, String eventType, Function eventTarget, boolean capture) 给指定element的制定Event添加listener
  • remove (Element element, String eventType, Function eventTarget, boolean capture) 移除指定的listner
  • removeAll (Element element) 移除指定Element的所有listener

WebCore.EventProocessor.Selection

设置对指定对象允许或者禁止Selection。因为对某些对象,双击可能引发selection,因此可以通过调用这里定义的函数对指定对象屏蔽selection功能。两个函数disable(element)和enable(element)

WebCore.Environment

WebCore.Environment定义了所有浏览器相关的环境信息。首先是浏览器种类
  • BROWSER_OPERA, BROWSER_SAFARI, BROWSER_KONQUEROR, BROWSER_FIREFOX, BROWSER_MOZILLA, BROWSER_INTERNET_EXPLORER 等6个boolean变量分别对应6大浏览器家族。
  • BROWSER_MAJOR_VERSION, BROWSER_MINOR_VERSION定义了当前浏览器的版本号
  • 剩下一些杂项主要用来让其他函数识别相应的浏览器功能。从定义上看,Echo3主要支持IE, Mozilla/Firfox,和Opera

WebCore.HttpConnection

该类型包装了不同浏览器对XMLHttpRequest的支持。HttpConnection不是一个工具类,而是一个实体类,需要初始化。初始化函数为$construct (String url, String method, Object messageObject, String contentType)。其中method为GET或者是POST, messageObject可以是任意字符串,或者是一个DOM对象。contentType就是对应的content-type的值。 函数包括
  • addResponseListener (Function l) 添加响应函数。该对象的使用方法是使用相应的参数初始化对象之后,将结果响应函数通过addResponseListener加入,然后调用connect函数。相应的结果将以event的形式传送到响应函数内。event的格式是
event = {
   type: "response",
   source: this, //即该HttpConnection自身,响应函数可以通过调用getStatus, getResponseText, 以及getResponseXml来获取结果。
   valid, //true代表成功,false代表失败
   exception //如果是异常的话,则存在该变量
}
  • removeResponseListener (Function l) 移除响应函数
  • connect () 开始建立连接并提交请求
  • getStatus (), getResponseText (), getResponseXml () 获取结果
  • dispose () 清除该对象。

WebCore.Library

用来动态调入其他javascript library的工具类。接口和使用方法都很简单。所有的library都会以WebCore.Library.Group来组织。Group有以下几个公开函数
  • add(String libraryUrl) 添加相应library的URL
  • addLoadListener (Function l) 添加一个响应函数(非必须)
  • removeLoadListener (Function l) 删除一个响应函数
  • hasNewLibraries () 检测是否有未load的lib
  • load () 调入该组内所有的库

使用方法很简单,创建一个WebCore.Library.Group,然后通过add函数加入相应的库,再调用load调入所有的库即可。

WebCore.Measure

用来进行分辨率尺寸换算的一个工具类。唯一的一个公开接口是extentToPixels (Number value, String units, boolean horizontal)。类里定义了”in”,”cm”,”mm”,”pt”,”pc”,”em”,”ex”到pixel的转换率。并且使用_calculateExtentSizes函数进行修正。

WebCore.Measure.Bounds

WebCore.Measure内定义的一个类,用来帮助计算相应DOM对象的大小以及位置。

WebCore.Scheduler 和 WebCore.Scheduler.Runnable

WebCore.Scheduler是工具类,用来安排周期性任务的运行。相当于java中的Timer。Runnable则定义了一个可被执行周期性任务的对象接口。 Runnable的任何实现,必须定义函数run。Runnable还有两个可供重载的参数,timeInterval和repeat。如果是周期性任务,则需要设置repeat为true,timeInterval为相应的时间间隔(单位是毫秒)。

对于已经定义好的任何Runnale的子类,可以通过WebCore.Scheduler.add(Runnable task)或者是WebCore.Scheduler.remove(Runnable task)添加到Schedule当中去或者是从Schedule当中删除。也可以通过直接调用WebCore.Scheduler.run(Function f, Number timeInterval, boolean repeat)将指定的函数加入到schedule当中去。run将返回相应的Runnable对象(WebCore.Scheduler.MethodRunnable的一个实例)。

2008 年 07 月 09 日 10:03 | ZhongHaoming | Comment 共0条留言
2008-06-16
Java allow developers to access the HTTPS resources directly with the URL such as
...
URL url = new URL("https://www.example.com");
InputStream ins = url.openConnection().getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
...

However, for any security URL, the key pair of the site must has a corresponding record in the keystore. If java cannot found the key in the key store, a SSLHandshakeException will be throw out to complain “unable to find valid certification path to requested target”. However, you have no means to get the key at the URL level.

To get the site key, you must lower down the programming level to the SSLSocket. and add the key into the local key store. http://blogs.sun.com/andreas/entry/no_more_unable_to_find gave a good example to do that. The only thing that you should notice is that the new key store looking up order is

1. Defined by the system property javax.net.ssl.trustStore
2. <java-home>/lib/security/jssecacerts
3.<java-home>/lib/security/cacerts

And, by default, the installation of Java running environment will create the /lib/security/cacerts as the key store. However, in the Andreas’ blog and the code, he was using the jssecacerts file under the current running folder, which may bring some troubles to the system.

2008 年 06 月 16 日 17:20 | ZhongHaoming | Comment 共0条留言
2008-06-13

There are two approaches to monitoring the dynamics of Eclipse extensions.

1. Listen the event in IExtensionRegistry from Platform.
    IExtensionRegistry registry    =Platform.getExtensionRegistry();

    registry.addRegistryChangeListener(new IRegistryChangeListener() {
        @Override
        public void registryChanged(IRegistryChangeEvent event) {
            ....
        }
    });

when there are any new extension has been added or existing extension has been removed, the registryChanged function will be notified with the details in the event parameter.

Another approach is using the ExtensionTracker, which is defined in org.eclipse.core.runtime.dynamichelpers

The ExtensionTracker implements the IRegisterChangeListener. You can get an instance of it from the PlatformUI or just directly create a new one as
    IExtensionRegistry registry    =Platform.getExtensionRegistry();
    tracker=new ExtensionTracker(registry);

The ExtensionTracker use a finer grained event handler interface and filter to look for corresponding handlers and functions.
The handler interface is IExtensionChangeHandler with two functions: addExtension(IExtensionTracker tracker, IExtension extension) and removeExtension (IExtension extension, Object? objects)

The filter interface is IFilter with one function: matches (IExtensionPoint target). The ExtensionTracker provides some predefined filters, which can be access via IFilter createExtensionPointFilter(final IExtensionPoint xpt), IFilter createExtensionPointFilter(final IExtensionPoint? xpts), IFilter createNamespaceFilter(final String id).

A typical handler registration looks like
tracker.registerHandler (handler, filter);

However, the ExtensionTracker assume that once a extension got registered, it must be associated with some objects. Normally, these are configuration proxy objects for the extension. That means, for all existing extensions and newly added extensions, they must be associated with objects via calling registerObject(element, object, referenceType), otherwise, once the extension has been removed, your handler will not be notified.

Following are the complete codes about the handler of dynamic extensions
public void bindingDynamicHandler () {
    IExtensionRegistry registry    =Platform.getExtensionRegistry();
    IExtensionPoint pointer    =registry.getExtensionPoint(
    PLUGIN_ID,        
    "yourExtensionNameHere");

    if (tracker==null) 
        tracker=new ExtensionTracker(registry);
        if (pointer!=null) {
        IExtension extensions[]    =pointer.getExtensions();
        for (IExtension extension:extensions) {
            handler.addExtension(tracker, extension);
        }
    }

    tracker.registerHandler(
            handler,
            ExtensionTracker.createExtensionPointFilter(pointer));
}
public class ChangeHandler implements IExtensionChangeHandler{

    @Override
    public void addExtension(IExtensionTracker tracker, IExtension extension) {

        ......

        for (IConfigurationElement element:extension.getConfigurationElements())
        tracker.registerObject(
                extension, 
                new ServiceContributor(element), 
                IExtensionTracker.REF_STRONG);

        ......

    }

    @Override
    public void removeExtension(IExtension extension, Object[] objects) {

        ......

    }

}
2008 年 06 月 13 日 15:18 | ZhongHaoming | Comment 共0条留言
2008-06-12

《Professional ruby on rails》这本书里面,第一章就有个例子, 它有两个model,一个是recipe,一个是 ingredient,将 recipe和 ingredient做为父子关系,为了使得test case能通过,就需要改很多地方。 原料是菜谱的一部分,所以它在routes当中把搞成 recipe/1/ingredient/1 这样的URL。而自动生成的functional test里面,都是按照缺省的route来处理的。 所以我要在所有碰到 ingredient_path的地方改成 recipe_ingredient_path。 很多地方,包括test, controller,view 都要改,包括增加 :recipe_id=>1 这样的参数,以及把@recipe 这个参数添加到 path 或者 link_to 的参数当中。 改了很多地方,这个恐怕不能算是DRY吧。

2008 年 06 月 12 日 14:40 | SimonLei | Comment 共0条留言
2008-05-19

rails当中,如果要实现类似于组件的开发,需要使用 render partials 这种机制来实现。 现在,又多了一种方法: cells, http://cells.rubyforge.org/

cells 其实还是类似于 controller ,只不过它自带了view,而且可以继承,这一点比 controller 强。另外,如果当前cell的view找不到,还可以找父类的view,从而使得 在rails当中,拥有类似于组件的开发机制。不过现在还刚开始没多久,文挡不是很多。

2008 年 05 月 19 日 22:49 | SimonLei | Comment 共0条留言
2008-05-05

http://migrate4j.sourceforge.net/ 上一篇刚刚说到有模仿 acts_as_versioned 的东西出现,现在又来了一个模仿 db:migrate 的东西。虽然以前也有 liquibase http://www.liquibase.org/ 做 类似的事情,不过那个是用xml来定义的,我不是很喜欢。而这个migrate4j,则 纯粹是 java 的方式调用。有点类似于以前哪个老大说的,完全取代ant的一个build 工具,用纯java替代xml的方式,我个人比较喜欢这个。不仅不用去了解xml的schema, 而且还可以依赖编译检查来避免错误。前不久 ZhongHaoming 还提到,由于在xml当中不小心将 jelly 写成 jetty,导致调试了很长时间的事情。如果是使用java来调用的话,这种情况就可以 在很大程度上避免了。

2008 年 05 月 05 日 13:26 | SimonLei | Comment 共0条留言
2008-04-27

在rails里面用 acts_as_versioned 感觉很不错,这里还有 hibernate 版本的一个: http://www.jboss.org/envers/quickstart.html 说白了就是在 hibernate的事件当中来做这个事情,不错,有机会可以试一下。

Currently, the library has the following features:
  • versioning of basic properties (strings, integers, longs…)
  • versioning of embedded components, which are composed of basic properties
  • versioning of classes with simple, composite and embedded ids
  • versioning of one-to-one uni- and bi-directional relations (new!)
  • versioning of one-to-many bi-directional relations (new!)
2008 年 04 月 27 日 13:31 | SimonLei | Comment 共0条留言
 
Powered by Ruby_on_rails.Copyright © 2006, SimonLei