--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="con" path="com.google.appengine.eclipse.core.GAE_CONTAINER"/>\r
+ <classpathentry kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>\r
+ <classpathentry kind="output" path="war/WEB-INF/classes"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>chat</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>com.google.gwt.eclipse.core.gwtProjectValidator</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>com.google.appengine.eclipse.core.enhancerbuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>com.google.appengine.eclipse.core.projectValidator</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>com.google.gdt.eclipse.core.webAppProjectValidator</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ <nature>com.google.appengine.eclipse.core.gaeNature</nature>\r
+ <nature>com.google.gwt.eclipse.core.gwtNature</nature>\r
+ <nature>com.google.gdt.eclipse.core.webAppNature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+#Mon Mar 29 21:59:37 JST 2010\r
+eclipse.preferences.version=1\r
+filesCopiedToWebInfLib=appengine-api-1.0-sdk-1.3.0.jar|appengine-api-labs-1.3.0.jar|datanucleus-appengine-1.0.4.1.final.jar|datanucleus-core-1.1.5.jar|datanucleus-jpa-1.1.5.jar|geronimo-jpa_3.0_spec-1.1.1.jar|geronimo-jta_1.1_spec-1.1.1.jar|jdo2-api-2.3-eb.jar\r
--- /dev/null
+#Mon Mar 29 21:59:36 JST 2010\r
+eclipse.preferences.version=1\r
+filesCopiedToWebInfLib=gwt-servlet.jar\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
+
+ <persistence-manager-factory name="transactions-optional">
+ <property name="javax.jdo.PersistenceManagerFactoryClass"
+ value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
+ <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
+ <property name="javax.jdo.option.NontransactionalRead" value="true"/>
+ <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
+ <property name="javax.jdo.option.RetainValues" value="true"/>
+ <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
+ </persistence-manager-factory>
+</jdoconfig>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<module\r
+ rename-to="index">\r
+\r
+ <inherits\r
+ name="com.google.gwt.user.User" />\r
+ <inherits\r
+ name="com.google.gwt.user.theme.standard.Standard" />\r
+ <entry-point\r
+ class="chat.client.Index" />\r
+ <source\r
+ path="client" />\r
+\r
+</module>\r
--- /dev/null
+\r
+package chat.client;\r
+\r
+import java.io.Serializable;\r
+import java.util.Date;\r
+\r
+@SuppressWarnings("serial")\r
+public class ChatMessageDto implements Serializable {\r
+\r
+ private String message;\r
+\r
+ private Date submitDate;\r
+\r
+ public String getMessage() {\r
+ return this.message;\r
+ }\r
+\r
+ public void setMessage(String message) {\r
+ this.message = message;\r
+ }\r
+\r
+ public Date getSubmitDate() {\r
+ return this.submitDate;\r
+ }\r
+\r
+ public void setSubmitDate(Date submitDate) {\r
+ this.submitDate = submitDate;\r
+ }\r
+\r
+}\r
--- /dev/null
+\r
+package chat.client;\r
+\r
+import java.util.List;\r
+\r
+import com.google.gwt.user.client.rpc.RemoteService;\r
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;\r
+\r
+@RemoteServiceRelativePath("chat")\r
+public interface ChatService extends RemoteService {\r
+\r
+ void say(String message);\r
+\r
+ List<ChatMessageDto> getMessage();\r
+\r
+}\r
--- /dev/null
+\r
+package chat.client;\r
+\r
+import java.util.List;\r
+\r
+import com.google.gwt.user.client.rpc.AsyncCallback;\r
+\r
+public interface ChatServiceAsync {\r
+\r
+ void say(String message, AsyncCallback<Void> callback);\r
+\r
+ void getMessage(AsyncCallback<List<ChatMessageDto>> callback);\r
+\r
+}\r
--- /dev/null
+\r
+package chat.client;\r
+\r
+import java.util.List;\r
+\r
+import com.google.gwt.core.client.EntryPoint;\r
+import com.google.gwt.core.client.GWT;\r
+import com.google.gwt.event.dom.client.ClickEvent;\r
+import com.google.gwt.event.dom.client.ClickHandler;\r
+import com.google.gwt.user.client.Window;\r
+import com.google.gwt.user.client.rpc.AsyncCallback;\r
+import com.google.gwt.user.client.ui.Button;\r
+import com.google.gwt.user.client.ui.Grid;\r
+import com.google.gwt.user.client.ui.HorizontalPanel;\r
+import com.google.gwt.user.client.ui.RootPanel;\r
+import com.google.gwt.user.client.ui.TextBox;\r
+import com.google.gwt.user.client.ui.VerticalPanel;\r
+\r
+public class Index implements EntryPoint {\r
+\r
+ private final ChatServiceAsync chatService = GWT.create(ChatService.class);\r
+\r
+ private Grid receiveMessageGrid;\r
+\r
+ private TextBox messageField;\r
+\r
+ private Button sayButton;\r
+\r
+ public void onModuleLoad() {\r
+ VerticalPanel p1 = new VerticalPanel();\r
+\r
+ this.receiveMessageGrid = new Grid(0, 1);\r
+ p1.add(this.receiveMessageGrid);\r
+\r
+ HorizontalPanel p2 = new HorizontalPanel();\r
+ p1.add(p2);\r
+\r
+ this.messageField = new TextBox();\r
+ this.messageField.setFocus(true);\r
+ p2.add(this.messageField);\r
+\r
+ this.sayButton = new Button();\r
+ this.sayButton.setText("Say");\r
+ this.sayButton.addClickHandler(new ClickHandler() {\r
+\r
+ @Override\r
+ public void onClick(ClickEvent event) {\r
+ Index.this.sayMessage();\r
+ }\r
+\r
+ });\r
+ p2.add(this.sayButton);\r
+\r
+ RootPanel.get().add(p1);\r
+\r
+ this.getMessages();\r
+ }\r
+\r
+ private void sayMessage() {\r
+ this.chatService.say(this.messageField.getText(), new AsyncCallback<Void>() {\r
+\r
+ @Override\r
+ public void onSuccess(Void result) {\r
+ }\r
+\r
+ @Override\r
+ public void onFailure(Throwable caught) {\r
+ Window.alert("error");\r
+ }\r
+\r
+ });\r
+\r
+ this.messageField.setText("");\r
+ this.messageField.setFocus(true);\r
+ }\r
+\r
+ private void getMessages() {\r
+ this.chatService.getMessage(new AsyncCallback<List<ChatMessageDto>>() {\r
+\r
+ @Override\r
+ public void onSuccess(List<ChatMessageDto> result) {\r
+ for (ChatMessageDto dto : result) {\r
+ Index.this.receiveMessageGrid.resize(Index.this.receiveMessageGrid.getRowCount() + 1, 1);\r
+ Index.this.receiveMessageGrid.setText(Index.this.receiveMessageGrid.getRowCount() - 1, 0, dto.getMessage());\r
+ }\r
+\r
+ Index.this.getMessages();\r
+ }\r
+\r
+ @Override\r
+ public void onFailure(Throwable caught) {\r
+ Window.alert("error");\r
+ }\r
+\r
+ });\r
+ }\r
+\r
+}\r
--- /dev/null
+\r
+package chat.server;\r
+\r
+import java.util.Date;\r
+\r
+import javax.jdo.annotations.IdGeneratorStrategy;\r
+import javax.jdo.annotations.IdentityType;\r
+import javax.jdo.annotations.PersistenceCapable;\r
+import javax.jdo.annotations.Persistent;\r
+import javax.jdo.annotations.PrimaryKey;\r
+\r
+@PersistenceCapable(identityType = IdentityType.APPLICATION)\r
+public class ChatMessageEntity {\r
+\r
+ @PrimaryKey\r
+ @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)\r
+ private Long id;\r
+\r
+ @Persistent\r
+ private String message;\r
+\r
+ @Persistent\r
+ private Date submitDate;\r
+\r
+ @Persistent\r
+ private String ip;\r
+\r
+ public Long getId() {\r
+ return this.id;\r
+ }\r
+\r
+ public void setId(Long id) {\r
+ this.id = id;\r
+ }\r
+\r
+ public String getMessage() {\r
+ return this.message;\r
+ }\r
+\r
+ public void setMessage(String message) {\r
+ this.message = message;\r
+ }\r
+\r
+ public Date getSubmitDate() {\r
+ return this.submitDate;\r
+ }\r
+\r
+ public void setSubmitDate(Date submitDate) {\r
+ this.submitDate = submitDate;\r
+ }\r
+\r
+ public String getIp() {\r
+ return this.ip;\r
+ }\r
+\r
+ public void setIp(String ip) {\r
+ this.ip = ip;\r
+ }\r
+\r
+}\r
--- /dev/null
+\r
+package chat.server;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Date;\r
+import java.util.List;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import javax.jdo.PersistenceManager;\r
+import javax.jdo.Query;\r
+import javax.servlet.http.HttpSession;\r
+\r
+import chat.client.ChatMessageDto;\r
+import chat.client.ChatService;\r
+\r
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;\r
+\r
+@SuppressWarnings("serial")\r
+public class ChatServiceImpl extends RemoteServiceServlet implements ChatService {\r
+\r
+ private static final Logger LOG = Logger.getLogger(ChatServiceImpl.class.getName());\r
+\r
+ @Override\r
+ public void say(String message) {\r
+ ChatServiceImpl.LOG.log(Level.FINEST, "say() start");\r
+ try {\r
+\r
+ PersistenceManager pm = PMF.get().getPersistenceManager();\r
+ try {\r
+\r
+ ChatMessageEntity m = new ChatMessageEntity();\r
+ m.setMessage(message);\r
+ m.setIp(super.getThreadLocalRequest().getRemoteHost());\r
+ m.setSubmitDate(new Date());\r
+\r
+ pm.makePersistent(m);\r
+\r
+ } finally {\r
+ pm.close();\r
+ }\r
+\r
+ } catch (RuntimeException e) {\r
+ ChatServiceImpl.LOG.log(Level.SEVERE, null, e);\r
+\r
+ throw e;\r
+ } finally {\r
+ ChatServiceImpl.LOG.log(Level.FINEST, "say() end");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public List<ChatMessageDto> getMessage() {\r
+ ChatServiceImpl.LOG.log(Level.FINEST, "getMessage() start");\r
+ try {\r
+\r
+ long requestTime = System.currentTimeMillis();\r
+\r
+ Date startDate = null;\r
+\r
+ HttpSession session = super.getThreadLocalRequest().getSession();\r
+\r
+ if (session.getAttribute("startDate") == null) {\r
+ startDate = new Date();\r
+ session.setAttribute("startDate", startDate);\r
+ } else {\r
+ startDate = (Date) session.getAttribute("startDate");\r
+ }\r
+\r
+ List<ChatMessageDto> messageList = new ArrayList<ChatMessageDto>();\r
+\r
+ while ((System.currentTimeMillis() - requestTime) < 5000) {\r
+ PersistenceManager pm = PMF.get().getPersistenceManager();\r
+ try {\r
+ Query query = pm.newQuery(ChatMessageEntity.class);\r
+ query.setFilter("submitDate > submitDateParam");\r
+ query.declareParameters("java.util.Date submitDateParam");\r
+ query.setOrdering("submitDate");\r
+\r
+ List<ChatMessageEntity> queryResult = (List<ChatMessageEntity>) query.execute(startDate);\r
+\r
+ for (ChatMessageEntity m : queryResult) {\r
+ ChatMessageDto dto = new ChatMessageDto();\r
+ dto.setMessage(m.getMessage());\r
+ dto.setSubmitDate(m.getSubmitDate());\r
+\r
+ messageList.add(dto);\r
+\r
+ session.setAttribute("startDate", m.getSubmitDate());\r
+ }\r
+ } finally {\r
+ pm.close();\r
+ }\r
+\r
+ if (messageList.size() > 0) {\r
+ break;\r
+ }\r
+\r
+ try {\r
+ Thread.sleep(500);\r
+ } catch (InterruptedException e) {\r
+ ChatServiceImpl.LOG.log(Level.SEVERE, null, e);\r
+ }\r
+ }\r
+\r
+ return messageList;\r
+\r
+ } catch (RuntimeException e) {\r
+ ChatServiceImpl.LOG.log(Level.SEVERE, null, e);\r
+\r
+ throw e;\r
+ } finally {\r
+ ChatServiceImpl.LOG.log(Level.FINEST, "getMessage() end");\r
+ }\r
+ }\r
+}\r
--- /dev/null
+\r
+package chat.server;\r
+\r
+import javax.jdo.JDOHelper;\r
+import javax.jdo.PersistenceManagerFactory;\r
+\r
+public class PMF {\r
+\r
+ private static final PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("transactions-optional");\r
+\r
+ private PMF() {\r
+ }\r
+\r
+ public static PersistenceManagerFactory get() {\r
+ return PMF.pmf;\r
+ }\r
+\r
+}\r
--- /dev/null
+# A default log4j configuration for log4j users.
+#
+# To use this configuration, deploy it into your application's WEB-INF/classes
+# directory. You are also encouraged to edit it as you like.
+
+# Configure the console as our one appender
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n
+
+# tighten logging on the DataNucleus Categories
+log4j.category.DataNucleus.JDO=WARN, A1
+log4j.category.DataNucleus.Persistence=WARN, A1
+log4j.category.DataNucleus.Cache=WARN, A1
+log4j.category.DataNucleus.MetaData=WARN, A1
+log4j.category.DataNucleus.General=WARN, A1
+log4j.category.DataNucleus.Utility=WARN, A1
+log4j.category.DataNucleus.Transaction=WARN, A1
+log4j.category.DataNucleus.Datastore=WARN, A1
+log4j.category.DataNucleus.ClassLoading=WARN, A1
+log4j.category.DataNucleus.Plugin=WARN, A1
+log4j.category.DataNucleus.ValueGeneration=WARN, A1
+log4j.category.DataNucleus.Enhancer=WARN, A1
+log4j.category.DataNucleus.SchemaTool=WARN, A1
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<appengine-web-app
+ xmlns="http://appengine.google.com/ns/1.0">
+
+ <application>test-chat</application>
+ <version>1</version>
+
+ <!-- Configure java.util.logging -->
+ <system-properties>
+ <property
+ name="java.util.logging.config.file"
+ value="WEB-INF/logging.properties" />
+ </system-properties>
+
+ <sessions-enabled>true</sessions-enabled>
+
+</appengine-web-app>
--- /dev/null
+# A default java.util.logging configuration.
+# (All App Engine logging is through java.util.logging by default).
+#
+# To use this configuration, copy it into your application's WEB-INF
+# folder and add the following to your appengine-web.xml:
+#
+# <system-properties>
+# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
+# </system-properties>
+#
+
+# Set the default logging level for all loggers to WARNING
+.level = WARNING
+
+# Set the default logging level for ORM, specifically, to WARNING
+DataNucleus.JDO.level=WARNING
+DataNucleus.Persistence.level=WARNING
+DataNucleus.Cache.level=WARNING
+DataNucleus.MetaData.level=WARNING
+DataNucleus.General.level=WARNING
+DataNucleus.Utility.level=WARNING
+DataNucleus.Transaction.level=WARNING
+DataNucleus.Datastore.level=WARNING
+DataNucleus.ClassLoading.level=WARNING
+DataNucleus.Plugin.level=WARNING
+DataNucleus.ValueGeneration.level=WARNING
+DataNucleus.Enhancer.level=WARNING
+DataNucleus.SchemaTool.level=WARNING
+
+# Translation Chat logger
+chat.server.ChatServiceImpl.level=FINEST
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">\r
+<web-app>\r
+ <servlet>\r
+ <servlet-name>ChatServlet</servlet-name>\r
+ <servlet-class>chat.server.ChatServiceImpl</servlet-class>\r
+ </servlet>\r
+ <servlet-mapping>\r
+ <servlet-name>ChatServlet</servlet-name>\r
+ <url-pattern>/index/chat</url-pattern>\r
+ </servlet-mapping>\r
+ <welcome-file-list>\r
+ <welcome-file>index.html</welcome-file>\r
+ </welcome-file-list>\r
+</web-app>\r
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\r
+<html>\r
+ <head>\r
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">\r
+ <title>Chat</title>\r
+ <script type="text/javascript" language="javascript" src="index/index.nocache.js"></script>\r
+ </head>\r
+ <body>\r
+ <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position: absolute; width: 0; height: 0; border: 0"></iframe>\r
+ </body>\r
+</html>\r