Posts Tagged ‘ Restlet ’


GWT学习研究

1. GWT与Restlet的结合

目前进行的项目中,使用App Engine作为底层以及数据库的平台,Restlet是作为HTTP调用的库,然后GWT来前台界面的实现。基本上整个架构是严格按照Google的思路来进行的。最近把重心从底层慢慢走到了前台,于是对GWT有了些研究。把一些经验和总结列举出来。

首先,可以看到Restlet在最新的2.0m6和Snapshot之中,有五种版本,每一种版本的JAR的结构和路径都不一样。我们的项目在后台是使用的J2EE的版本,而在前台需要使用的是for GWT版本,于是这里就产生了第一个问题,在同一个项目中均需要使用这两个版本的冲突。在两个JAR包下都有org.restlet.jar存在。于是一个很简单的方法,就是改变其中一个JAR包的名字,来避免冲突。这里一定要注意的是,之后在引用JAR包时,一定要看清楚你的类是属于前台还是后台的,如果是前台的,一般包名是以com.google.gwt.core.client开头的。

2. – GWT的项目结构

在一个创建好的GWT项目中,一般会有以下三个重要的部分,第一个是项目名.gwt.xml,这个文件是整个项目的核心文件,里面包括了模块的引用,入口点的定义以及其他的一些配置方面的设置。接下来两部分分别存在两个包中,client包和server包。其中,client包就是为了做前台界面而存在的包,而server包可以认为是后台的,当然你可以使用你已经做好的而不是强制性非要放在里面。client包下的项目名同名java文件,即是在配置文件中定义的入口点,这个文件定义了界面UI的结构,以及各种事件的触发,就等于是一个以java而写的界面。GWT在编译过程中,是将其转化为javascript而给浏览器运行的。

现在有这样一个问题存在,如何实现页面的跳转。我们知道对于AJAX实现的Web应用,习惯于只用单页面来实现不同的内容,也就是利用同样的定位符如div等,来不断改变其中的内容。那么在GWT中,有没有比较好的办法做到这一点呢。我采用的方法,是使用一个单例模式下的固定div来实现。我们可以将不同的页面均视作为Content的不同实现,然后只需要替换页面下的Content即可。具体办法为:

1)创建一个Content的抽象类继承Composite类,这个类将作为所有通用的内容类,在主页面中替换使用。

public abstract class Content extends Composite {

}

2)在项目名java文件中,利用单例模式定一个该入口点的实例,同时创建一个Content实例,这个实例用来承载不同的实现内容类。同时加上一个setContent(Content content)方法,用来设定当前的Content。

public class XXX implements EntryPoint {

private static final XXX INSTANCE = new XXX();

private Content content;

private XXX(){}

public static XXX getInstance(){

return INSTANCE;

}

@Override

public void onModuleLoad() {

XXX.getInstance().setContent(new LoginContent());

}

public void setContent(Content content){

this.content = content;

RootPanel.get(”content”).clear();

RootPanel.get(”content”).add(content);

}

}

3)对于每一个不同的实现页面,分别创建一个类继承Content类,在这个类的构造函数中定义其界面和事件的触发函数。

public class LoginContent extends Content {

private Label usrLabel = new Label(”Username”);

……

public LoginContent(){

usrPanel.add(usrLabel);

usrPanel.add(usrTextBox);

……

initWidget(verPanel);

}

4)在今后的每次需要更换页面的时候,只需要获得该项目下的Content实例,进行替换即可。

3. – GWT下的UiBinder的实现

用上面的方法来实现界面存在的一个问题是,经常需要在类文件中写大量的界面元素的定义,如同Swing一样。一来对于Java程序员不太方便,二来对于界面程序员更为苦恼。于是在最新的GWT的版本中,Google推出了UiBinder,这是一个采用xml来作为界面管理器的实现方法,将界面的元素与功能实现了脱离,一方面简化了代码,另一方面也给前台和后台的程序员提供了便利。

在uiBinder下,我们如果需要创建一个新的界面,不再直接使用类来实现,而是创建一个UiBinder对象,在Eclipse下,new一个UiBinder会生成两个文件,一个是扩展了Composite的类,另一个是同名的.ui.xml文件。在.ui.xml中,xmlns:g=”urn:import:com.google.gwt.user.client.ui”的空间下定义的是所有的Widget,于是可以就像使用Java一样,列出这些需要的Widget了。在<ui:style>中,还可以定义所需要使用的样式,当然也可以单独在CSS中定义。以下是一个简单的例子:

<!DOCTYPE ui:UiBinder SYSTEM “http://dl.google.com/gwt/DTD/xhtml.ent”>

<ui:UiBinder xmlns:ui=”urn:ui:com.google.gwt.uibinder”

xmlns:g=”urn:import:com.google.gwt.user.client.ui”

xmlns:c=”urn:import:com.featheast.client”>

<ui:style>

.important {

font-weight: bold;

}

.bolder {

font-weight: bold;

}

</ui:style>

<g:HTMLPanel>

Hello,

<g:Button styleName=”{style.important}” ui:field=”button” />

<g:Label styleName=”{style.bolder}” text=”This is my label in bold!”></g:Label>

<g:VerticalPanel ui:field=”myPanelContent” spacing=”5″></g:VerticalPanel>

</g:HTMLPanel>

</ui:UiBinder>

对于每一个在.ui.xml中的元素,我们都有一个在类文件中的对应,比如这里,我们有一个Button,一个Label,这些可以操作的值在类中分别有

@UiField

Button button;

@UiField

Label label;

的对应。这样的结果就是我们能更好的操作这些元素。在该类的构造函数中,可以定义这些元素的事件函数和其他的操作。这样将界面和执行分隔开,效果的确比较好,思路也更加清晰。以下是一个对应的示例:

public class MyUIBinder extends Composite {

private static MyUIBinderUiBinder uiBinder = GWT.create(MyUIBinderUiBinder.class);

interface MyUIBinderUiBinder extends UiBinder<Widget, MyUIBinder> {}

@UiField

Button button;

@UiField

VerticalPanel myPanelContent;

public MyUIBinder(String firstName) {

initWidget(uiBinder.createAndBindUi(this));

button.setText(firstName);

HTML html1 = new HTML();

html1.setHTML(”<a href=’http://www.google.com’>Click me!</a>”);

myPanelContent.add(html1);

HTML html2 = new HTML();

html2.setHTML(”This is my sample <b>content</b>!”);

myPanelContent.add(html2);

}

@UiHandler(”button”)

void onClick(ClickEvent e) {

RootPanel.get().add(new FavouriteColorWidget());

}

}

运用同样的单例模式,我们可以更改同一个界面的内容,在此就不重复了。

Restlet2.0初尝试

今天一直在研究Restlet的API,大约应该是在2007年起,1.0版就已经问世了。但是由于这个概念在当时还是比较新的,所以1.0版本的普及率特别是在国内的普及率还是很低的。百度一下就会发现研究的人的确不多。现在RESTful的概念是越吵越热,且不说它到底好坏与否,但是引起这么多人的关注,就说明了它有其出色的地方。

在网站上看到的Stable的版本是1.6,于是保守地先从这个版本试起,结果发现无论如何添加Connector,都会报找不到Protocol的错,恰好这时James过来看看我,于是在他的建议之下,尝试了2.0m5版本。至少在最简单的程序部分,能够很正常的运行。

下面把一些很简单的步骤作个罗列:

在我看来,完成一个全部由Restlet构造的程序,一共需要四个部分。首先便是资源文件,对于REST概念比较熟悉的同学都应该明白,Resource在REST中是根本中的根本。于是,我们首先定义一个十分简单的Resource,为了简单起见,我们只给它附加两个功能,GET和POST。其中GET方法,很简单的返回一个String,POST方法是将得到的两个数进行相加返回结果字符串。

public class TestResource extends ServerResource{

	@Get
	public String getResultGet(){
		return "This is my test!";
	}

	@Post
	public String getResultPost(Representation entity){
		Form form = new Form(entity);
		String first = form.getFirstValue("first");
		String second = form.getFirstValue("second");
		int a = Integer.parseInt(first);
		int b = Integer.parseInt(second);

		return "The result of "+a+"+"+b+"is "+(a+b);
	}
}

在这一步结束之后,我们需要把资源放到一给应用(Application)中去,同时要提供访问它的路径。我个人心目中就把这个部分成为RouterPath,因为这两个单词也是这个文件中最多见的。

public class RouterPath extends Application{
        @Override
	public Restlet createInboundRoot() {
		// TODO Auto-generated method stub
		Router router = new Router(getContext());

		router.attach("/greeting",TestResource.class);

		return router;
	}
}

在这一给类中,它继承来Application这个类,目的就可以看成是提供了一个程序环境,在这个大的环境之下,你输入的方法,就将被引导到某个类之中去。比如在这里,所有的以greeting结尾的请求,都会被引导到我们刚才所创建的类里面去。

有来这样两样以后,我们只需要启动这个资源让它开始服务就可以了。我把这个类命名为StartResource类。

public class StartResource {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			// Create a new Component.
			Component component = new Component();  

			// Add a new HTTP server listening on port 8182.
			component.getServers().add(Protocol.HTTP, 8182);  

			// Attach the sample application.
			component.getDefaultHost().attach(new RouterPath());  

			// Start the component.
			component.start();
		}catch (Exception e) {
			// Something is wrong.
			e.printStackTrace();
		}
	}
}

我们可以很清楚的看到,这个类启动来在端口8182上的HTTP协议,然后将前面的Application附加在来这个端口上。然后服务就开始启动,等待他人的使用了。

现在我们就可以直接在浏览器中输入这个URL,来调用GET方法了。在这里,我们输入http://localhost:8182/greeting,屏幕上就出现来This is my test!字样。当然浏览器是不支持POST,PUT和DELETE直接在地址栏中的,我们可以用一个简单的JSP界面做一给POST框体,也可以像如下的类用Restlet提供的Client来完成请求。

public class Client {
	public static void main(String[] args) {
		int a=4;
		int b=9;
		ClientResource client = new ClientResource("http://localhost:8182/greeting");
		try {
			System.out.println(client.get().getText());
			Form form = new Form();
			form.add("first", a+"");
			form.add("second",b+"");
			System.out.println(client.post(form.getWebRepresentation()).getText());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

很容易看到,GET方法是可以直接用client得到的,而POST方法需要传入一个参数,也就是Resource端所需要的参数。用类似的办法,我们同样可以完成PUT和DELETE。

以上的这个例子实际上非常基础,但是至少展现来Restlet的一些特点,首先它是一个很完善的既支持客户端又支持服务端的API;其次它的结构也十分清晰,只要很好的划分了不同的请求,就能很好地看清楚之间的逻辑;最后就是API的封装还是很简洁的,尤其是在客户端调用这一块。

明天开始继续深入研究,更多的Tutorial可以参见http://www.restlet.org/documentation/2.0/tutorial

  • English Version

    • Cannot read Chinese? Please take a look at my English site, hope you can find more you need there!
  • 感谢支持

  • twitter

    facebook

    linkedin

  • Categories