Category Archives: Browser

Introducing Java2Script’s Simple Pipe

Communications between server and browser are essential things in AJAX world.

Basic Knowledge

Each HTTP connection is forked by browser. And one HTTP connection may serve multiple sessions in HTTP 1.1 but server only one session in HTTP 1.0. There is no time limit or data transfer limit on each HTTP connection. This is basic knowledge of HTTP connections.

An HTML web page, may contains lots of resources, images, style sheets or JavaScript files, and each resources requires an HTTP session. And according technologies called AJAX, browser can load resources at any specific time.

What is Simple Pipe?

Simple Pipe is a kind of data transformation from server to browser. Once browser open up an HTTP connection to the server, the server keep the connection open. And whenever the server get data, it will flush data through the HTTP connection to browser. The data transfered through the connection is serialized and deserialized in SimpleSerializable format.

How to Setup a Simple Pipe?

Simple Pipe is currently designed for Java language. There is a class named “com.java2script.ajax.pipe.SimplePipeRequest” with a static method named “pipeRequest” which accept a parameter in type of “com.java2script.ajax.pipe.SimplePipeRunnable”. The SimplePipeRunnable is will accept parameters from browser side, and then be passed to server side (Tomcat or other Servlet containers), and its action will be executed. In most cases, an other-type connection is created, and listeners are added to the connections for up-coming events. And the connection will be registered with a generated pipe key. And the pipe key will be sent back to browser side. Browser side will create another connection to the server with the given pipe key. Server will check the pipe key, and hold the HTTP connection, and flush any data from previous registered connection to browser side in format of SimpleSerializable. Browser will use IFRAME to accept the data, and return back to object instances. In keeping the HTTP connection, browser will send a notifying signal (HTTP connection) to server to make sure that browser is keeping the connection live. If browser likes to close the pipe, it will send another Simple RPC call the server to notify server that pipe should be closed. If it happens that browser exit without notifying closing pipe, server will close the pipe in a minute or so, as there is no such signals for the pipe to be kept alive.

As Simple Pipe is a subset of Java2Script library API, Simple Pipe may only be used in Java language. But after being converted to JavaScript by Java2Script compiler, Simple Pipe technology can be used in JavaScript language. And JavaScript demo of Google Talk is an example of Simple Pipe.

Code Snippets

SimplePipeSWTRequest#swtPipe usage:

SimplePipeSWTRequest.swtPipe(new LoginRunnable() {

@Override
public void ajaxIn() {
username = userNameText.getText().trim();
password = passwordText.getText();
}

@Override
public void ajaxOut() {
if (failed) {
MessageBox messageBox = new MessageBox(MainWindow.this, SWT.ICON_ERROR);
// notify error
return;
}
setData("ConnectionKey", key);
setData("PipeKey", pipeKey);
// continue to login
}

@Override
public void ajaxFail() {
MessageBox messageBox = new MessageBox(MainWindow.this, SWT.ICON_ERROR);
// .. notify errors
}

public void deal(PresenceSerializable ps) {
// Data received from pipe! To update presence status
}

@Override
public void deal(final MessageSerializable ms) {
// Data received from pipe! To popup chatting dialog...
}

@Override
public void deal(RosterSerializable rs) {
// Data received from pipe! To update roster entries...
}

});

LoginRunnable

public class LoginRunnable extends SimplePipeRunnable {

public static class PresenceSerializable extends SimpleSerializable {
public String name;
public String email;
public String status;
public String type;
public String mode;
}

public static class MessageSerializable extends SimpleSerializable {
public String from;
public String body;
public String to;
}

public static class RosterSerializable extends SimpleSerializable {
public String from;
}

public String username;

public String password;

public String host;

public int port;

public String service;

public String key;

public boolean failed;

@Override
public String getHttpURL() {
return TalkRunnble.TALK_URL_BASE + "simplerpc";
}

@Override
public String getPipeURL() {
return TalkRunnble.TALK_URL_BASE + "simplepipe";
}

/**
* TODO: [email protected] #pipeSetup()} should be ignored by Java2Script compiler
* by default.
* @j2sIgnore
*/

@Override
public void pipeSetup() {
failed = false;
JabberHelper instance = JabberHelper.getInstance();
key = instance.login(username, password, host, port, service);
if (key == null) {
failed = true;
return;
}
new Thread(new Runnable() {

public void run() {
while (true) {
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
//e.printStackTrace();
}
if (!isPipeLive()) {
pipeDestroy();
break;
}
}
}

}, "Jabber Connection Monitor").start();

XMPPConnection conn = instance.getConnectionByKey(key);

Presence presence = new Presence(Presence.Type.available);
conn.sendPacket(presence);

//XMPPConnection conn = JabberHelper.getInstance().getConnectionByKey(key);
conn.addPacketListener(new PacketListener() {

public void processPacket(Packet packet) {
pipeThrough(packet);
}

}, null);
}

/**
* @j2sIgnore
*/

@Override
public boolean isPipeLive() {
JabberHelper instance = JabberHelper.getInstance();
XMPPConnection conn = instance.getConnectionByKey(key);
return super.isPipeLive() && conn != null && conn.isConnected()
&& !instance.isConnectionLost(key);
}

/**
* @j2sIgnore
*/

@Override
public void pipeDestroy() {
JabberHelper instance = JabberHelper.getInstance();
instance.logout(key); // try to logout
}

/**
* @j2sIgnore
*/

@Override
public void keepPipeLive() {
JabberHelper instance = JabberHelper.getInstance();
instance.update(key);
}

/**
* TODO: [email protected] #through(Object...)} should be ignored by Java2Script
* compiler by default.
* @j2sIgnore
*/

public SimpleSerializable[] through(Object... args) {
if (args != null && args.length > 0) {
Packet packet = (Packet) args[0];
SimpleSerializable[] ss = new SimpleSerializable[1];
if (packet instanceof Presence) {
Presence presence = (Presence) packet;
PresenceSerializable ps = new PresenceSerializable();
ps.email = presence.getFrom();
Mode mode = presence.getMode();
ps.mode = mode == null ? null : mode.name();
Type type = presence.getType();
ps.type = type == null ? null : type.name();
ps.status = presence.getStatus();
ss[0] = ps;
return ss;
} else if (packet instanceof Message) {
Message message = (Message) packet;
MessageSerializable ms = new MessageSerializable();
ms.from = message.getFrom();
ms.body = message.getBody();
ms.to = message.getTo();
ss[0] = ms;
return ss;
} else if (packet instanceof RosterPacket) {
RosterPacket roster = (RosterPacket) packet;
RosterSerializable rs = new RosterSerializable();
rs.from = roster.getFrom();
roster.getType();
ss[0] = rs;
return ss;
}
}
return null;
}

public void deal(PresenceSerializable ps) {
// To be override
}

public void deal(MessageSerializable ms) {
// To be override
}

public void deal(RosterSerializable rs) {
// To be override
}

}

SimplePipeRunnable

public abstract class SimplePipeRunnable extends SimpleRPCRunnable {

/**
* Pipe's id
*/

public String pipeKey;

private boolean pipeAlive;

private SimplePipeHelper.IPipeThrough helper;

/**
*
* @param helper
* @j2sIgnore
*/

void setPipeHelper(SimplePipeHelper.IPipeThrough helper) {
this.helper = helper;
}

public String getPipeURL() {
return "simplepipe"; // url is relative to the servlet!
}

public String getPipeMethod() {
return "GET";
}

@Override
public void ajaxRun() {
pipeKey = SimplePipeHelper.registerPipe(this);
if (pipeKey != null) {
pipeSetup();
pipeAlive = true;
} else { // failed!
pipeAlive = false;
}
}

/**
* Listening on given events and pipe events from Simple RPC to client.
*/

public abstract void pipeSetup();

/**
* Destroy the pipe and remove listeners.
* After pipe is destroyed, [email protected] #isPipeLive()} must be false
*/

public abstract void pipeDestroy();

/**
* Return whether the pipe is still live or not.
* @return pipe is live or not.
*/

public boolean isPipeLive() {
return pipeAlive;
}

/**
* Notify that the pipe is still alive.
*/

public void keepPipeLive() {
// to be override
}

/**
* Update pipe's live status.
*
* @param live if live is true, just notify the pipe is still alive. if live is false
* and [email protected] #isPipeLive()} is true, [email protected] #pipeDestroy()} will be called.
*/

protected void updateStatus(boolean live) {
if (live) {
keepPipeLive();
pipeAlive = true;
} else if (isPipeLive()) {
pipeDestroy();
pipeAlive = false;
}
}

/**
* Convert input objects into SimpleSerializable objects.
*
* @param args
* @return SimpleSerializable objects to be sent through the pipe.
*/

public abstract SimpleSerializable[] through(Object ... args);

public void deal(SimpleSerializable ss) {
try {
Class<? extends SimpleSerializable> clazz = ss.getClass();
if ("net.sf.j2s.ajax.SimpleSerializable".equals(clazz.getName())) {
System.out.println("Default!");
}
Method method = null;

Class<?> clzz = getClass();
String clazzName = clzz.getName();
int idx = -1;
while ((idx = clazzName.lastIndexOf('$')) != -1) {
if (clazzName.length() > idx + 1) {
char ch = clazzName.charAt(idx + 1);
if (ch < '0' && ch > '9') { // not a number
break; // inner class
}
}
clzz = clzz.getSuperclass();
if (clzz == null) {
break; // should never happen!
}
clazzName = clzz.getName();
}
if (clzz != null) {
method = clzz.getMethod("deal", clazz);
if (method != null) {
method.invoke(this, ss);
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
// default
System.out.println("Default!");
}

/**
* A method used to pipe a bundle of instances through.
*
* Attention: Only visible inside [email protected] #pipeSetup()}.
* @param args
* @j2sIgnore
*/

protected void pipeThrough(Object ... args) {
SimplePipeRunnable pipe = SimplePipeHelper.getPipe(pipeKey);
if (pipe == null) return;
SimpleSerializable[] objs = pipe.through(args);

if (objs == null || objs.length == 0) return;

if (pipe instanceof SimplePipeRunnable) {
SimplePipeRunnable pipeRunnable = (SimplePipeRunnable) pipe;
if (pipeRunnable.helper != null) {
pipeRunnable.helper.helpThrough(pipe, objs);
return;
}
}
for (int i = 0; i < objs.length; i++) {
pipe.deal(objs[i]);
}
}

}

Posted in Architecture, Browser, JavaScript | 247 Comments

Browser Statistics Among Web Developers

Here are some numbers about browsers used among web developers:

1. Firefox 54.63%
2. Internet Explorer 38.10%
3. Opera 3.84%
4. Safari 1.56%
5. Mozilla 1.34%

Firefox versions:
1. 2.0.0.3 50.74%
2. 2.0.0.4 30.25%
3. 1.5.0.11 6.05%
4. 1.5.0.12 3.65%
5. 2.0.0.2 1.88%
6. 2.0.0.1 1.26%
7. 2.0 1.26%
8. 1.0.7 1.26%

Internet Explorer versions:
1. 6.0 67.27%
2. 7.0 32.65%

BTW: From the latest 15 days, more and more people update their Firefox to 2.0.0.4/1.5.0.12:
1. 2.0.0.4 71.76% (up)
2. 2.0.0.3 11.11%
3. 1.5.0.12 8.10% (up)
4. 2.0.0.2 1.44%

Posted in Architecture, Browser | 1 Comment