级别: 中级 Michael Galpin (mike.sr@gmail.com), 软件工程师, eBay
2008 年 4 月 22 日 Eclipse 最初是作为一个 Java™ 技术集成开发环境(integrated development environment,IDE)而名声大震的。Eclipse 的插件架构是其取得成功的重要原因之一。您可以使用许多流行的插件,而且很容易创建自己的插件。这两个优点使 Eclipse 非常适合于具有特殊架构的系统,比如 eBay。本文是包括两部分的系列中的第一部分,将讨论 eBay 对 Eclipse 的应用,我们将查看 eBay 的架构以及 eBay 如何对 Eclipse 进行定制以适合其架构。尽管本文主要讨论 eBay,您也可以使用这些知识根据自己的系统架构对 Eclipse 进行定制。
本文假设您基本了解 Eclipse。我们将讨论 eBay Development Environment (EDE),它基于 Eclipse V3.2,而且需要 JDK V5.0 或更高版本(请参见 参考资料 中的链接)。熟悉 Web 开发(包括 HTML、JavaScript 和 CSS)会很有用,但这不是必需的。
Eclipse 作为一个带有出色插件系统的 Java IDE 而广为人知。Eclipse 的 Eclipse V3.3 (Europa) 版本附带一些特殊的 Eclipse 发行包,包括针对 Java 开发人员的 Eclipse 和针对 Java EE 开发人员的 Eclipse。此外,您还可以使用 Eclipse C/C++ Development Toolkit (CDT) 和 Eclipse PHP Development Toolkit (PDT)。
所有这些发行包都是由基本的 Eclipse 平台和一些插件组合构成。这正是 Eclipse 的美妙之处。甚至集成到 Eclipse 中的所有常用的 Java 工具实际上都只是插件。如果您正在进行 PHP 开发,您无需因 Java 构建器而烦恼。Java、C++ 和 PHP 都是编程语言,因此,它们本身就有广泛的话题。然而,与其他使用同一种编程语言的其他系统相比,您的系统需求可能更加特殊。
您的 IDE 不也应该与您的系统一样特殊吗?这就是隐藏在 Eclipse 背后的思想,而且这也正是 eBay 创建基于 Eclipse 的 EDE 的指导原则。让我们查看一下 eBay 的架构,以理解其特殊的需求,以及 eBay 如何让 Eclipse 满足这些需求。
eBay 的架构
要描述 eBay 的架构和历史,需要编写一整本书。本文将专注于 eBay 的表示架构,它是如何发展的,以及 eBay 使用 Eclipse 平台所满足的特殊需求。我们将简略了解一下 eBay 架构的这段历史,以理解当前架构所应对的挑战。
历史
eBay 最初于 1995 年作为 AuctionWeb 创立。最初的站点是用 Perl 编写的。随着站点的发展,其后端改为用 C++ 编写,而前端用 XSL 编写。在 20 世纪 90 年代后期,使用 XSL 生成 HTML 是非常热门的技术。eBay 在 1998 年向大众公开,然后持续保持快速发展。
从 2001 年开始,流量带来的巨大压力使得 eBay 不得不用 Java 编程语言大规模重写其后端。前端架构没有更改。在 eBay 内部,Java+XSL 架构被称为 V3 架构,Perl 被称为 V1, C++/XSL 称为 V2。事实证明,V3 架构具有很强的可伸缩性,从而支持 eBay 发展到目前的规模,成为全世界访问量最大的站点之一。它不仅是一个高流量的站点,而且是一个高事务量的站点,全世界许多国家的数百万人都在 eBay 上标价、出价和购买商品。当然,任何解决方案都不是完美的,对于一个正在持续增长的系统来说更是如此,比如 eBay 的系统。
挑战
从后端的角度来看,V3 架构解决了 Web 应用程序可能出现的一些非常困难的问题。从纯数据的角度来看,eBay 可以被描述为具有大量的读、写操作,但对容错性和一致性的要求非常严格。毕竟,这里主要涉及的是钱财交易。没有太多的人会注意搜索索引和新闻聚合器中的缺陷,或者因此而烦恼,但是当涉及到他们在拍卖中支付的钱时,大多数人会立即注意到错误。类似地,如果无法预知接收电子邮件或即时消息所需的时间,可能还能够接受,但是人们希望立即知道他们的拍卖价钱比其他人高。V3 架构解决了这些非常困难的问题,但是,当您取得 eBay 这样的成功时还会遇到很多前端问题。
例如,当您有一个巨大且笨拙的站点时,像管理站点中所有图像这样看似简单的事情可能变得非常复杂。我们假设您想要进行一些清理并删除一张图像。那么如果图像正在使用中该怎么办呢?在基于 XSL 的系统中,您只能在数百万行 XSL (数百万行文本)中进行搜索。甚至这还不够,因为 XSL 能够轻易地将字符串连接起来,因此表示图像的最终字符串可能位于多个位置。不仅 XSL 是这样,所有基于文本/脚本的前端架构都存在这种现象。
图像还只是小问题。随着 eBay 的现代化,它越来越多地使用级联样式表(Cascading Style Sheet,CSS)。与将 CSS 内联到页面中供浏览器缓存相比,将 CSS 放入其自己的外部文件中会更加有效。如果可以跨页面重用这些 CSS 文件将更不错,因为可以从浏览器缓存获益。但是,让我们假设您需要更改一个页面上的样式。如果这会影响到其他页面怎么办呢?上面的情形也可能在 JavaScript 中出现。
您在 Web 站点上移动过页面吗?您需要确保更改了所有到这个页面的链接。现在想象一下对一个和 eBay 规模相当的站点执行该操作。又一次,您不得不对该链接的字符串表示的所有变体进行大量的文本搜索。
还记得门户网站开始流行的时候吗?在许多方面,它们是现代 mash-up 的前身 — 有点像本地 mash-up。典型的门户类型的页面包含其他页面的元素,而且这通常意味着使用的是 CSS 和 JavaScript。现在您可能遇到一种冲突:相同样式、名称相似的标题可能被用于多个位置,但是具有不同的值。类似地,相同的 JavaScript 函数或变量也可能在多个位置定义。即使不存在冲突,仍然会出现重复问题,完全相同的数据(CSS 或 JavaScript)可能在同一页面上出现 2 次、3 次或更多次。使用 eBay 的基于 XSL 的前端架构很难解决这类问题。您必须依靠文本搜索。
最后,可能还有一个最大的问题:本地化。像 eBay 这样的全球化 Web 站点被翻译为超过 30 种语言。甚至最基础的,一个基于 XSL 的系统也需要将每个 XSL 文件本地化。您可能认为,“使用像 Java 的 ResourceBundles 这样的属性文件不就行了吗?”请再想一想。如何解决在某些语言中与性别相关的文本?甚至是一些单复数变化,比如 “There is 1 bid” 与 “There are 2 bids”。如何处理必须本地化的图像呢?如果本地化中不同的地区需要不同的标记(比如一个句子中的链接位置)该怎么办?
所有这些因素使 eBay 不得不对每个 XSL 文件本地化。请记住,XSL 是代码,像更改一个页面上的措词这样简单的事情也需要向数千台服务器发布新的代码。
很少有 Web 站点遇到这些问题。不要惊讶,因为没有一个开源框架能够解决所有这些问题。eBay 需要构建一个非常智能、非常专业的工具。而且它正是这么做的。
V4
正如 eBay 使用 V3 架构解决了一些很复杂的后端可伸缩性问题,它使用一种新的前端架构(称为 V4)解决复杂的前端可伸缩性问题。V4 架构使用后端的强类型 Java 对象表示将要用于前端的所有内容。您的页面使用图像吗?那么就会有一个相对应的 Java 对象。如果想要删除该图像,就跟删除任何其他 Java 类一样简单或一样复杂。这种方式也可以用于链接、CSS 和 JavaScript。V4 不使用 servlet 或 JSP 创建 HTML。实际的 HTML DOM 使用 Java 语言来表示的,因此您可以在服务器上将 CSS 和 JavaScript 连接起来。
那么如何解决最困难的问题:内容?V4 的内容系统非常独特和强大。它使用自己的基于 XML 的语言创建内容契约。选择 契约 这个词是有意图的。契约定义创建一小块(单元)内容的任何变体所需的数据类型。例如,在英语中,一个特定单元可能需要一个整数和一个字符串(比如 “Raymond has 4 cars”),但是在另一种语言中需要两个整数和一个字符串。性别和基数也可能有多种变体。契约用 XML 定义所有内容。然后用特定应用程序/页面/组件支持的各种语言实现契约。当然,在 Java 中,契约必须表示为一个强类型的对象,需要按顺序提供所有动态数据(比如上面的 “Raymond” 和 “4”)以供使用。因为所有内容都是强类型的 Java 对象,所以所有内容都会在编译时强制执行。如果应用程序开发人员未在其 Java 代码中提供这 2 个参数,代码将不会编译。
所有内容的所有这些 Java 表示听起来很不错,然而,您可以想象,如果每次希望使用一幅图像或页面上的一些 JavaScript 时都必须编写一个新的 Java 类,这将是一个很大的负担。这正是 Eclipse 发挥作用的地方。
Eclipse 和 V4
现在我们了解了存在的问题和期望的解决方案,接下来看看 Eclipse 如何帮助 eBay 实现此解决方案。上面描述的许多代码 — 表示图像、链接、样式 和 JavaScript 的代码 — 都可被看作是样板代码,或者至少有一部分是样板代码。这是框架的一个常见问题。它们需要某种只有框架才需要的代码。一些框架使用代理对象或字节码操作实现此目的。这将导致难以调试和较差的性能。V4 框架使用一种不同的技术:代码生成工具。如果您需要一个 Java 对象表示 JavaScript,最好实际创建这个对象,但是如果不必为其编写代码就更好了。相反,Eclipse 插件可以代替您编写代码。
使用代码生成器
Eclipse 的 Java 和 Java EE 版本大量使用代码生成器。它们通常以向导的形式为您生成大量工件。例如,如果您使用的是 Java EE,那么您可以使用 New Dynamic Web Application 向导。这将为您的 Web 应用程序创建目录结构和 web.xml 文件,从而可以轻易地压缩为 WAR 文件并发布到 Web 容器。eBay 创建的代码生成器也具有类似用途:它们为您创建样板代码,因此您可以专注于应用程序代码,以及应用程序的 “业务逻辑”。
让我们看一下 eBay 创建的两个代码生成 Eclipse 插件,它们是 V4 框架工具的一部分。
JavaScript
JavaScript 是一种 Web 语言,在 V4 中发挥着重要作用。V4 框架允许通过普通方式进行 JavaScript 编码,但是要使用一些特殊的构造。它对 JavaScript 使用面向对象的本地语法,尽管并不是 JavaScript 必需的。它还使用来自开源项目 JsDoc 的代码注释语法,用于指定关于 JavaScript 类的额外的元数据。将这两种特性结合使用就能够为每个 JavaScript 类生成一个 JavaScript 引用(JsRef)类。这是一个可以在 V4 DOM 树内部的任何地方引用的 Java 类。让我们看一个示例。
清单 1. eBay JavaScript 类
vjo.needs("vjo.samples.classes.Person");
vjo.type("vjo.samples.classes.HelloPerson")
.props({
/**
* @return boolean
* @access public
*/
helloPerson:function(){
var person1 = new vjo.samples.classes.Person();
person1.setName("John");
alert("Hello " + person1.getName());
return false;
}
});
|
各种 vjo.* 构造用于模拟 Java 类文件声明中的构造。JsDoc 注释提供了 JavaScript 无法提供的强类型检验(strong typing)。这允许 Eclipse 插件为 JavaScript 类生成一个 JsRef,如清单 2 所示。
清单 2. 对应的 JsRef
package vjo.samples.classes;
import com.ebay.dsf.resource.pattern.js.JsType;
import com.ebay.dsf.spec.component.BaseComponentSpec;
import com.ebay.dsf.aggregator.jsref.internals.JsCmpMeta;
import com.ebay.dsf.spec.component.IComponentSpec;
import vjo.bootstrap.VjBootstrap;
import com.ebay.dsf.resource.pattern.js.JsResource;
import com.ebay.dsf.aggregator.jsref.JsObj;
import com.ebay.dsf.aggregator.jsref.JsFunc;
import com.ebay.dsf.jstojava.codegen.JsRefGenerator23;
import com.ebay.dsf.resource.pattern.js.IJsResourceRef;
import com.ebay.kernel.CodeGenerated;
/*
Generator: JsRefGenerator23 version: 2.3
Generated: Mon Jan 28 11:01:13 PST 2008
Source URL: file:/D:/Views/v4flash/v4samplecode/VjoSample/src/vjo/samples/
classes/HelloPerson.js
*/
/**
* <pre>vjo.needs("vjo.samples.classes.Person");
* vjo.type("vjo.samples.classes.HelloPerson")
* .props({
* //*
* * @return boolean
* * @access public
* //
* helloPerson:function(){
* var person1 = new vjo.samples.classes.Person();
* person1.setName("John");
* alert("Hello " + person1.getName());
* return false;
* }
* });
* </pre>
*/
@com.ebay.dsf.resource.utils.CodeGen(JsRefGenerator23.class)
@com.ebay.dsf.jstojava.codegen.JsRefGeneratorVersion(2.3)
public class HelloPersonJsr extends JsObj implements CodeGenerated{
private static final long serialVersionUID = 1L;
public static final JsResource RESOURCE = JsResource.viaName("HelloPerson");
public static class ResourceSpec extends BaseComponentSpec{
public static final IJsResourceRef REF = jsRef(RESOURCE, JsType.DefOnly);
private static volatile IComponentSpec s_instance;
public static IComponentSpec getInstance() {
if (s_instance != null) {
return s_instance;
}
synchronized (
ResourceSpec.class) {
if (s_instance == null) {
s_instance = new ResourceSpec();
}
}
return s_instance;
}
private
ResourceSpec() {
addJsRef(jsRef(RESOURCE, JsType.DefOnly));
addDependentComponent(VjBootstrap.ResourceSpec.getInstance());
addDependentComponent(vjo.samples.classes.PersonJsr.ResourceSpec.
getInstance());
}
}
protected HelloPersonJsr(JsCmpMeta cmpMeta, boolean isProto) {
super(cmpMeta, isProto);
}
/**
* @return boolean
@access public
* @jsfunc helloPerson
*
* function () {
* var person1 = new vjo.samples.classes.Person();
* person1.setName("John");
* alert("Hello " + person1.getName());
* return false;
* }
*/
public static final JsFunc<Boolean> helloPerson(){
JsFunc<Boolean> func = new JsFunc<Boolean> (getInstance(),
"helloPerson",true,true);
return func;
}
public static final JsCmpMeta META = new JsCmpMeta("vjo.samples.classes.HelloPerson",
"HelloPerson", ResourceSpec.getInstance());
private static volatile HelloPersonJsr s_instance;
private static HelloPersonJsr getInstance(){
addResourceSpec(ResourceSpec.getInstance());if (s_instance !=null){
return s_instance;
}
synchronized (HelloPersonJsr.class){
if (s_instance == null) {
s_instance = new HelloPersonJsr(META,false);}return s_instance;}
}
}
|
使用 Eclipse 工具很容易为开发人员生成这些代码。您只需右键单击一个 JavaScript 文件并生成所需的 Java 代码,如下图所示。
图 1. 使用 Eclipse 生成 JsRef
然后,可以在 Java 代码中通过编程的方式使用 JsRef。这使得很容易在 Java 代码中将 JavaScript 连接起来。例如,当用户单击一个按钮时,您希望调用上面的 JavaScript 验证一个表单。
清单 3. 在服务器上连接客户端事件
DButton button = new DButton("Hello Person - click me");
button.add(EventType.CLICK, HelloPersonJsr.helloPerson());
|
JsDoc 注释还简化了对依赖关系的跟踪,因为它使用了一个 Java 导入结构。您无需包含您的 JavaScript 所依赖的其他 JavaScript 库。只需在代码中申明依赖关系。如果更改 JavaScript — 比如,向函数添加一个新参数 — 将会导致使用该函数的代码出现编译错误,除非您重构该代码。包语法也为 JavaScript 提供了一个清晰的名称空间,用于防止冲突。使用 Java 类表示 JavaScript 就会获得所有这些好处,但是借助 Eclipse 插件的帮助,只需通过很少量的工作就可以获得这些优势。
JavaScript 只是 Web 应用程序的其中一部分,eBay 的 V4 框架和 Eclipse 插件通过独特的方式处理这个 Web 应用程序,而 Eclipse 插件使开发人员能够使用 V4。另一个重要的例子是 CSS。
CSS
V4 应用于 JavaScript 的方法是,保留本地 JavaScript 并使用 Java 代码执行代理。应用于 CSS 的方法稍微有点不同。JavaScript 是一种复杂的编程语言。CSS 可以被编译,但是肯定与 JavaScript 不同。对于 CSS,CSS 文件只能充当一个起点。它们被用于创建 Java 类,在运行时使用这个 Java 类生成 CSS。原始的 CSS 文件可以被遗弃。Java 类变成了源代码控制的一部分,但是,实际上签入的是 JavaSCript 文件,而 JsRef 是由开发人员系统上的 Eclipse 或用于将代码转出到生产中的构建系统生成的。尽管如此,从 CSS 开始入手通常更自然一些,也许应该从某种可视的模型入手。
因此,eBay 构建了用于处理 CSS 的 Eclipse 插件并 “引导” Java-CSS (JCSS) 的创建。在清单 4 中,我们将看到一个典型的 CSS 文件。
清单 4. CSS 文件
* {
margin: 0;
padding: 0;
}
body {
font-size: 100.01%;
font-family: Verdana,sans-serif;
padding: 10px;
text-align: center;
}
h1, h2, h3, h4, h5 {
font-family: Georgia,serif;
}
h1 {
color: #9A351D;
background-color: transparent;
margin: 1em 0;
}
h3 {
font-size: .9em;
}
div h3 {
color: #58B;
text-transform: uppercase;
font-size: .7em;
}
div h3:before {
content: "by";
font-style: italic;
text-transform: none;
color: #000;
display: block;
margin-bottom: 1em;
}
h4 {
text-align: center;
font-style: italic;
color: #666;
margin: 2em 0 0 0;
font-size: .7em;
font-weight: normal;
}
.footer {
color: #999;
font-size: .5em;
font-family: Verdana,sans-serif;
font-weight: normal;
}
|
再一次,我们可以在 Eclipse 中右键单击,访问 JCSS 插件并创建 JCSS 类。
图 2. 使用 JCSS 插件
这将生成一个 Java 类。
清单 5. 对应的 JCSS 类
package com.ebay.css.example;
import com.ebay.dsf.css.CssClassConstant;
import com.ebay.dsf.javatocss.JCssDef;
import com.ebay.dsf.javatocss.JCssStyleRule;
import com.ebay.dsf.javatocss.prop.BackgroundColor;
import com.ebay.dsf.javatocss.prop.Display;
import com.ebay.dsf.javatocss.prop.FontStyle;
import com.ebay.dsf.javatocss.prop.FontWeight;
import com.ebay.dsf.javatocss.prop.TextAlign;
import com.ebay.dsf.javatocss.prop.TextTransform;
import com.ebay.kernel.CodeGenerated;
/*
Generator: class com.ebay.dsf.csscodegen.generator.def.JCssDefGenerator
version: 1.0
Generated: Mon Jan 28 17:37:36 PST 2008
Source: com.ebay.css.example cover.css
*/
/**
* <pre>
* {margin:0; padding:0}
body {font-size:100.01%; font-family:Verdana, sans-serif; padding:10px;
text-align:center}
h1, h2, h3, h4, h5 {font-family:Georgia, serif}
h1 {color:#9a351d; background-color:transparent; margin:1em 0}
h3 {font-size:0.9em}
div h3 {color:#58b; text-transform:uppercase; font-size:0.7em}
div h3:before {content:"by"; font-style:italic; text-transform:none;
color:#000; display:block; margin-bottom:1em}
h4 {text-align:center; font-style:italic; color:#666; margin:2em 0 0 0;
font-size:0.7em; font-weight:normal}
.footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public class CoverJCssDef extends JCssDef implements CodeGenerated {
private static final String SCOPE_NAME = "";
public interface Clz {
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
|-------10--------20--------30--------40--------50--------60--------70--------80--------9|
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
* </pre>
*/
CssClassConstant footer_ = new CssClassConstant(SCOPE_NAME, "footer");
}
private static CoverJCssDef s_instance= new CoverJCssDef();
public static CoverJCssDef getInstance(){
return s_instance;
}
public static interface DEFAULT {
public static final JCssStyleRule STYLE_RULE_1 = s_instance.rule()
.select(any)
.set( properties()
.margin("0")
.padding("0"));
public static final JCssStyleRule STYLE_RULE_2 = s_instance.rule()
.select(body)
.set( properties()
.fontSize("100.01%")
.fontFamily("Verdana, sans-serif")
.padding("10px")
.textAlign(TextAlign.CENTER));
public static final JCssStyleRule STYLE_RULE_3 = s_instance.rule()
.select(h1)
.select(h2)
.select(h3)
.select(h4)
.select(h5)
.set( properties()
.fontFamily("Georgia, serif"));
public static final JCssStyleRule STYLE_RULE_4 = s_instance.rule()
.select(h1)
.set( properties()
.color("#9a351d")
.backgroundColor(BackgroundColor.TRANSPARENT)
.margin("1em 0"));
public static final JCssStyleRule STYLE_RULE_5 = s_instance.rule()
.select(h3)
.set( properties()
.fontSize("0.9em"));
public static final JCssStyleRule STYLE_RULE_6 = s_instance.rule()
.select(div.desc(h3))
.set( properties()
.color("#58b")
.textTransform(TextTransform.UPPERCASE)
.fontSize("0.7em"));
public static final JCssStyleRule STYLE_RULE_7 = s_instance.rule()
.select(div.desc(h3.pseudoBefore()))
.set( properties()
.content("\"by\"")
.fontStyle(FontStyle.ITALIC)
.textTransform(TextTransform.NONE)
.color("#000")
.display(Display.BLOCK)
.marginBottom("1em"));
public static final JCssStyleRule STYLE_RULE_8 = s_instance.rule()
.select(h4)
.set( properties()
.textAlign(TextAlign.CENTER)
.fontStyle(FontStyle.ITALIC)
.color("#666")
.margin("2em 0 0 0")
.fontSize("0.7em")
.fontWeight(FontWeight.NORMAL));
public static final JCssStyleRule STYLE_RULE_9 = s_instance.rule()
.select(any.with(Clz.footer_))
.set( properties()
.color("#999")
.fontSize("0.5em")
.fontFamily("Verdana, sans-serif")
.fontWeight(FontWeight.NORMAL));
}
}
|
此文件表示我们的样式定义。为了实现关注点分离(separation of concerns),使用一个单独的类在 HTML 文档中实现 CSS。当然,这个类也是由 Eclipse 插件生成的。
图 3. 使用插件生成 CSS realizer
下面展示了一个插件生成的 CSS realizer 类示例。
清单 6. 生成的 CSS realizer
package com.ebay.css.example;
import com.ebay.css.example.CoverJCssDef;
import com.ebay.dsf.css.CssClassConstant;
import com.ebay.dsf.csscodegen.generator.realizer.CssRealizerGenerator;
import com.ebay.dsf.csscommon.BaseCssRealizer;
import com.ebay.dsf.html.dom.BaseCoreHtmlElement;
import com.ebay.dsf.resource.pattern.css.CssResource;
import com.ebay.kernel.CodeGenerated;
/*
Generator: class com.ebay.dsf.csscodegen.generator.realizer.CssRealizerGenerator
version: 2.0
Generated: Tue Jan 29 15:20:08 PST 2008
Source: com.ebay.css.example.CoverJCssDef
*/
/**
* <pre>
* {margin:0; padding:0}
body {font-size:100.01%; font-family:Verdana, sans-serif; padding:10px;
text-align:center}
h1, h2, h3, h4, h5 {font-family:Georgia, serif}
h1 {color:#9a351d; background-color:transparent; margin:1em 0}
h3 {font-size:0.9em}
div h3 {color:#58b; text-transform:uppercase; font-size:0.7em}
div h3:before {content:"by"; font-style:italic; text-transform:none;
color:#000; display:block; margin-bottom:1em}
h4 {text-align:center; font-style:italic; color:#666; margin:2em 0 0 0;
font-size:0.7em; font-weight:normal}
.footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
@com.ebay.dsf.resource.utils.CodeGen(CssRealizerGenerator.class)
public class CoverCssr extends BaseCssRealizer implements CodeGenerated {
/** Answers the actual text this realizer was genned from */
public static final String gentext =
"* {margin:0; padding:0}\n"
+ "body {font-size:100.01%; font-family:Verdana, sans-serif; padding:10px;
text-align:center}\n"
+ "h1, h2, h3, h4, h5 {font-family:Georgia, serif}\n"
+ "h1 {color:#9a351d; background-color:transparent; margin:1em 0}\n"
+ "h3 {font-size:0.9em}\n"
+ "div h3 {color:#58b; text-transform:uppercase; font-size:0.7em}\n"
+ "div h3:before {content:\"by\"; font-style:italic; text-transform:none;
color:#000; display:block; margin-bottom:1em}\n"
+ "h4 {text-align:center; font-style:italic; color:#666; margin:2em 0 0 0;
font-size:0.7em; font-weight:normal}\n"
+ ".footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}\n"
;
public static final CssResource src = CssResource.viaDef(
CoverJCssDef.getInstance());
public static final Classes classes = new Classes() ;
public static final Utils utils = new Utils() ;
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public static final class Classes {
private Classes() { /* prevent instantiation */ }
private static final CssClassConstant s_footer = new CssClassConstant("footer");
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public final CssClassConstant footer = s_footer;
}
public static final class Utils {
private Utils() { /* prevent instantiation */ }
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public Utils addClass_footer(final BaseCoreHtmlElement element) {
addCssClass(element, Classes.s_footer);
return this;
}
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public Utils setClass_footer(final BaseCoreHtmlElement element) {
setCssClass(element, Classes.s_footer);
return this;
}
}
}
|
realizer 使得很容易将样式附加到任何可视元素上。另外,您可以在一个页面上注册一个或多个 JCSS 定义,用于定义默认的样式。这些技术的一个示例如下所示。
清单 7. 使用 JCSS
// Create a div, attach a style to it
DDiv footer = new DDiv();
CoverCssr.utils.addClass_footer(footer);
// create DOM, register the CSS for it
HtmlDisplayer2 doc = new HtmlDisplayer2(footer);
doc.registerCss(CoverJCssDef.getInstance());
|
我们在这里有一次享受到了拥有名称空间(JCSS 包)的好处,所以在运行时,我们的 CSS 将不会与任何其他 CSS 发生冲突。我们不但获得了此好处,还能够利用 “本地” CSS。关键在于使用 Eclipse 将 CSS 和 JCSS 连接了起来。
现在,您了解了这个模式。V4 需要所有这些强类型的构造,而且 Eclipse 使得提供和处理这些构造变得非常简单。我们曾提到 V4 中最复杂的资源类型:内容。让我看看 Eclipse 如何在这方面提供帮助。
内容
V4 内容系统依赖于内容契约。它们拥有自己的 XML 语言,但是,使用 Eclipse 插件能够为这些契约提供更直观的编辑器。
图 4. V4 内容结构化的编辑器
我们需要生成 Java 代码表示内容契约,同样,我们可以使用 Eclipse 来生成。
图 5. 生成内容契约的 Java 代码
内容契约具有强大的语法,可以用内容类型描述 eBay 应用程序中的复杂结构。这会生成一个容易使用的表示性 API,但是手工编写将会很单调乏味。Eclipse 插件能够解决这个问题。
正如您所见到的,eBay 利用 Eclipse 插件在大量场景中为开发人员生成代码。这些插件并不都是 eBay 编写的。让我们看一下 eBay 使用的其他插件,以及它们能解决什么样的需求。
其他 V4 插件
V4 系统还提供了一种运行和调试应用程序的独特方式,因为它使用 Java 表示 Web 页面上的所有元素。这可以极大地提高您的生产力,简化了应用程序的开发和调试。
从 Eclipse 运行 web 应用程序
您是否曾经希望能够运行一个 Web 页面,而无需将其部署到 Web 服务器上并启动或重启 Web 服务器?借助 eBay 的 V4 Eclipse 插件,完全可以实现这个愿望。
图 6. 将 Web 应用程序当作 “dervlet” 运行
您也许会问,“什么是 dervlet?”顾名思义,它与 servlet 很相似。V4 框架允许将它们用作引导类,用于快速原型化和测试 Web 应用程序。这类似于将一个具有主方法的类用于测试包中的其他类。您需要做的只是右键单击并作为 dervlet 运行。显然,您也可以作为 dervlet 进行调试,以及像其他可执行文件一样逐步调试您的代码。在后台,Eclipse 插件启动了一个嵌入式版本的 Jetty servlet 容器,然后使用 Web 浏览器打开 Jetty 提供的 URL。
该 Eclipse 插件使得很容易在开发过程中对 V4 应用程序进行调试。V4 框架和 Eclipse 还能够进一步简化对生产环境中的应用程序的调试。
从 Eclipse 调试生产应用程序
V4 框架是一个组件框架。当您将所有内容都用作 Java 类时,那么面向对象形式的下一步就是将相关的类封装为一个组件。这提供了高度可重用的组件,而且可以进一步提升开发人员的生产力。如果你看一下 eBay Web 页面,那么您看到的就是一些 V4 组件的集合。 这不但适合快速创建应用程序,而且有助于对运行中的应用程序进行内省。
图 7. 具有 Spyglass 的 eBay 页面
注意到所有的红色框了吗?它们都是 V4 组件。将鼠标悬停在任何框上,就会显示组件的完全限定名称。如果页面上存在外观缺陷,很容易发现是哪段代码(组件)导致的问题。V4 可以帮助您立即获得这段代码。注意到蓝色框中的链接了吗?eBay 开发人员可以从任何 eBay V4 生产页面直接转到 Eclipse。可以尝试使用 XSL、JSP 等执行类似的操作。这是通过从 Eclipse 运行一个侦听器服务器来实现的。侦听器是 eBay 编写的另一个插件,利用 eBay 架构帮助开发人员使用 Eclipse。
结束语
我们讨论了 eBay 面对的独特挑战。我们看到了 eBay 如何使用一种严格的架构来解决这些问题。这类架构只能与强大的工具结合使用,从而使开发人员避免编写单调乏味的样板代码。Eclipse 插件系统非常完美,它提供了这些工具,以及其他许多用于开发和调试的强大工具。本文描述的问题和解决方案在某些方面也许是 eBay 独有的,但是使用的技术却不是。如果您的组织需要高度专门化的架构,您可以借鉴 eBay 的页面,并创建 Eclipse 插件帮助解决这些问题,而且不会影响开发人员的生产力。
参考资料 学习
获得产品和技术
讨论
关于作者  | 
|  | Michael Galpin 从 1998 年就开始进行专业的软件开发。他拥有加州理工学院的数学学位,现在是位于 San Jose, CA 的 eBay 的工程师。 |
对本文的评价
|