First, there are no clever ways for ClassLoader.
Second, in my conception, there are another term “asynchronous programming” besides “synchronous programming”.
Third, Java2Script compiler does perform very poorly at loading huge arsenal of SWT classes.
In the implementation of Java2Script ClassLoader (not full APIs implemented), it mainly supports three modes:
1. Asynchronous over SCRIPT tags (mainly in used)
2. Asynchronous over XMLHttpRequest (not used a lot)
3. Synchronous over XMLHttpRequest (seldom used),
Up until now, I seldom use ClassLoader.loadClass in synchronous XHR mode, as the browser may be locked. Instead, an asynchronous ClassLoader wrapper is introduced:
public class AClass {
// Java2Script compiler will generate different scripts without Thread for this method body
public static void load(final String clazzName, final Runnable afterLoaded) {
new Thread(new Runnable() {
public void run() {
try {
Class clz = Class.forName(clazzName);
if (afterLoaded instanceof ARunnable) {
ARunnable runnable = (ARunnable) afterLoaded;
runnable.setClazz(clz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
return ;
}
afterLoaded.run();
}
}).start();
}
}
Java2Script recommends using this kind of asynchronous ClassLoader, in which the compiler will modify AClass.load with JavaScript codes without java.lang.Thread but with asynchronous XMLHttpRequest or SCRIPT-tag class loading codes.
And now it comes to something about “Asynchronous Programming” v.s. “Synchronous Programming”, if we use asynchronous class loading.
We know that JavaScript does not support Thread like Java. But when you use Java, lots of Thread things will be introduced. For example, when you use GUI toolkit to open a dialog, there are threads running? already. But when we introduce UI widgets in JavaScript, we have no Threads. What we can use are asynchronous callbacks. And we do use use asynchronous callbacks a lot in XMLHttpRequest. So if we take asynchronous callbacks for granted, we should always considering “Asynchronous Programming” pattern. That is to say, when we know some calls are *time-consuming*, we should put following codes into a callback and let that method call to call back later. So RPC and Class.forName call should be wrapped into a callback by developer intentionally.
Here is an example for scenario of using asynchronous class loader. There is a page, with many tabs. And each tab will present a different thing (a different class). We need not to load all those tab classes in starting up. We just need to load that tab class only when user switches to that tab. When user switches, here asynchronous class loader should be used to load the tab page class:
tabFolder.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
//...
Object data = item.getData(); // class name actually
if (data != null) {
// ASWTClass#shellLoad is actually similar to AClass#load
ASWTClass.shellLoad(tabFolder.getShell(), (String) data, new ARunnable() {
public void run() {
try {
Constructor constructor = getClazz().getConstructor(new Class[] {ControlExample.class});
Object inst = constructor.newInstance(new Object[] {ControlExample.this});
Tab tab = (Tab) inst;
// ... do the following layout job
} catch (Throwable e) {
e.printStackTrace();
throw (Error) e;
}
}
});
}
}
});
Here when asynchronous class loader is loading classes, show some texts saying “fetching data …” should be fine.
The problem of *huge* number of HTTP round-trips is a *huge* problem of Java2Script compiler. So packed *.z.js, which is similar to *.jar, is introduced. First ClassLoader is designed to load class in per class mode but is also designed to load all related classes when one class is required. For example, when instantiating class A must require instantiating class B, then ClassLoader will try to load class B when class A is required, and only marks class A as loaded after class B or other classes are loaded. This may require algorithm to calculate class dependencies, which may be similar to GWT’s call graph. So if class A and class B have their relationship, we can pack A.js and B.js together to reduce the number of HTTP round-trips. In practical implementation of J2S’ SWT, ToolBar and ToolItem are packed as ToolBar.z.js, Shell.js, Decorations.js and Display.js are packed as Shell.z.js. This is a way Java2Script compiler trying to reduce *huge* number of HTTP round-trips. But as mentioned, the loading performance is not good enough. And the packing algorithm may require professional design besides compiler’s capabilities.
Actually, the original design was to pack most of? library *.js into some (less than 15 or 20) *.z.js files. And those *.z.js are static with different release versions. Other pages (or other websites’ pages) may load these static libraries when necessary. As different pages have common *.z.js URLs. There are no needs to download them again once they are already downloaded by other visited pages (Just like Flash is required to install once). And if the *.js is distributed on a fast network (like Google network or other distributed publishing network). In fact, maybe these library *.z.js are not too huge for such publishing and not small enough to be efficient libraries, as this design is not yet proved.
GWT’s *.js seems to be project-dependent. And different projects only share a bootstrap library and not share some big *.js libraries (But do share in Java source level). Am I right?
Currently Java2Script’s ClassLoader just ignore “Class not found” silently when SCRIPT-tag gets nothing. And this should be fixed. “Method not found” exceptions existed already before Java reflection was implemented.
As mentioned in my last post, I admired GWT team for the motivation of “to be as small and as efficient as possible” and “excellent 100k ~ 200k final *.js files with very good performance”. And I learned that two separate goals:
(1) Create highly optimized JS output
(2) Optionally, publish selected parts of your code with a JS-compatible API
For such design, without ClassLoader and Java reflection should be OK for me. But I am still wondering whether it’s a must feature or not for enterprise edition of GWT projects. OK, I am just wondering from my lazy perspective.
Maybe I should learn something from GWT’s motivation and goals. Maybe it’s direction for JavaScript, also for Java2Script. And may be it’s not. No matter what, I learned that my early (in May) understanding of GWT compiler was totally wrong. And thanks for Bruce, and thanks for this thread and all participators,? I have a better understanding of GWT compiler now.
By the way, about “Asynchronous Programming” v.s. “Synchronous Programming”, I think there are should be something inside. And I are wondering Java to JavaScript compiler should consider this seriously or not.