抱歉我的问题太久了。我完全是GWT的新手,我的经验还很少。我想用GWT创建简单的聊天应用程序。
我在this使用 java + servlet + jsp 找到了一个简单的java聊天应用程序链接。我对它进行了修改并转换为GWT可压缩代码。 (我使用GAE 在http://gwt-chatting-test.appspot.com部署了它)。我相信我的代码可能有很多弱点和安全漏洞,但我处理它们的经验较少。(如果你找到了,请善意纠正我)。
我想完整地描述我的代码。我使用了servlet 2.4,GWT 2.5,UI Binder和其他必需的库。
1)。登录页面
Index.ui.xml
<!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:d="urn:import:com.google.gwt.dom.client">
<ui:style>
.important {
font-weight: bold;
}
.lblUserName {
margin-right: 3px;
}
</ui:style>
<g:HTMLPanel>
<center>
<g:HorizontalPanel>
<g:Label ui:field="lblUserName" text="User Name : " styleName="{style.lblUserName}"/><g:TextBox height="15px" ui:field="txtUserName"/>
<g:Button styleName="{style.important}" text="Login" ui:field="btnLogin" />
</g:HorizontalPanel>
</center>
</g:HTMLPanel>
Index.java
public class Index extends Composite {
interface IndexUiBinder extends UiBinder<Widget, Index> {}
private static IndexUiBinder uiBinder = GWT.create(IndexUiBinder.class);
@UiField Button btnLogin;
@UiField TextBox txtUserName;
private ChatServiceAsync chatService = GWT.create(ChatService.class);
public Index() {
initWidget(uiBinder.createAndBindUi(this));
}
@UiHandler("btnLogin")
void onClick(ClickEvent e) {
login();
}
@UiHandler("txtUserName")
void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
if (txtUserName.getText().trim().length() == 0) {
Window.alert("Enter your name for chat-room !");
}
else {
login();
}
}
}
private void login() {
AsyncCallback<String> callback = new AsyncCallback<String>() {
@Override
public void onSuccess(String result) {
RootPanel.get().clear();
RootPanel.get().add(new Chat());
}
@Override
public void onFailure(Throwable caught) {
System.err.println(caught.getMessage());
}
};
chatService.login(txtUserName.getText(), callback);
}
}
2)。用于聊天页面
Chat.ui.xml
<!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">
<ui:style>
.main {
height: 420px;
border: 1px solid silver;
padding: 2px;
width: 620px;
margin-bottom: 5px;
}
.txtInputMsg {
margin-right: 3px;
width : 560px;
height :25px;
}
</ui:style>
<g:HTMLPanel>
<center>
<g:VerticalPanel>
<g:ScrollPanel styleName="{style.main}" height="420px" width="620px" ui:field="pnlMain">
<g:VerticalPanel ui:field="pnlMessage"/>
</g:ScrollPanel>
<g:HorizontalPanel ><g:TextBox styleName="{style.txtInputMsg}" ui:field="txtInputMsg"/><g:Button ui:field="btnLogout" text="Logout"/></g:HorizontalPanel>
</g:VerticalPanel>
</center>
</g:HTMLPanel>
Chat.java
public class Chat extends Composite {
public interface MessageTemlate extends SafeHtmlTemplates {
@Template("<div class =\"{2}\"><div class = \"msgTitle\"> {0}</div><div class=\"msgDescription\">{1}</div></div>")
SafeHtml getFormattedMessage(String name, SafeHtml message, String backgroundColor);
@Template("<div class =\"loginInform\"><span class=\"userName\">{0}</span> has been joined to this conversation !</div>")
SafeHtml notifyLoginUser(String userName);
@Template("<div class =\"logoutInform\"><span class=\"userName\">{0}</span> has been left from this conversation !</div>")
SafeHtml notifyLogoutUser(String userName);
}
private static ChatUiBinder uiBinder = GWT.create(ChatUiBinder.class);
@UiField TextBox txtInputMsg;
@UiField ScrollPanel pnlMain;
@UiField VerticalPanel pnlMessage;
@UiField Button btnLogout;
private static final XMLHttpRequest request = XMLHttpRequest.create();
private static final MessageTemlate TEMPLATES = GWT.create(MessageTemlate.class);
private ChatServiceAsync chatService = GWT.create(ChatService.class);
private String loginUser;
private Timer timer;
interface ChatUiBinder extends UiBinder<Widget, Chat> {}
public Chat() {
initWidget(uiBinder.createAndBindUi(this));
pnlMessage.getElement().setAttribute("style", "width:100%");
initLoginUserName();
request.open("post", URL.encode(GWT.getModuleBaseURL()
+ "chat.do?action=login&time='" + new Date().getTime() + ""));
request.send(null);
}
@UiHandler("txtInputMsg")
void sendMessage(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
if (txtInputMsg.getText().trim().length() > 0) {
SafeHtml message = SafeHtmlUtils.fromTrustedString(txtInputMsg.getText());
request.open("post", URL.encode(GWT.getModuleBaseURL()
+ "chat.do?action=send&msg=" + message.asString() + "&time='" + new Date().getTime() + ""));
txtInputMsg.setText("");
pnlMessage.add(new HTML(TEMPLATES.getFormattedMessage(loginUser, message, "myMessage")));
pnlMain.scrollToBottom();
txtInputMsg.setFocus(true);
request.send(null);
}
}
}
@UiHandler("btnLogout")
void logout(ClickEvent e) {
boolean confirm = Window.confirm("Are you sure you want to logout now ?");
if (confirm) {
timer.cancel();
request.open("post", URL.encode(GWT.getModuleBaseURL()
+ "chat.do?action=logout&time='" + new Date().getTime() + ""));
request.send(null);
AsyncCallback<String> callback = new AsyncCallback<String>() {
@Override
public void onSuccess(String result) {
RootPanel.get().clear();
RootPanel.get().add(new Index());
}
@Override
public void onFailure(Throwable caught) {
System.err.println(caught.getMessage());
}
};
chatService.logOut(loginUser, callback);
}
}
private void initChatListener() {
timer = new Timer() {
@Override
public void run() {
request.open("post", URL.encode(GWT.getModuleBaseURL()
+ "chat.do?action=get&time='"
+ new Date().getTime() + ""));
request.send(null);
request.setOnReadyStateChange(new ReadyStateChangeHandler() {
@Override
public void onReadyStateChange(XMLHttpRequest xhr) {
if (xhr.getReadyState() == 4) {
if (xhr.getStatus() == 200 && xhr.getResponseText().trim().length() > 1) {
HTML html = new HTML(xhr.getResponseText().trim());
JSONValue value = JSONParser.parseLenient(html.getText());
JSONWrapper json = new JSONWrapper(value);
for (int i = 0; i < json.getValue().isArray().size(); i++) {
String[] chatData = json.get(i).getValue().toString()
.replaceAll("\"", "").split(":");
if (chatData.length >= 3) {
if (chatData[2].equals("login")) {
pnlMessage.add(new HTML(TEMPLATES.notifyLoginUser(chatData[0])));
}
else {
pnlMessage.add(new HTML(TEMPLATES.notifyLogoutUser(chatData[0])));
}
}
else {
pnlMessage.add(new HTML(TEMPLATES.getFormattedMessage(chatData[0],
SafeHtmlUtils.fromTrustedString(chatData[1]), "receivedMessage")
.asString()));
}
pnlMain.scrollToBottom();
}
}
}
}
});
}
};
timer.scheduleRepeating(1000);
}
private void initLoginUserName() {
AsyncCallback<String> callback = new AsyncCallback<String>() {
@Override
public void onSuccess(String result) {
loginUser = result;
if (loginUser == null) {
Window.alert("You need to login !");
RootPanel.get().clear();
RootPanel.get().add(new Index());
}
else {
initChatListener();
}
}
@Override
public void onFailure(Throwable caught) {
System.err.println(caught.getMessage());
}
};
chatService.getUsername(callback);
}
}
3)。 RPC的服务和ServiceAsync
ChatService.java
@RemoteServiceRelativePath("chatService")
public interface ChatService extends RemoteService {
String login(String userName);
String getUsername();
String logOut(String username);
}
ChatServiceAsync.java
public interface ChatServiceAsync {
void login(String userName, AsyncCallback<String> callback);
void getUsername(AsyncCallback<String> callback);
void logOut(String username, AsyncCallback<String> callback);
}
4)。服务器端控制
ChatServiceImpl.java
@SuppressWarnings("serial")
public class ChatServiceImpl extends RemoteServiceServlet implements ChatService {
public String login(final String userName) {
String newUserName = userName;
HttpSession httpSession = getThreadLocalRequest().getSession();
Map<String, List<String>> chat = ChatSupport.getChattingUsers();
synchronized (chat) {
// prevent userName conflict
int i = 1;
while (chat.containsKey(newUserName)) {
++i;
newUserName = userName + "(" + i + ")";
}
chat.put(newUserName, new ArrayList<String>());
}
httpSession.setAttribute("UID", newUserName);
return "LOGIN_SUCCESS";
}
public String logOut(String username) {
// remove the mapping of user name
ChatSupport.getChattingUsers().remove(username);
getThreadLocalRequest().getSession().invalidate();
return "LOGOUT_SUCCESS";
}
public String getUsername() {
// check if there is a HTTP session setup.
HttpSession httpSession = getThreadLocalRequest().getSession(false);
if (httpSession == null) {
return null;
}
// return the user name
return (String) httpSession.getAttribute("UID");
}
}
ChatSupport.java
@SuppressWarnings("serial")
public class ChatSupport extends HttpServlet {
// message map, mapping user UID with a message list
private static Map<String, List<String>> users = new HashMap<String, List<String>>();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String action = req.getParameter("action");
if ("login".equals(action)) {
String userName = (String) req.getSession().getAttribute("UID");
for (String key : users.keySet()) {
if (!key.equals(userName)) {
synchronized (users.get(key)) {
users.get(key).add(userName + "::login");
}
}
}
}
else if ("logout".equals(action)) {
String userName = (String) req.getSession().getAttribute("UID");
for (String key : users.keySet()) {
if (!key.equals(userName)) {
synchronized (users.get(key)) {
users.get(key).add(userName + "::logout");
}
}
}
}
// send message
else if ("send".equals(action)) {
// get param with UTF-8 enconding
String msg = new String(req.getParameter("msg").getBytes("ISO-8859-1"), "UTF-8");
String userName = (String) req.getSession().getAttribute("UID");
for (String key : users.keySet()) {
if (!key.equals(userName)) {
synchronized (users.get(key)) {
// put message to any other user's msg list
users.get(key).add(userName + ":" + msg);
}
}
}
}
else if ("get".equals(action)) { // get message
String userName = (String) req.getSession().getAttribute("UID");
if (userName == null) resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
List<String> messages = users.get(userName);
synchronized (messages) {
if (messages.size() > 0) {
// for UTF-8 chars
resp.setCharacterEncoding("UTF-8");
PrintWriter out = resp.getWriter();
JSONArray result = new JSONArray();
// add all msg to json array and clear list
while (messages.size() > 0) {
result.add(messages.remove(0));
}
out.println(result);
out.close();
}
}
}
}
public static Map<String, List<String>> getChattingUsers() {
return users;
}
}
5)。我的gwt.xml
....
<module rename-to='testing'>
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.user.theme.clean.Clean'/>
<inherits name="com.google.gwt.http.HTTP" />
<inherits name="com.google.gwt.json.JSON" />
<inherits name="com.pathf.gwt.util.json.jsonwrapper" />
<entry-point class='test.client.TestingEntry'/>
<source path='client'/><source path='shared'/>
</module>
6)。 web.xml上的Servlet配置
<servlet>
<servlet-name>chatServlet</servlet-name>
<servlet-class>test.server.ChatServiceImpl</servlet-class>
</servlet>
<servlet>
<servlet-name>ChatServlet</servlet-name>
<servlet-class>test.server.ChatSupport</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ChatServlet</servlet-name>
<url-pattern>/testing/chat.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>chatServlet</servlet-name>
<url-pattern>/testing/chatService</url-pattern>
我的程序在多个浏览器上运行正常,但无法在同一个浏览器上运行。我不能通过使用带有多标签的同一个浏览器来运行此程序。我想我正在使用相同的会话,我需要为每次登录创建新的会话。但我不知道如何创建它。从How to get multiple session in single browser,这可能会导致安全问题,我不应该这样做。但目前我忘记了安全问题。
那么,我如何才能支持多标签浏览器?