Sakura Wars game and OVA vs TV series

I’m a fan of the Sakura Wars (サクラ大戦) game franchise since the series came out for the Sega Saturn. It was the first dating sim game I have ever played and finished. So far I have only finished four and three of them were Sakura Wars 1 to 3. Most other dating sim games are just a big bore to play with. The game itself is in a language I can barely understand — Japanese, so I had to make up some of the story as I go along.
Continue reading Sakura Wars game and OVA vs TV series

AJAX queue

I was thinking of doing a simple AJAX framework for myself using some concepts of GUI programming. Actually just one, the event queue. Please note I haven’t done the implementation yet, this is just an idea at the moment. The advantange to performing this approach is that it allows a separation of event handling from the actual page. The development of the AJAX enabled page would be similar to that of a standard GUI application. Components:

  • one JMS queue that resides on the presentation tier.
  • one servlet to retrieve the queue data from the JMS queue and convert it to XML
  • one servlet to retrieve a secure random number.
  • a Javascript API to retrieve the queue data from the servlet and process the queue events.
  • a JavaScript API to send data to the Servlet tier
  • a Java API to put things into the JMS queue.

JMS Queue All the event information is stored in a JMS queue. A JMS queue is required in order to ensure clusterabilty. On my initial drafts I put the queue as part of the session. Once I started to type out some rough implementation, I realized it would not be thread safe, and putting in the synchronized block prevents it from being clusterable. So I ended up with using a JMS queue on the presentation tier.

For those that don’t understand the concept of “on the presentation tier,” the basic idea is you would have a separate set of resources (which might be cheaper alternatives) that is used only to support whatever presentation tasks you might have. This is to allow separation of business concerns from presentation concerns. For example, a presentation tier database stores things that are too big or cumbersome to let session management handle. In the same way a temp file is used to put information rather than store things in memory and let the virtual memory manager handle the data. The presentation tier resources tend to be cheaper and less persistent, i.e., on application restart, all the temporary runtime data stored in there are deleted.

In order to prevent queues from overflowing, each message in the queue should have an expiration set. The expiration should be no more than one minute depending on how long you expect clients to process the next event. The messages should also be mark as non-persisting, since we don’t need them to stay if the queues happen to restart (since the application might be restarted as well anyway). QueueServlet The QueueServlet is an HttpServlet that retrieves event data from the JMS queue and transforms the data into an XML stream (possibly via JDOM or manual StringBuffers). This is the only place for the AJAX side to get responses from the server side. The servlet will implement the doPost() method and uses the following logic:

public void doPost(HttpServletRequest request, HttpServletResponse response) {
 // requestId is set by the RequestIdServlet
 String requestId = request.getHeader("requestId");
 String sessionId = request.getSession(false).getId();
 List queueData = new LinkedList(); // we don't do random access to the list

 do {
   // get the message from the JMS queue non blocking
   Message m =
      jmsQueue.getMessage("requestId = '" + requestId + "' and " +
                          "sessionId = '" + sessionId + "'");
   if (m == null) {
     break;
   }
   queueData.add(new AjaxEvent(m));
 }
 // Write the queue data as XML
 writeQueueDataXml(queueData,response);
}

This approach ensures that the largest possible chunk is sent for every request. This solves the issue of too many requests getting a small chunk of data. RequestIdServlet This servlet performs a SecureRandom call to get a random number to pass back to the browser. The purpose of the id is to allow multiple pages to the same website to be open at the same time. As the QueueServlet, it only supports the POST method. AjaxQueue JavaScript The AjaxQueue JavaScript API is a JS file provided by the framework. It is responsible for creating/managing the XmlHttpRequest object and dealing with infrastructure based tasks. The typical Ajax enabled page would look like this:

<script src="ajaxQueue.js" type="text/javascript"></script>
<script>
// define event handlers
function handleFooEvent(eventDomTree) {
 // do some DOM manipulation
}

// register events
registerEventHandler("Foo", handleFooEvent);
registerEventHandler("Bar", handleBarEvent);
registerErrorEventHandler(handleErrorEvent);

// start up the queue
startQueue("QueueHandlerUrl", "RequestIdUrl");
</script>

In earlier attempts of implementing this, I tried to use a more object oriented fashion. However, since the xmlHttpRequest.onreadystatechange function cannot have parameters passed into it, things just ended up in global variables. The global variables in ajaxQueue.js are:

  • xmlHttp – an XmlHttpRequest object built based on the browser
  • eventHandlers – an associative array that maps event name to a handler
  • errorEventHandler – a function reference that gets called when there is an error
  • requestId – a unique number representing the requests made from the current page
  • queueUrl – the URL to the QueueServlet
  • queueTimeout – the time to sleep before the next queue query

The xmlHttp.onreadystatechange function is implemented something like this.

xmlHttp.onreadystatechange = function () {
   if (xmlHttp.readyState == 4) {
       if (xmlHttp.status == 200) {
           var events = xmlHttp.responseXML.getElementsByTagName("event");
           for (var i = 0; i < events.length; ++i) {
              var eventName = events[i].name
             if (eventName == "history") {
                  // special event to create a virtual entry in
                  // the history to handle the back button
                  doHistoryEvent(events[i]);
              }
              eventHandler = eventHandlers[eventName];
              if (eventHandler) {
                  eventHandler(events[i]);
              }
           }
           window.setTimeout("processQueue()", queueTimeout);
       } else {
           errorEventHandler(xmlHttp.statusText);
       }
   }
}

The startQueue function would look like this:

function startQueue(url, requestIdUrl, timeout) {
   if (xmlHttp == null) {
       // don't do anything if AJAX is not supported.
       return;
   }
   requestId = getRequestId(requestIdUrl);
   queueUrl = url;
   queueTimeout = timeout
   // create invisible iframe to handle history events
   createHistoryIFrame();

   processQueue();
}

The processQueue() function that sends the request to the servlet would look like this:

function processQueue() {
   xmlHttp.open("POST", queueUrl, true);
   xmlHttp.setRequestHeader("requestId", requestId);
   xmlHttp.send(null);
}

One way to further reduce the load on the servers is to alter the queueTimeout with a more intelligent algorithm rather than a fixed amount. This would have to be application specific though. Sending messages to the server Requests to the server side always need to have the “requestId” so they know what to put into the queue. And there will be several pieces of information that is going to get passed into the backend for each request. So the following function is provided:

function sendMessage(url, messageData) {
   if (xmlHttp == null) {
       // ensure that nothing happens if AJAX is not supported.
       return;
   }
   // create a new xmlHttpRequest for sending data
   var sendXmlHttp = createXmlHttpRequest();
   sendXmlHttp.open("POST", url);
   sendXmlHttp.setRequestHeader("requestId", requestId);
   sendXmlHttp.send(messageData);
   // error handling goes here.
}

The requests for the message sends are synchronous rather than asynchronous. This ensures that errors are captured properly if there are any. Putting events into the queue To put a message into the queue, the following code gets invoked:

AjaxQueue queue = null;
try {
 queue = new AjaxQueue(servletContext, request);
 queue.putEvent(e);
} finally {
 if (queue != null) {
   queue.close();
 }
}

Of course that code block is too big and there will be a lot of copy and paste if we just left it like that. So a utility class called AjaxQueueUtil with a method

AjaxQueueUtil.putEvent(ServletContext context,
 HttpServletRequest request,
 Event events...)

that sends a list of events into the queue. This is provided to shield users from copying and pasting so many lines of code. Conclusion As specified earlier the advtange to performing this approach is that it allows a separation of event handling from the actual page. The development of the AJAX enabled page would be similar to that of a standard GUI application. If the browser does not support AJAX or even scripting, its okay since the pattern is non-invasive to HTML and developers can AJAXify things as needed. This pattern takes advantage of facilities within the J2EE spec and supports clustered environments and multiple windows on the same session. A large chunk of the logic is hidden within the API implementation to make things easier for the developers. However, there is no concrete implementation of this pattern yet.

Input device rant

I really want to get a Mac laptop, I really do. But the fact that this is a track pad only annoys me. I wish they made a version that supports the TrackPoint or even the UltraNav (trackpoint + trackpad in IBM laptops) so I can disable the track pad portion of that stupid thing. Speaking of which, I wish IBM or someone made a keyboard with integrated trackpoint and has a normal keyboard layout. Some USB ports would be a nice addition too, but I still want a normal keyboard layout with my number pad and window keys. Alas, I am destined to wait out a bit longer.

However, in 2008 I did get my first personal laptop, a 13″ MacBook Pro.  Though I did miss the TrackPoint which eventually made me get a Lenovo T430 in 2012, the large TrackPad of the Mac combined with the Mac OS X gestures made it tolerable for most things.  However, PC laptops are switched over to trackpads and none of them ever made a good impression on me.

Personal blog of Archimedes Trajano