Performance Matters or Not

Joel Spolsky had a post “Strategy Letter VI“. And I did agree with him to some degree when reading the post. And I decided to write a post about these opinions related to Java2Script later in the evening. And later in evening I found that Ajaxian’s Ben Galbraith posted his opinions but with title “Go Ajax, Young Man“. Well, the title said something already.

Here follows the Java2Script performance issues and SDK concerns.

Right now, performance is a really big matter to Java2Script. But does performance matter a lot in the future? That is a question.

Java2Script was open sourced in late of 2005. And now, 2 years has passed. And Firefox 2.0 is already released, while 2 year ago, it was still in its 1.0. And I could tell that the browser is improved a lot. OK, as I have paid lots of efforts in improving Java2Script performance, I think I would rather bet on the improvement of browser, the improvement of JavaScript engine (such as upgrading to Adobe’s script engine), upgrading of CPU and machine.

About performance, maybe a lot of developers complain that Java is slow. In fact, a lot of developer complains that Eclipse, which is Java based, is very slow and could not stand for it. But Java is still a language used by most of the developers in the last 4 or 5 years. And JavaScript is a language that is much slower than other language. But in recent three years, it is getting hotter and hotter in the format of AJAX development. No matter how slow it is.

Back to Java2Script. Java2Script compile Java sources to JavaScript, and Java2Script supports a port of desktop SWT library in JavaScript besides supporting most common java.* classes. The compilation from Java to JavaScript is done in a Java-way, that is to say, all information in Java sources are preserved in JavaScript, so lots of Java information and features are reserved, including Java reflection. And SWT library is considered a heavy weight library for JavaScript, as it was once designed for desktop application, not for web application.

Now come to comparison of Google Web Toolkit (GWT). GWT is a similar project when comparing to Java2Script. It contains a Java to JavaScript compiler, and supports a set of basic java.* libraries and a set of UI library. But the compilation of Java to JavaScript is not in the same way of Java2Script. It is in a C-way. It links all related JavaScript into a big JavaScript file. And those unrelated (unused) JavaScript are left without packing into the big JavaScript file. So you can not retrieve the Java class information from the final *.js file. But as compiling in C-way, the final *.js is a lot smaller than Java2Script’s *.js. And it also runs a lot faster than Java2Script’s. And the UI library of GWT is a new set of APIs. It is designed for web applications in browser only. So it is more browser-oriented and much more light weighted. In such a way, GWT’s performance is far faster than Java2Script. So few people cares about GWT’s performance.

In some simple words:
Java2Script is not optimized with more heavy weight SDK APIs
GWT is optimized in compiling with a light weight SDK APIs.

In fact, GWT is 100 times more popular than Java2Script at this writing time. So that means performance do matters. But what about 2 more years later? Is there any possibilities that GWT’s APIs is out and GWT’s early performance optimization is lagged behind the improvement of hardware and browser? Maybe later, AJAX developments prove that those optimized compiled *.js is not the trends, and web applications oriented UI APIs is not the trends for richer applications. But no one knows now. So as Ben Galbraith said:

it’s hard to dispute that in a year or two, JavaScript will be much, much faster.

BTW: Java2Script 1.0.0 is to be released by the end of this September, even though performance is still a big problem (but not a huge problem now).

For more discussions, you may want to read early Java to JavaScript compiler discussions

Posted in Architecture, GWT, Hacks, JavaScript, SWT | 10 Comments

Performance of Comet and Simple Pipe

About one and a half months ago, I introduced Java2Script Simple Pipe, another Comet implementation. At that time, I knew there were lots of performance issues on such Comet technology. Because at that time I were busy and it was not a urgent deal for me, I didn’t write test codes to test out the performances at that moment.

Only recently, I took some tests on performance issues. And the results said Simple Pipe’s Comet implementation performs really bad!

Opening 10 Comet connections using Simple Pipe does not affect the whole server. But when I opened 20 Comet connections, the CPU usage is up to 100% quickly! It’s not a problem of the application logics. It is a matter of Comet connection.

Java2Script Simple Pipe supports switching pipe mode into query mode. It is very easy, just call a static method:

SimplePipeRequest.switchToQueryMode(250);

After switching to query mode for the pipe, I can open up to 100+ connections to the server to receive stream data back to browser. The peak of CPU usage is about 60% ~ 70%. The average CPU usage may be about 20%-30%. It is an acceptable technology with such performances under the pressure tests.

( I did not test those server with special Comet optimizations. I just tested Simple Pipe on Apache Tomcat 5.5. Maybe those Comet optimized should have a better results. )

Posted in Architecture, Articles, Comet, JavaScript | 4 Comments

Java != JavaScript

The cry-out of “Java != JavaScript” is not a new expression. I learned it in year 2001 when I started learning JavaScript.

But as “Java2Script” project, it links “Java” and “JavaScript” together by a Java to JavaScript compiler. It is unavoidable to explain these two languages in Java2Script manner before making senses.

“Java” is a word for “OO” (Object Oriented) concept in the eyes of Java2Script. C++ is considered not a pure OO language, but C# may be. And there are other OO languages. But the well-known one should be Java. Java2Script just takes Java as a great OOAD tool (language tool) to analyze, design and implement desktop applications or rich client platform (RCP).

Naming ECMAScript as JavaScript is big mistakes for early Netscape developer. But we already accepted it. Even though lots of JavaScript syntaxes are similar to Java, it does not matter that JavaScript is a different language. And ActionScript, which is used in Flash, is another script language which is based on ECMAScript. It seems that few cares about comparison between Java and ActionScript. Whatever language is, Java2Script developers need tools to design rich internet applications (RIA).

On fields of language conversion, you may already know that GWT is also a Java to JavaScript compiler that links together these two languages. And I also know that there are other languages to JavaScript compilers. For example, C# to JavaScript compiler ( http://jsc.sourceforge.net/ ), and Java to ActionScript ( http://osflash.org/j2as )

… // more needs to to be explained

In some simple words, “Java” and “JavaScript” is not two languages but two tools (or tool sets) in the opinions of Java2Script. By Java2Script compiler, we can use these two tools to develop once but get both RCP and RIA at the same time. That is the key advantage of Java2Script.

We do not care what languages are used. We know we use OO concepts to design things and get what we want by using the existed tools.

Posted in GWT, Java, JavaScript | 7 Comments

JavaScript’s Defects on Numbers

Number larger than 0×20000000000000 (54bit) will not be reliable.

Here is the test script:

javascript:alert (0x1ffffffffffffe);
javascript:alert (0x1fffffffffffff);
javascript:alert (0x20000000000000);
javascript:alert (0x20000000000001);

You can see that the latter two numbers are same number in JavaScript!

Conclusion

The safe number range for integer is “-0x1fffffffffffff – 0x1fffffffffffff” (53bit). Therefore, in Java to JavaScript world, conversion of Java’s long number to JavaScript’s Number may result in incorrect calculations.

Posted in JavaScript | 26 Comments

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 | 96 Comments

Demo: Java2Script Google Talk in SWT

Pre-words

In March, I wrote “Java2Script version of Google Talk”. It was a very simple SWT application that talks to talk.google.com using Jabber XMPP protocol by Smack library.

About 3 months later, I rewrote the whole Java2Script version of Google Talk. In this time, I still used Smack library. But I used Instantiations’ Windows Builder Pro to help me to design the whole interface. And this time, it copied all existed UI from the desktop version of Google Talk, including every dialogs, menus or layouts. It looks almost exactly the same of Google Talk.

Here is the demo addres: http://demo.java2script.org/gtalk/

P.S. Other available web IM services provided by this Java2Script technologies:

Here are some screenshots:

Click for Large Screenshot: Google Talk Client’s JavaScript Copy

Requirements

It is just trying to re-implement all the features of Google Talk except the voice talk. And all dialogs or layouts are copied from Google Talk, as I thought that Google Talk is excellent.

Architecture

Jabber Server (talk.google.com) –> XMPP –> Tomcat Server (Smack) –> mod_jk Connector –> Apache HTTP Server –> Browser Client

Implementation

Java2Script’ Simple RPC and Simple Pipe were used in the Java2Script Google Talk. Simple RPC serializes requests from browser to server, and server will parse them and send out related XMPP requests. All packets received from Jabber server are piped through Simple Pipe to the browser. The Simple Pipe just serializes XMPP packets into SimpleSerializable instances, and sends out through the comet HTTP connection.

That is to say, Simple RPC is OutputStream and Simple Pipe is InputStream in Java’s terms. And all data are serialized or deserialized by SimpleSerializable.

Optimization

JavaScript and DOM manipulation is very slow actually. And loading bundles of *.js from server to browser also requires a very long waiting. In order to improve the performances, lazy loading technologies are applied.

First, lazy loading those *.js related to the UI until the UI is required. For example, chatting dialog related classes’ *.js only be loaded when user open a chatting dialog or a message is received. This principles are also applied to settings dialogs, inviting friends dialogs and others. To lazy loading *.js, AClass or ASWTClass is introduced in Java2Script. It will load classes only when needed. The usage of A/SWT/Class is as following:

ASWTClass.shellLoad ("org.java2script.....", new ARunnable () {
public void run() {
// begin to later action.
});

Second, some part of codes are executed lazily. For example, create menus may require a lot of CPU times for browser. So only before the menus are to be shown do the menus are being created. In such a way, only what you see on the UI is created. Or they won’t be created by default.

Third, virtual list is used to avoid large friends list updating. For example, if there are more than 200 friends on one’s Google Talk friends list and concrete SWT widgets are created according to each friends, there will be 200 widgets being created. And it will freeze the whole browser UI. And when the dialogs are resized, re-layout these hundreds of widgets may also freeze browser. To avoid such freezing, virtual list is introduced. The virtual list is that only visible list item are created. Those invisible items are never created. In such a way, 500+ friends are supported.

Posted in Demo, SWT | 1,345 Comments

Regarding GWT

You must already know GWT if you learn Java2Script by “Java to JavaScript compiler”.

I seldom mentioned GWT in “Java2Script” topics in the past, except an article “GWT v.s. Java2Script SWT” in early days when GWT was announced.

But late these days, I think if I compare more details between GWT and Java2Script, it may help new developers to get further understanding of both GWT and Java2Script.

Here is a list of similarities for GWT and Java2Script:

  1. Java to JavaScript compiler
  2. Widget components (GWT v.s. SWT)
  3. Support RPC (GWT’s RPC v.s. Java2Script’s Simple RPC)
  4. Support Eclipse

I think later I will discuss more details (similarities and differences, advantages and disadvantages) on the above list.

Posted in Architecture, GWT | 15 Comments

Asynchrony is Everything

You may know “Position is Everything” already. And I believe you should also know “Asynchony is Everything” or “Everything is Asynchronous” in RIA development.

Asynchrony is not only very important in web application development but also vital in normal desktop applications and embed systems. Asynchronous programming may help a lot in improving user experience. In normal desktop applications, if the main thread blocks UI for its long-time-consuming computing, users will feel desperately, waiting the busy-cursor to turn into default. But if such computing is run in background, user would find other things to do, such as switching focuses or typing something.

What about the definition of Asynchronous Computing? It is not only about asynchronous loading or asynchronous remote procedure call (RPC), it is also about local calculating. One thing should be mentioned here is UI layout computing. Lots of UI need complex layout calculating. For example, rendering a web page, rendering a 3D interfaces, or rending a complex dialog box. This CPU times required is especially obvious when it’s performed in a slow computer or in a slow computing environment, such as JavaScript runtime in browsers.

Here, I concerns about asynchronous layout. In implementing Java2Script’s SWT library, there is a huge problem for us. JavaScript is so slow that it can not finish a complex layout in less than 5 seconds. Or a complex layout requires more than 1 second will freeze browser UI, which make the user experience very bad and unacceptable. In order to overcome such problems. Java2Script introduced “Asynchronous Layout”.

Asynchronous layout is about to split the layout jobs into small pieces of jobs and run them step by step. As you may know, in SWT, all widgets are placed inside some containers. And to make a layout over container, the bounds of parent container, which is a Composite widget, is calculated first. And then the bounds will be passed as constraints of its child widgets to make children layouts. In such layout algorithms, we could split the layout jobs into parent container layout and child widget layout jobs, queue these jobs, and monitor each job’s required CPU times to avoid complex layout takes more than 200ms. If layout computing takes more than 200ms, then sleep for about 50ms before calling the next layout job.

Here are some snippets about Asynchronous Layout:

Display#readAndDispatch

public boolean readAndDispatch () {
checkDevice ();
drawMenuBars ();
runPopups ();
/*
if (OS.PeekMessage (msg, 0, 0, 0, OS.PM_REMOVE)) {
if (!filterMessage (msg)) {
OS.TranslateMessage (msg);
OS.DispatchMessage (msg);
}
runDeferredEvents ();
return true;
}
return runAsyncMessages (false);
*/

if (messageProc != 0) {
return true; //already hooked, return directly
}
messageProc = window.setInterval(new RunnableCompatibility() {
private boolean messageLoop = false;
public void run() {
runPopups ();
MESSAGE[] msgs = Display.this.msgs;
if (msgs.length == 0 && messageLoop)
/**
* @j2sNative
* var layoutFinished = window["j2s.swt.shell.finish.layout"];
* if (layoutFinished != null) {
* layoutFinished ();
* }
* this.messageLoop = false;
*/
{ }
if (msgs.length != 0) {
messageLoop = true;
MESSAGE[] defered = new MESSAGE[0];

int defsize = 0;
for (int i = msgs.length - 1; i >= 0; i--) {
MESSAGE m1 = msgs[i];
if (m1 == null) {
continue;
}
m1.defer = false;
for (int j = i - 1; j >= 0; j--) {
MESSAGE m2 = msgs[j];
if (m2 != null && m2.control == m1.control
&& m2.type == m1.type) {
msgs[j] = null;
}
}

if(m1.type == MESSAGE.CONTROL_LAYOUT){
if(m1.control.parent != null && m1.control.parent.waitingForLayout){
m1.defer = true;
defered[defsize++] = m1;
}
}

}
long time = 0;

for (int i = 0; i < msgs.length; i++) {
MESSAGE m = msgs[i];

if(m != null && m.defer){
continue;
}
msgs[i] = null;
if (m != null && m.type == MESSAGE.CONTROL_LAYOUT) {
m.control.waitingForLayout = false;
if (!m.control.isVisible()) { continue; }
Date d = new Date();
Composite c = (Composite) m.control;
if(c.waitingForLayoutWithResize){
c.setResizeChildren (false);
}
if(c.layout != null){
c.layout.layout (c, (c.state & Composite.LAYOUT_CHANGED) != 0);
c.state &= ~(Composite.LAYOUT_NEEDED | Composite.LAYOUT_CHANGED);
}
if(c.waitingForLayoutWithResize){
c.setResizeChildren (true);
c.waitingForLayoutWithResize = false;
}

if (m.data != null) {
boolean[] bs = (boolean[]) m.data;
c.updateLayout(bs[0], bs[1]);
} else {
c.layout();
}
time += new Date().getTime() - d.getTime();
if (time > 200) {
for (int j = i + 1; j < msgs.length; j++) {
msgs[j - i - 1] = msgs[j];
}
int length = msgs.length - i - 1;
for(int j = 0; j < defsize; j++){
msgs[length + j] = defered[j];
}
/**
* @j2sNativeSrc
* msgs.length -= i + 1;
* @j2sNative
* a.length -= f + 1;
*/
{}
return ;
}
}
}
/**
* @j2sNativeSrc
* msgs.length = 0;
* @j2sNative
* a.length = 0;
*/
{}
Display.this.msgs = defered;
// for(int j = 0; j < defsize; j++){
// msgs[j] = defered[j];
// }
}
}
}, 100);
return true;
}

Composite#updateLayout

void updateLayout (boolean resize, boolean all) {
if (isLayoutDeferred ()) return;
if ((state & LAYOUT_NEEDED) != 0 && !waitingForLayout) {
// boolean changed = (state & LAYOUT_CHANGED) != 0;
// state &= ~(LAYOUT_NEEDED | LAYOUT_CHANGED);
// if (resize) setResizeChildren (false);
// layout.layout (this, changed);
// if (resize) setResizeChildren (true);
this.waitingForLayout = true;
this.waitingForLayoutWithResize = resize;
display.sendMessage(new MESSAGE(this, MESSAGE.CONTROL_LAYOUT, new boolean[] {resize, all}));
}

if (all) {
Control [] children = _getChildren ();
int length = children.length;
for (int i=0; i<length; i++) {
// children [i].updateLayout (resize, all);
if (children[i] instanceof Composite) {
display.sendMessage(new MESSAGE(children[i], MESSAGE.CONTROL_LAYOUT, new boolean[] {resize, all}));
}
}
}
}

Control#setBounds:

void setBounds (int x, int y, int width, int height, int flags, boolean defer) {
/**
* A patch to send bounds to support mirroring features like what Windows have.
*/

boundsSet = true;
int tempX = x;
if(parent != null){
if((parent.style & SWT.RIGHT_TO_LEFT) != 0){
x = Math.max(0, parent.getClientArea().width - x - width);
}
}
Element topHandle = topHandle ();
if (defer && parent != null) {
forceResize ();
WINDOWPOS [] lpwp = parent.lpwp;
if (lpwp == null) {
/*
* This code is intentionally commented. All widgets that
* are created by SWT have WS_CLIPSIBLINGS to ensure that
* application code does not draw outside of the control.
*/

// int count = parent.getChildrenCount ();
// if (count > 1) {
// int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
// if ((bits & OS.WS_CLIPSIBLINGS) == 0) flags |= OS.SWP_NOCOPYBITS;
// }
if ((width != this.width || height != this.height)
&& this instanceof Composite) {
display.sendMessage(new MESSAGE(this, MESSAGE.CONTROL_LAYOUT, null));
}
this.left = x;
this.top = y;
this.width = width;
this.height = height;
SetWindowPos (topHandle, null, x, y, width, height, flags);
} else {
int index = 0;
while (index < lpwp.length) {
if (lpwp [index] == null) break;
index ++;
}
if (index == lpwp.length) {
WINDOWPOS [] newLpwp = new WINDOWPOS [lpwp.length + 4];
System.arraycopy (lpwp, 0, newLpwp, 0, lpwp.length);
parent.lpwp = lpwp = newLpwp;
}
WINDOWPOS wp = new WINDOWPOS ();
wp.hwnd = topHandle;
wp.x = x;
wp.y = y;
wp.cx = width;
wp.cy = height;
wp.flags = flags;
lpwp [index] = wp;
}
} else {
if ((width != this.width || height != this.height)
&& this instanceof Composite) {
display.sendMessage(new MESSAGE(this, MESSAGE.CONTROL_LAYOUT, null));
}
this.left = x;
this.top = y;
this.width = width;
this.height = height;
SetWindowPos (topHandle, null, x, y, width, height, flags);
}
/*
* The x coordination should be preserved, because the right to left emulation is just
* for the view, not the data!
*/

this.left = tempX;
}

For more details about Java2Script’s SWT Asynchronous Layout implementation, please check the sources history from SVN repository at http://j2s.svn.sourceforge.net/

Posted in Architecture, Hacks | 1 Comment

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 | Leave a comment

QoS of JavaScript

You must be aware of QoS when you are writing Rich Internet Application (RIA), especially when you are using AJAX. Why? And what aspects should be considered in QoS of RIA?

Here are some points:

1. Loading JavaScript file *.js by <script> tag may fail without acknowledge by the following JavaScript, which may result in a total page error;
2. Loading CSS style file *.css by <link> tag may fail, which results in ugly page layout;
3. Loading images by <img> tag may fail, which may confuse users;
4. …

The above scenarios do not occurs often in development period. Because you may using local HTTP server to serve resources, or you may have high bandwidth with slow latency. But if you deploy your applications to server, your server may be busy all the time to serve resources, and will fail to serve some resources at some uncertain time. And people may report such bugs if they are in beta tests, but people may lose their deals or money if such failures happen in real online.

So, QoS is required.

How to make sure the RIA’s qualities? First, make sure that such HTTP timeouts or failures does not interrupt normal business logics. To do so, try reload resources if such failures are detected. It would be easy to reload *.js file by <script> tag, or other resources by other tags. In order to detect failures, you may have your own loader. This is the reason why there is ClassLoader in Java2Script. To detect failures is also easy. Just hook the onerror or onreadystatechange event of loading JavaScript. Or you can setup a thread looping to check whether a *.css or an image is loaded or not.

There are other QoS aspects. Stay tuned for more coming discussions.

Posted in Architecture, Hacks, JavaScript | 6 Comments