??xml version="1.0" encoding="utf-8" standalone="yes"?>
大部分代码也是参考文中的?
在你的EntryPoint中装配好你的indicator
q样调用?
]]>
When a GWT application loads, nothing is actually displayed by your application until all the generated JavaScript has been downloaded by the browser. I was looking for way to display a loading screen while my GWT application was loading, and then remove it once the GWT application is loaded.
Since every GWT application has to be embedded in an HTML Host Page, an easy way to display a loading message is to place the loading message in a <div> in the HTML Host Page. Once all the GWT application JavaScript is done loading, we can have the GWT application remove the loading message by doing some DOM manipulation on the HTML Host Page.
Here is a sample HTML Host Page. The loading message, along with a loading animation image is contained in a <div> named “Loading-Message”.
1: <html>
2:
3: <head>
4: <title>GWT Application</title>
5: <link rel="stylesheet" href="style.css">
6: </head>
7:
8: <body>
9:
10: <script type="text/javascript" language="javascript" src="gwtapp.nocache.js"></script>
11:
12: <h2>GWT Application</h2>
13:
14: <!-- The loading message div -->
15: <div id="Loading-Message">
16: <img src="loading-animation.gif" alt="loading"> Loading GWT Application, please wait...
17: </div>
18:
19: <!-- The Application's UI elements will be placed in this div by the Application module's -->
20: <!-- entry point class when it is loaded -->
21: <div id="GWT-Application-Panel">
22: </div>
23:
24: </body>
25:
26: </html>
The “Loading-Message” <div> can be removed from the HTML Host Page using the following line of Java Code:
DOM.setInnerHTML(RootPanel.get("Loading-Message").getElement(), "");
Where would you put this line of code? You can put it anywhere in your GWT application. However, a good place to put it would be in your GWT application EntryPoint class’s onModuleLoad method. You can place it either before or after your application loads the UI elements. Here is an example onModuleLoad method:
1: public void onModuleLoad() {
2: // Remove the loading message
3: DOM.setInnerHTML(RootPanel.get(“Loading-Message”).getElement(), “”);
4:
5: // Get the Application Container div from the DOM
6: mainPanel = RootPanel.get(“GWT-Application_Panel”);
7:
8: // Add GWT UI components
9: addWidgetsTo(mainPanel);
10: }
If you aren’t the artistic type, here is a website you can use to download a customized animation image. Here is another website, where you can select some existing animations to use in your app.
If you haven’t already, please check out our continuing series of GWT Tutorials beginning with Introduction to GWT. You can find the rest of our GWT tutorials here.
忠告之一: 分而治?
众所周知QGWT应用是Java应用。不q,问题在于?#8220;哪种Java”Q我们需要牢? GWT~译的是与J2SE 1.4.2或者更早版本兼容的Java源代码。另外,只有J2SE 1.4.2 API的子集得到支持,即java.lang和java.utilE序包。即便在使用q些E序包时Q也要非常认真地研究Google在运行库支持斚w的注 释,q且牢记相应的忠? 如果保从一开始就只用客L代码中的可{换类Q那么就可以避免许多问题。ؓ了及早发现问题,只要在宿L式(hosted modeQ下q行Q就要对照JRE仿真库检验代码。因而,W一ơ运行应用时Q就会发现大部分不支持的库。所以,要及早ƈ且经常运行?
现在Q笔者给出的忠告是“分而治?#8221;Q具体意思就是一开始就把应用代码分成三个不同的部分: 客户端代码、RPC相关代码和服务器端代码,然后构徏相应的Eclipse目Q从而完成Q务。这样一来,可以利用不同的Java语言版本Q用于客L 和服务器部分。笔者用Java 5构徏了应用的服务器部分(服务器小E序代码Q? 但如果用Mustang版本Q那么在本文的代码片D中Q由于篇q有限,本文所涉及的程序代码可通过以下链接查询: http://blog.ccw.com.cn/article-htm-itemid-17924-type-blog.htmlQ,可以用Java 6取代Java 5。即便在服务器端仍然使用J2SE 1.4.2Q这U分L也可以在来提供更大的灵zL,明确分离代码Q?#8220;分离问题”Q,而不会在GWT宿主模式下限制调试操作。如果所有部分都在一? Eclipse目中,则需要非怸谨,特别是在服务器端? 不然Q就会出现编译或者运行问题?
需要用特D的命名U定Q这样可以清楚确认不同项目,q且化部|脚本。可以用譬如名为GWT-< ModuleName>的Eclipse工作集来包括所有三个项目。这里,“ModuleName”是识别Web应用的GWT模块的名U?
?客户端代? 包含与用L面相关的代码Q可以{换成JavaScript。因此,局限于J2SE 1.4.2和GWTq行时支持。启用每个项目的Eclipse Java~译器设|和“Java~译器错?警告”Q把Java依从U别调整?.4、把源代码和cL件兼Ҏ调整到1.4Q假设不是?.4之前? JDK版本Q。该目的名U是< ModuleName>-clientQ譬?#8220;JUnit2MR-client”Q它依赖于构\径设|中?lt; ModuleName>-rpc目。程序包名称cM< com.company.project>.gwt.< moduleName>.client?
?RPC相关代码: 包含RPC相关的代码,可以转换成JavaScript。该目遵从与上q客L代码目同样的指导准则。项目名?lt; ModuleName>-rpcQ譬?#8220;JUnit2MR-rpc”Q它q不依赖于其他Q何项目。程序包名称?lt; ModuleName>-client目的程序包名称一栗RPC目包含客户端上的远E接口、RPC期间由GWTq行序列化的数据传输对象Q以 及全局帔RcR?
?服务器端代码: 含有服务器小E序代码Q如果服务器端由Java服务器小E序l成的话。如果用Tomcat 5.5或者Tomcat 6Q可以充分利用Java 5+的全部功能。启用每个项目的Eclipse~译器设|,然后使用Java 5~译器设|,依从U别讄?.0。如果用Eclipse 3.2.2Q那么其新的“源代?#8594;清理”Ҏ也值得配置。该目名称?lt; ModuleName>-serverQ譬?#8220;JUnit2MR-server”Q它依赖于构\径设|中?lt; ModuleName>-rpc目。如果按照GWT的默认程序包提案q行~程Q程序包名称?lt; com.company.project>.gwt.< moduleName>.server?
忠告之二: 调试和错误报告不仅仅只有Window.alert ()
在创建GWT应用Ӟ其实可以使用IDE的全部调试功能。但在深入分析何处可能出现错误之前,需要代码的客户端和 服务器端都有可靠的异常报告机制。用try/catch代码块通常可以做到q一炏V在客户端的catch代码块中Q应当注意这一现实: 默认的方法调用e.printStackTrace()q不是在所有情况下都适合的解军_法。它适用于应用运行在GWT宿主模式下,把文本输出到 Eclipse控制台。不q在Web模式下,要问问自? “我发送到stdout或者stderr的堆栈跟t信息和错误信息会在什么地ҎC?”一U可能的解决Ҏ是使用Mat Gessel的调试实用程序类Qhttp://www.asquare.net/gwttkQ,但是需要浏览器JavaScript控制台来查看Web? 式下的结果?
在客LQ徏议要做的一件事是Q用GWT.setUncaughtExceptionHandler()? 法,ZQ何未被发现的异常提供自己的异常处理程序。发Cq几U异常后Q有几个选择: GWT.logQmessage, caughtQ、Debug.println (message_with_stacktrace); 如果使用Mat Gessel的Debugc,可选择Window.alertQmessage_with_stacktraceQ,或者自己定制的错误报告?
视来源而定Q会得到“无法装入模块”或?#8220;未被发现的异常被漏过”的信息。笔者编写了一个小的DebugUtilityc,它提供了易于定制的默认客L错误处理机制Q见代码片段1Q?
在服务器端,可以使用java.util.logging API或者log4j的广泛功能,具体取决于个人偏好或者项目的U束条g。但要是没有为GWT? com.google.gwt.user.server.rpc.RemoteServiceServletcL补丁Q对于未被发现、未被检查的异常Q只 会在堆栈跟踪里面得到提示Q指向生成该错误的服务器端类。对于catch()代码块里面发现及报告的被查的异常Q一切都正常?
忠告之三: 当心GWT Shell?#8220;h”按钮陷阱
在宿L式下启动应用Ӟ会在览器Q务栏上看?#8220;h”按钮。要是摁了这个按钮,GWT׃把修改过的Java 客户端源代码重新~译成Java字节码(作ؓ.gwt.-cache/bytecode目录中的.tmp文gQ,然后重新装入模块。可以用这个按钮来~? 短编?#8594;~译→调试周期Q但在用这特性时要牢记几个方?
?只有修改q的源代码才重新~译Q也是_不会Z赖修改过代码的文件生成新的字节码。所以,如果改变了全局帔R的|假设public final int字段的|不会立即在相x件看到这个变化?
?只有修改q的源代码才由GWT重新~译。这意味着Q即便Eclipse IDE里面?#8220;Project clean”也帮不上? 要媄响到所有的相关源代码,譬如通过d新的I?
因ؓq个q程相当W拙Q笔者的忠告是在修改全局帔R旉循以下四个步?
1Q在相应的源文g里面改变public final constant?
2Q重新编译改变后的源代码;
3Q移除整?lt; ModuleName>-client/.get-cache/bytecode目录Q从而删除GWT~存内容;
4、用Eclipse里面?#8220;Run as”Q重新开始启动应用,从而创建带重新~译后字W码的新GWT~存内容Q这U情况下Q最好忽?#8220;h”按钮Q不q在有些情况下,删除整个< ModuleName>-client/.get-cache/bytecode目录后可以?#8220;h”按钮?
在修Ҏ务器端代码时QGWT字节码缓存内容不受媄响。不q,嵌入的Tomcat实例会缓存它Q因而在使用“h”按钮后,只有重新开始启动应用后最初改变的代码才会得到认可。所以ؓ了安全v见,改变服务器端代码后,最好还是重新开始启动应用?
忠告之四: 在宿L式下dServlet Init参数
在处理数据库pȝӞ一般不希望服务器小E序源代码中有硬~码的数据库q接参数。通常会从属性文件读取这些参? 或者更好的是,把它们作为init参数提供l服务器程序(作ؓ应用的Web.xml文g的一部分Q。如果在Web模式下运行应用那没有什么,但在宿主? 式下会出问题Q这是由于GWT宿主模式下的服务器小E序处理存在限制?
好消息是Q只要修改由嵌入式Tomcat实例使用的Web.xml文gQ就可以解决q个问题。ؓ此,? ?lt; ModuleName>-client/tomcat/webapps/ROOT/WEB-INF目录中的Web.xml文gQ或者必要时创徏一 个): 除了嵌入式Tomcat的GWTShellServlet映射外,d带有init参数的上下文部分。因Z下文信息?#8220;全局性的”Q而不是针对特定的? 务器程序,在这里只有一部分的init参数信息Q或者用特D的命名ҎQ把参数与不同的服务器小E序联系h。如果用这个新的web.xml文gQ? 可以删除src/web/WEB-INF文g夹中的那个旧文g?
在服务器程序代码中Q访问init参数的方式与Web模式下读取它们的方式一P譬如final String host = getInitParameter("host")。笔者实现这一点的办法是修改GWT的RemoteServiceServletQ方法跟W二个忠? 里面的如Z辙。现在,只要覆盖GenericServlet的getInitParameter()ҎQ以便? getServletContext()Q而不?getServletConfig()?
另一个忠告是Q如果在宿主模式下和Web模式下测试不同的服务器代码,略过Gant脚本中的GWT~译部分Q从“temp”位置拯~译前的JavaScript代码Q则可以节省旉。这适用于客L代码复杂、编译时间超q?0分钟的情形?
忠告之五: 在浏览器里面昄PDF文g
大多数实际的Web应用提供了生成及阅读PDF文g的方法。本文假设这个PDF文g由服务器程序生成,譬如通过 JasperReport。以后只要点L个超文本链接Q就可以在浏览器里面阅读生成的文件。如果想在宿L式下和Web模式下测试这特性,采取? 下步?
1Q设计一个RPC接口Q接受告诉服务器是在宿主模式下运行还是在Web模式下运行的布尔参数。接口方法会q回的字W串应当带有服务器小E序生成的PDF文g的名Uͼx件名的最后一部分Q?
2Q根据代码片D?昄的代码,实现服务器小E序代码Q这取决于布参?#8220;isScript”?
3Q在客户? 在窗口组件代码里面,使用GWT.isScript()参数调用createXyzPDF()ҎQ从而生成包含服务器程序结果字W串的外部超文本链接?
代码片段4昄了接口方法名为createSummaryPDF()的示例。从服务器小E序q回的字W串?#8220;summary.pdf”?
q当然不是处理这U情늚惟一办法Q但目前适用于我们这个示例。请注意: 在宿L式下启动应用之前Q必d< ModuleName>-client project's src/…/public文g夹中臛_创徏一个虚假的“summary.pdf”文gQ文件名从服务器程序返回)。不Ӟ在浏览器中点M文本链? 后,GWT试图dPDF文gӞ会出?#8220;HTTP 404Q找不到|页”的信息?
忠告之六:力求获得无状态服务器
设计客户?服务器Web应用时要考虑的一个关键问题就? 如何处理会话和状态管理?在Web 1.0时代Q答案很昄: 会话和状态管理是一个服务器问题。但若用GWTQ就有另一个选择。服务器再也不是只提供HTML内容?#8220;web”服务。用GWT RPCQ服务器现在可以支持只提供结构化数据的服务———在本文CZ中,服务由服务器程序实现?
那么QGWT对会话和状态管理有何媄响呢QGWT的技术领导Bruce Johnson在去q的JAOO大会上指出,若用GWTQ会话管理现在应当是一个客L问题。附图显C的qȝ片评qCU种变化?
在本文的JUnit2MR GWT应用中,W者一开始用传l方法来处理服务器小E序中的会话状态。但q是相当W拙的Q务,于是L另一U选择。因此,看了Bruce的灯片后,? 定重新设计整个应用。但q一步需要改变所有RPC接口、缓存策? 最重要的是Q还要改变所有的服务器小E序。因此笔者的? 及早考虑在何处实施会话和状态管理,不妨试试Bruce Johnson的诀H。最l会收到成效?
׃q个军_Q客L对象之间有了更多的联pR于是笔者用了有名的GoF中介者模式(mediator patternQ。不q,在客L有一些JDK 1.4和GWTq行库的限制。因此,重新实现了PropertyChangeEventcd中介者支持,来处理监听程序注册和消息q播?
忠告之七: 使用Selenium实现GWT Web试的自动化
Selenium是一U开源工P它能够轻松测试包含丰富、互动的客户端内容的Web应用?所以,它非帔R用于测试像用GWT创徏的应用那LAjax应用?
当然QGWT里面仍有JUnit和JUnit支持功能Q特别是针对pȝ的异步部分。这里着重介l? SeleniumQ因为它易于使用Q至它的IDE是这P、功能强大。最后但q最不重要的一ҎQ它与JUnit有许多共同之处。可以? Selenium IDE来记录GUI用例Q然后用其“Play”Ҏ来q行记录下来的操作。每个操作之后跟着cMJUnit?#8220;assert”命oQ负责确认页面上的某 些文本。该IDE是Firefox的扩展插Ӟ但务必要使用最新版本的Selenium: Selenium IDE 0 .8 .7Q因为它包含?#8220;waitFor…”命o的重大修正版。说到测试Ajax应用Q这些命令以?#8220;pause”命o非常重要?
忠告之八: 使用Groovy Gant脚本部v应用
在GWT宿主模式下试q行应用Q这实很好Q但把应用部|到应用服务器上或者类似Tomcat的服务器程序容? 上,GWT的真实功能才会体现出来。在q一步,需要创Z个war文gQ它会自动拷贝到Tomcat“webapps”目录。当Ӟ可以使用Ant? ant-contribq行所有必要的准备、编译、拷贝及其他d。但׃Ant脚本变得更复杂后Q? ant-contrib控制l构和属性regex处理有一点笨拙。于是可以用集Groovy和Ant两者之所长的Gant。安装Groovy和Gant 用不?0分钟Q然后,使用来自“build.properties”文g的普通属性,卛_定制“build.gant”脚本。(黑~译Q?
Q计机世界?2007q??5?W?4?B22、B23Q?
The Google Web Toolkit has been out for a while now, and yet there is still basic functionality that is missing from the toolkit. Don’t get me started on the lack of draggable/resizable columns for the FlexTable, because that’s a rant and a half. Given that GWT’s event handling model isn’t bad, you’d think they’d have included from the get-go the ability to handle right-clicks and bringing up a context menu or popup menu. Well, even with 1.6 on the doorstep it seems they forgot again or just don’t care. Now some people will spout out “web apps don’t need or shouldn’t have right-clicks handled or context menus overridden”……and for those I say STFU! Web apps are used for more than just banking, news, forums and dare I say blogs. The browser is becoming the new medium for running applications and just because an application is running in a browser doesn’t mean we should limit functionality. That’s about as narrow minded as saying that we’ve only had one mouse button for this long, why add a second one? Duh!
Anyway, enough with the blabbing. I’ve put together a simple example
to add a right click context menu and override the default browser
context menu using GWT.
In the box below you can try it out, right-click in there and you can demo it.
|
|
In case the iframe doesn’t show up in your browser you can see and try the example here.
Now here’s how it’s done.
I used a DeckPanel to switch between several panels. The popup context menu is used to choose. To allow the DeckPanel to catch the right-click event (and you can also listen for double-clicks and several other things) I extended the DeckPanel. For simplicity I added “Adv” (Advanced) in front of the several classes I’ve extended so this one will be AdvDeckPanel. The first thing to do in the constructor is add sinkEvents(). Then we’ll override onBrowserEvent() in the class. Here’s the code:
public AdvDeckPanel() {
super();
sinkEvents(Event.ONMOUSEUP | Event.ONDBLCLICK | Event.ONCONTEXTMENU);
}
public void onBrowserEvent(Event event) {
GWT.log("onBrowserEvent", null);
event.cancelBubble(true);//This will stop the event from being propagated
event.preventDefault();
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEUP:
if (DOM.eventGetButton(event) == Event.BUTTON_LEFT) {
GWT.log("Event.BUTTON_LEFT", null);
listener.onClick(this, event);
}
if (DOM.eventGetButton(event) == Event.BUTTON_RIGHT) {
GWT.log("Event.BUTTON_RIGHT", null);
listener.onRightClick(this, event);
}
break;
case Event.ONDBLCLICK:
break;
case Event.ONCONTEXTMENU:
GWT.log("Event.ONCONTEXTMENU", null);
break;
default:
break; // Do nothing
}//end switch
}
Notice the two lines in onBrowserEvent():
event.cancelBubble(true);
event.preventDefault();
These are the two lines that tell the browser not to show it’s default context popup menu. Also note that overriding the default context menu doesn’t work in all browsers, I’m not sure if this is a bug in GWT. If you’re using firefox then you’ll have no problems, with IE you may need to add the following to your html:
<body oncontextmenu="return false;">
Other than that there’s just a switch statement that checks the event type, in this case we’re interested with ONMOUSEUP, and we’ll call the listener’s onClick() or onRightClick() based on the Event’s fields.
AdvDeckPanel also has a reference to AdvClickListener which looks like:
public interface AdvClickListener extends ClickListener {
void onClick(Widget sender, Event event);
void onRightClick(Widget sender, Event event);
}
This reference is basically the same as ClickListener, but has a separate method to handle the right-click. I also pass the Event object so I can get the x and y from the click so the context menu shows up at that location instead of the top-left of the screen.
AdvDeckPanel implements AdvClickNotifier which does the same thing as GWT’s SourcesClickEvents interface, but handles the AdvClickListener instead.
public interface AdvClickNotifier {
public void addClickListener(AdvClickListener listener);
public void removeClickListener(AdvClickListener listener);
}
So, once you have your widget (in this case the AdvDeckPanel) and the listeners set up to handle the right-click, then we add in the code to build the popup menu and commands that go with it. I put all of this in the EntryPoint. I have three widgets (panels) and a Command for each like this:
private AdvDeckPanel deckPanel = new AdvDeckPanel();
final private PopupPanel popupPanel = new PopupPanel(true);
private VerticalPanel defaultPanel = new VerticalPanel();
private SimplePanel imagePanel = new SimplePanel();
private SimplePanel sponserPanel = new SimplePanel();
Command showAlertCommand = new Command() {
public void execute() {
deckPanel.showWidget(0);
popupPanel.hide();
Window.alert("Hope this example helps.");
}
};
Command showImageCommand = new Command() {
public void execute() {
deckPanel.showWidget(1);
popupPanel.hide();
}
};
Command showSponserCommand = new Command() {
public void execute() {
deckPanel.showWidget(2);
popupPanel.hide();
}
};
A Command is called when the MenuItem is clicked.
Now the code to build the menu, link the commands, and handle the right-click:
private void createPopupMenu() {
MenuBar popupMenuBar = new MenuBar(true);
MenuItem alertItem = new MenuItem("Show alert", true, showAlertCommand);
MenuItem imageItem = new MenuItem("Show Oliver ", true, showImageCommand);
MenuItem sponserItem = new MenuItem("Show sponser ", true, showSponserCommand);
popupPanel.setStyleName("popup");
alertItem.addStyleName("popup-item");
imageItem.addStyleName("popup-item");
sponserItem.addStyleName("popup-item");
popupMenuBar.addItem(alertItem);
popupMenuBar.addItem(imageItem);
popupMenuBar.addItem(sponserItem);
popupMenuBar.setVisible(true);
popupPanel.add(popupMenuBar);
}
public void onRightClick(Widget sender, Event event) {
int x = DOM.eventGetClientX(event);
int y = DOM.eventGetClientY(event);
popupPanel.setPopupPosition(x, y);
popupPanel.show();
}
Lastly, to make the menu actually look like a popup menu I modified the CSS like so:
.popup {
background-color: gray;
border-color: gray gray gray gray;
border-width: 1px 3px 3px 1px;
border-style: solid solid solid solid;
}
.popup-item {
font-weight: normal;
font-size: 80%;
}
What this does is makes it so the border isn’t the thick default GWT blue, and uses a thin border with the right and bottom borders a bit thicker. This gives the popup that shadowed look.
A couple of books worth checking out are: GWT in Actionand GWT in Practice
Here’s the source and more links.
This entry was posted on Tuesday, February 17th, 2009 at 8:12 pm and is filed under GWT, Java, Programming. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Of course you can debug with a JavaScript debugger (Venkman or Microsoft's Script Debugger), but generated JavaScript is really complex, for people and for those unstable debuggers.
But, wait, we have a debugger, is our state-of-the-art debugger, integrated in our IDE and GWT can use it, if only you could run client into the Shell and server side into your preferred development envinroment !!!
We could deploy our web-app into the integrated Tomcat, but it's not easy to do (and an hell to mantain) and also can be that you don't want (or you can't) use Tomcat for your Web Application.
I think that everyone should do his work, so let's Google Shell do the client, and your application server run the server side code. It's not difficult, you only need to follow these few steps.
First thing do to it's a little (I promise really little and only one) change to your GWT code, so that the client will always use absolute urls for your web-app, allowing us to use the ContextPath without problems, whatever it's the relative position of your Google client inside your root context.
To do that we have to register this way the end-point of the remote service:
endpoint.setServiceEntryPoint( GWT.getModuleBaseURL()+"/myRemoteService" );
now we must also remember to change our servlet path inside the GWT module file (*.gwt.xml) putting the complete service name at the top, in this case /mypackage.google.GWTClient:
<servlet path="/mypackage.google.GWTClient/myRemoteService" class="mypackage.google.server.MyRemoteServiceImpl"/>
No other change to your client side code, I promise again.
Now let's start to modify the web-app to run our services. The first thing is to include in our WEB-INF/lib the gwt-user.jar.
Unfortunately the one supplied by Google doesn't work, because it includes the javax.servlet.* classes to simplify the automatic generation of development projects.
We must strip that out (with an ANT Task obviously):
Now we can also add that to our IDE build-path.
Everything is ready to begin the configuration of our web-app, beginning with the registration of the remote service inside web.xml, remembering the module name we used in the *.gwt.xml file:
We don't have yet finished, we must also "simulate" one of the call implemented by GWTServlet, registering its path into web.xml and with the help of a JSP::
the gwt-hosted.jsp JSP it's only a simple script with a row of scriptlet that export our web-app context to the GWT client::
We did it !!!!The good thing is that we don't even have to "Compile" our JavaScript in the destination context during debug. Infact the GWT Shell callback will always call our Java classes and not the JavaScript code.
So we have together maximum flexibility on client-side project for our debugging purposes, and only at the very finish of the development cycle we will produce the JavaScript code.
Could we be in a better position as web developer ?
Now some tips about GWT's Shell:
Are we in debug mode or not ? The answer is yes, so we should configure the Shell log to a more verbose level (if you have used GWT.log in your code):
-logLevel ERROR|WARN|INFO|TRACE|DEBUG|SPAM|ALL
We should also disable the integrated Tomcat:
-noserver
and configure the Shell to generate JavaScript code directly inside our web-app project:
-out <web-app-project-dir>
Also can be useful to open directly the Shell browser to our web-app at startup, simply putting the http url as the last parameter of the command line that start the Shell.
But what if all this it's not enough to solve our problems, may be because the generated JavaScript is not working fine ? Well, together with much luck, we can try to switch the generation code to DETAILED:
-style DETAILED
and use traditional JavaScript debugging tecnique.
Ok, now everything it's integrated with your web-application and you're in love with GWT. You did really a great work, a prototype that it's working fine and looks pretty. But when time comes that you must put it into your old fashioned Struts/Tiles web-application it stops working and sure you can't write everything from scratch again, but do a step after another and start with a single functionality done with GWT.
Well, I don't know you, but I suffered from this problem, but at the end, thank to some little tips, I was able to let Struts and GWT not only to live together, but to cooperate to make my application look better.
It was like having an old fashioned B&W TV Set and a new HDMI on the same bench, side by side !!!
Well, to achieve this, we have to do some simple steps:
- In web.xml add another Struts action mapping, this time it must be extension based, for instance I choosed *.gwt:
- In struts-config.xml write the new Struts action that will render the GWT page, taking care to put at the top of the path the complete GWT's module name, so that the script will be able to find internal files with no problems:
- (only if you use Tiles as view)Add a Tiles definition that will include GWT's (in my example into the body of the layout JSP):
- (if you used filters for Struts Action mapping)Update your filters so that they will be able to do their work also with the new GWT module:
That's all, the game is made.
The life-cycle of a GWT's Service is managed by your
servlet-container, because as a design choice Google opted for the
portabily and simplicity of the Servlet model.
In our code this mean that our service implementations will all inherit (indirectly) from HttpServlet. This means also that to integrate them in our Spring managed container we have to play dirty. I decided to use static property inside our Google Service implementation, so that Spring will be able to assign it at startup time.
Pratically, if we want to inject something in GWT's service mypackage.google.server.MyRemoteServiceImpl's myProperty property, it's enough to declare it static:
Then to let Spring works, we also need an instance setter:
In our applicationContext.xml we can inject the myPropertyBean Bean simply with a dummy bean declaration of the GWT implementation class:
This works because at startup Spring will instantiate an
instance of the MyRemoteServiceImpl class, this instance will never be
used by the servlet-container, but the bean property is contained into
a static variable, so every instance will have it !!!
Now, if your business bean myPropertyBean uses Hibernate and you usually use Spring's OpenSessionInViewFilter/Interceptor
to manage your Session, you need a further step and use an AOP
Interceptor. But we are lucky in this, because all is done by Spring
out-of-the-box only with few lines of configuration::
With this configuration, if myPropertyBean is used outside a transaction scope, a new Hibernate transaction will be created and assigned to it. In our case this is also the span of the Hibernate's Session (thanks to HibernateTransactionManager). The myHibernateInterceptor will also take charge of eventually close the Session at the end of the business method.