SourceForge.net Logo
   
Introduction

Welcome to the demo page of Liweb 0.1.0!

Liweb is a web application framework which let's you develop AJAX web applications using your favorite Java development tools - including debugging tools as well.

Please note: this page is best viewed using Firefox 2/3 because it includes demo applications from the development version of Liweb. (Most demos run in IE 6/7 as well but they are slower than in Firefox. Firefox 3 beta 2 is preferred over Firefox 2.)

At first sight Liweb is very similar to GWT:

  • The application is developed entirely in Java, no HTML and Javascript programming is required (in an optimal case).
  • The application is first downloaded to the web browser, then it runs without the traditional request-response cycle. It communicates with the server side using an RPC (remote procedure call) mechanism.

But this is only the surface, the key differences are:

GWT Liweb
Compiles Java source code to Javascript source code, so:
  • the Java source code must be available
  • (currently) it is bound to the Java language
  • the Java language support depends on the compiler which supports Java 1.4 at the time of this writing, so advanced features of Java 5 (like generics, autoboxing) are not available
  • compilation is part of the build process
  • a limited subset of the core API classes are available for the programmer using emulation
  • the Java to Javascript compiler is very efficient
Executes JVM bytecode:
  • only the compiled version of the program is required, the source code is not
  • thoretically it can run any software written in any programming language which is compiled to JVM bytecode (of course with some important limitations);
    some of such popular languages are Groovy and Ruby
  • it can support advanced features like the latest features of the Java language (like generics, autoboxing), AOP, etc.
  • compilation is done at runtime, not at build time
  • a limited subset of the core API classes are available for the programmer using direct execution instead of emulation
  • experimental (naive) implementation of long arithmetic is available so partial support for complex API classes like BigDecimal or BigInteger are available as well
  • the bytecode emulation (even with the planned enhancements) will always be slower than direct compilation
In development mode (hosted mode) a special version of Firefox is used for debugging which runs using a special "shell" application. In development mode LiveConnect and RMI is used for debugging.
Running the application in development or production mode is only a matter of configuration (switching a configuration value between true and false) which makes the build and development environment simpler.

Please help us in discussing, designing, implementing, testing or using the framework.
Contact the main developer of Liweb directly or use the services of the project's site on SourceForge.net if you are interested.

FAQ
1. What is Liweb?
Liweb is a suite of software components allowing compiled Java code to be executed in modern web browsers using the browser's Javascript infrastructure. Liweb executes JVM bytecode which has some important advantages:
  • it is not necessarily bound to the Java language
  • the JVM specification changes less frequently than the Java language itself

Of course nothing is for free:
  • emulating the JVM infrastructure is expensive
  • the compiled bytecode representation of a Java program is not as compact as its source code representation, so the generated Javascript code is larger and slower as well

Liweb allows you to develop your applications using your choice of Java development tools.
2. How does it work?
The core component (implemented in Javascript) emulates a subset of the JVM's infrastructure with the following main functionalities:
  • unified model of classes, methods and fields
  • infrastructural services like JDK proxy creation, String intern, etc.
  • Java-Javascript access layer which enables two-way communication between the emulated Java environment and the Javascript environment of the web browser

Another main component is a pluggable JVM bytecode executor. Currently the following executors are implemented:
  • native executor (the bytecode is executed in a real JRE)
  • interpreted executor implemented in Java (used for testing)
  • interpreted executor implemented in Javascript
  • (a compiling executor is under development)
3. Isn't it too slow?
The interpreted implementations are slow, they are not appropriate for real world usage (although they perform surprisingly well in Firefox 3 beta 2). They are mainly created for reference implementations before a faster compiling executor is implemented. But this compiling executor will be surely slower than direct Java to Javascript compilers. The entire system is in a very early stage, so there is very much room for optimization in the core components as well (long arithmetic, proxy handling, method invocation, etc.).
4. What is development mode?
In development mode the Java code is executed in a real JRE, so it can be debugged in the usual way in your favorite IDE. Development mode is based on LiveConnect and RMI, so it can run in any browser which supports them (FireFox 2 and IE 7 are currently tested).
5. Which subset of the JVM is supported?
Currently only Sun JRE 1.6 is supported.
The most interesting supported features:
  • almost full bytecode support
  • long arithmetic (with naive and slow implementation)
  • JDK proxies
The following features of the JVM are unsupported:
  • threading and related services (eg. monitors, locks, etc.)
  • native methods
6. Which web browsers are supported?
The development is currently focusing on Firefox 2/3 and IE 7. The final version will support all popular modern web browsers.
7. How can I help?
You can help in discussing, designing, implementing, testing or using the framework.
Please contact the main developer of Liweb directly or use the services of the project's site on SourceForge.net.
8. Under which license is Liweb distributed?
Liweb is currently distributed under GPLv3.
The licensing policy may change later based on the feedback of the users (see the GPL FAQ if you have questions).
The copyright owner of Liweb is Norbert Sándor.
Documentation

Documentation is in a very early stage but it is sufficient for quick-start.

Please note that the documentation is not GPLed but is provided only for personal use, modification and redistribution is not allowed.
Copyright 2007-2008 Norbert Sándor, all rights reserved.

PDF version
HTML version

Sample: Fibonacci numbers

Calculates the selected fibonacci number and displays it in a message box.


 

Java code:
public static int fibonacci(int n)
{
    return n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}
Javascript code to calculate and display fibonacci(15):
alert(bce.invokeMethod('com.erinors.bce.demo.util.Math', 'fibonacci', 15));
Sample: Calculate Pi

Calculate the value of Pi using a sample code from the Java Tutorial.
The source code is used without any modification!

 

Java code:
/** constants used in pi computation */
private static final BigDecimal FOUR = BigDecimal.valueOf(4);

/** rounding mode to use during pi computation */
private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN;

/**
 * Compute the value of pi to the specified number of digits after the decimal point.
 * The value is computed using Machin's formula:
 * 
 * pi/4 = 4*arctan(1/5) - arctan(1/239)
 * 
 * and a power series expansion of arctan(x) to sufficient precision.
 */
public static String computePi(int digits)
{
    int scale = digits + 5;
    BigDecimal arctan1_5 = arctan(5, scale);
    BigDecimal arctan1_239 = arctan(239, scale);
    BigDecimal pi = arctan1_5.multiply(FOUR).subtract(arctan1_239).multiply(FOUR);
    return pi.setScale(digits, BigDecimal.ROUND_HALF_UP).toPlainString();
}

/**
 * Compute the value, in radians, of the arctangent of the inverse of the supplied integer
 * to the specified number of digits after the decimal point.
 * The value is computed using the power series expansion for the arc tangent:
 * 
 * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + (x^9)/9 ...
 */
private static BigDecimal arctan(int inverseX, int scale)
{
    BigDecimal result, numer, term;
    BigDecimal invX = BigDecimal.valueOf(inverseX);
    BigDecimal invX2 = BigDecimal.valueOf(inverseX * inverseX);

    numer = BigDecimal.ONE.divide(invX, scale, roundingMode);

    result = numer;
    int i = 1;
    do
    {
        numer = numer.divide(invX2, scale, roundingMode);
        int denom = 2 * i + 1;
        term = numer.divide(BigDecimal.valueOf(denom), scale, roundingMode);
        if ((i % 2) != 0)
        {
            result = result.subtract(term);
        }
        else
        {
            result = result.add(term);
        }
        i++;
    }
    while (term.compareTo(BigDecimal.ZERO) != 0);

    return result;
}
Javascript code to compute Pi with 15 decimal precision:
alert(bce.invokeMethod('com.erinors.bce.demo.util.Math', 'computePi', 15));
Sample: BigInteger arithmetic

Simple arbitrary precision calculator implemented using java.math.BigInteger.

+ =   

Java code:
public static String addBigNumbers(final String a, final String b)
{
    if (!isValidInteger(a) || !isValidInteger(b))
    {
        return "Invalid input!";
    }

    final BigInteger i1 = new BigInteger(a);
    final BigInteger i2 = new BigInteger(b);

    return i1.add(i2).toString();
}

private static boolean isValidInteger(final String a)
{
    if (a.charAt(0) != '-' && !Character.isDigit(a.charAt(0)))
    {
        return false;
    }

    if (a.charAt(0) == '-' && a.length() == 1)
    {
        return false;
    }

    for (int i = 1; i < a.length(); i++)
    {
        if (!Character.isDigit(a.charAt(i)))
        {
            return false;
        }
    }

    return true;
}
Javascript code to add -12345 and 54354:
var result = bce.invokeMethod(
    'com.erinors.bce.demo.BigIntegerArithmetic', 'addBigNumbers', -12345, 54354
    );
Sample: Java-Javascript communication

Clicking the button below calls a Java method which displays a greeting message using the Javascript function window.alert().

  

Java code:
public static String sayHello(final String name)
{
    return "Hello " + name + "!";
}
Javascript code to say hello to the world:
alert(bce.invokeMethod('com.erinors.bce.demo.common.HelloUtils', 'sayHello', "World"));
Sample: Event listeners and callbacks

This sample demonstrates that com.erinors.bce.api.Callback instances can be used direcly as event listeners.

Java code:
public static Callback createClickHandler()
{
    return new Callback()
    {
        @Override
        public Object execute(final Object... arguments)
        {
            final JsObject event = (JsObject) arguments[0];
            JsObjects.getGlobal().callMember("alert",
                    "Greetings from Java code! (timeStamp=" + event.get("timeStamp") + ")");
            return null;
        }
    };
}
Javascript code to register the handler for the click event:
$(document).ready(function() {
    $("#button").
        click(
            bce.invokeMethod('com.erinors.bce.demo.EventDemo', 'createClickHandler')
            );
});
Sample: DOM manipulation

Simple DOM manipulation, inserts some dynamic content into the page. DOM manipulation is currently slow because access of DOM elements is implemented using JDK proxies (in production mode as well).
Currently only Firefox is supported because of IE's special implementation of DOM objects (as COM objects).

Java code:
public static void insertHtml()
{
    final Document document =
        JsObjects.wrap((JsObject) JsObjects.getGlobal().get("document"), Document.class);
    final Node placeholder =
        JsObjects.wrap((JsObject) document.callMember("getElementById", "placeholder"), Node.class);

    final String[] items = new String[] { "These", "lines", "were", "inserted", "dynamically..." };

    for (String item : items)
    {
        final Text textNode = document.createTextNode(item);
        placeholder.appendChild(textNode);
        placeholder.appendChild(document.createElement("br"));
    }
}
Javascript code:
bce.invokeMethod('com.erinors.bce.demo.common.DomUtils', 'insertHtml');
Sample: Non-Java language (Scala)

Liweb works with compiled JVM bytecode, not with Java source code. This means that it is not bound to the Java language, it can execute any bytecode regardless of its source.
To demonstrate this feature, in the example below a function written in Scala language is called from Javascript.

 

Scala code:
object Factorial {
    def fact(n: Int): BigInt =
        if (n == 0) 1 else fact(n-1) * n
    class Factorizer(n: Int) {
        def ! = fact(n)
    }
    implicit def int2fact(n: Int) = new Factorizer(n)

    def factorial(number: Int) = {
        number!
    }
}
Java code:
public static int factorial(int value)
{
    return Factorial.factorial(value).intValue();
}
Javascript code to calculate 4!:
alert(bce.invokeMethod('com.erinors.bce.demo.util.ScalaProxy', 'factorial', 4));
Future plans

Liweb is currently in an early stage of development. The main future plans are:

  • Stabilize and cleanup the code
  • Make more unit tests
  • Broaden the JRE support (currently - during development - only Sun's JRE 1.6 is supported)
  • Create a bytecode executor which is not interpreted but compiles the JVM bytecode to Javascript code. We expect at least double performance from this solution:

      Executed code Performance
    Native Javascript
    function fibonacci(n)
    {
        return n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
    }
    
    Interpreted executor Interpreted execution of the following bytecode:

       L0 (0)
        ILOAD 0
        ICONST_1
        IF_ICMPGT L1
        ILOAD 0
        GOTO L2
       L1 (6)
        ILOAD 0
        ICONST_1
        ISUB
        INVOKESTATIC Math.fibonacci(I)I
        ILOAD 0
        ICONST_2
        ISUB
        INVOKESTATIC Math.fibonacci(I)I
        IADD
       L2 (17)
        IRETURN
    
    Planned compiling executor Note: this is only a theoretical pseudo output of the planned compiler!
    An optimalized version may perform better...

    if (f.v[0] > 1)
    {
        f.push(
            f.invokeStatic("Math.fibonacci(I)I", f.v[0] - 1) +
            f.invokeStatic("Math.fibonacci(I)I", f.v[0] - 2)
        );
    }
    else
    {
    	f.push(f.v[0]);
    }
    
    return f.pop();
    

    (Note: it may take several seconds!)

  • If the core codebase becomes stable enough, a user interface library will be developed based on it.
  • Support for Javascript frameworks like JQuery, ExtJs and others.
  • Tons of other plans... :)