The Source for Java Technology Collaboration


-- JohnCatherino - 22 Feb 2006

Creating Swing based graphical proxies

This example will require version 1.79, or any later version of the 40kB cajo.jar from the cajo project.

Below is the Swing version of the commonly used AWT based ProxyTest, the rest of this page will explain how it differs.

JTest.java:


import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.Serializable;
import gnu.cajo.invoke.Remote;
import gnu.cajo.utils.ItemServer;
import gnu.cajo.utils.MonitorItem;
import gnu.cajo.utils.CodebaseServer;
import gnu.cajo.utils.extra.AsyncMethod;

public class JTest {

    public static class JProxy implements Serializable, ActionListener {
        private Object selfRef, item;
        public void setItem(Object item) { this.item = item; }
        public Component init(Object ref) {
            selfRef = ref;
            return new JPanel() {
               {
                  setLayout(new BorderLayout());
                  JLabel label = new JLabel("Test Proxy (Swing)");
                  add(label, BorderLayout.NORTH);
                  JButton button = new JButton("fun button!");
                  button.addActionListener(JProxy.this);
                  add(button, BorderLayout.SOUTH);
               }
               public Dimension getPreferredSize() {
                  return new Dimension(300, 300);
               }
            };
        }
        public void actionPerformed(ActionEvent e) {
            AsyncMethod.invoke(item, "pressed", null, null);
        }
    }

    private JProxy proxy = new JProxy();
    public Object getProxy() { return proxy; }
    public void pressed() { System.out.println("I heard that!"); }
    public String toString() { return "Swing Proxy Server"; }
    public static void main(String args[]) throws Exception {
         new CodebaseServer(null, 9000, "gnu.cajo.invoke.JClient");
         Remote.config(null, 1198, null, 0);
         JTest t = new JTest();
         ItemServer.bind(new MonitorItem(t), "main", t.proxy);
         System.out.print("Server running on interface ");
         System.out.println(Remote.getServerHost());
         System.out.print("Using TCP port ");
         System.out.println(Remote.getServerPort());
         System.out.print("Codebase server running: ");
         System.out.println(System.getProperty("java.rmi.server.codebase"));
    }
}

First and foremost: for security reasons, serialised Swing components cannot be instantiated in an Applet or WebStart sandbox, they must be created locally, this is the major impetus for the change in structure. Now the proxy is an ordinary object, which provides a graphical user interface in response to the generic client's getProxy invocation. Notice that all of the AWT components have been replaced with their Swing equivalents, and that JTest is still a plain-old Java object.

Aside from this, there is only one other change which requires some discussion. CodebaseServer has an additional three argument constructor. Its purpose is to allow the specification of a non-default graphical client. In this case it specifies the JClient standard generic Swing client. An important detail is that JClient is a proper superset of the generic AWT Client, i.e. it can host AWT proxies in addition to Swing proxies. It may make sense to specificy JClient in the CodebaseServer constructor as a general rule.

Compiling the example:

The file above is compiled similarly to the previous example:

javac -classpath cajo.jar JTest.java
Then to run the server do similarly:
java -classpath cajo.jar;. JTest
This will result in the following console output:
Caller host = localhost <trace>
        class = gnu.cajo.utils.ItemServer.bind
        class = JTest.main
        file  = JTest.java line 45
Item called = Swing Proxy Server
Method call = startThread
Method args = null
Result data = java.lang.NoSuchMethodException: JTest.startThread()
        at java.lang.Class.getMethod(Unknown Source)
        at gnu.cajo.invoke.Remote.invoke(Unknown Source)
        at gnu.cajo.utils.MonitorItem.invoke(Unknown Source)
        at gnu.cajo.invoke.Remote.invoke(Unknown Source)
        at gnu.cajo.utils.ItemServer.bind(Unknown Source)
        at JTest.main(JTest.java:45)

Call count  = 1
Idle time   = ## ms
Busy time   = 0 ms
Free memory = ##%
Server running on interface hostName
Using TCP port 1198
Codebase server running: http://hostName:9000/
This time we are demonstrating a few new techniques:
  • First is the use of fixed ports, as opposed to anonymous ones, in both the Remote.config operation, and the CodebaseServer constructor. Unless you are working exclusively on a LAN, this is necessary, such that specific ports may then be opened on the firewall.

  • Second, we have added the functionality of a MonitorItem in the bind operation of the server object, to show how detailed debug functionality can be easily and transparently wrapped on. It will even show an internal, but entirely normal exception, which would otherwise be suppressed, silently. The exception occurs when the ItemServer calls the startThread method of the JTest object, to begin its internal processing thread. We simply didn't define one.

  • Third, we have implemented the java.lang.Object toString method on the Test object, so as to easily recognise it in the MonitorItem log output.

  • Finally, we are using the static invoke method of the AsyncMethod class in the proxy's actionPerformed method. As its name implies, it performs the remote method invocation asynchronously, i.e. the invoke method returns immediately, while the invocation is performed in a separate thread. Performing remote methods asynchronously from the Event Dispatch Thread is a very good idea.

Using the proxy:

This part is also done quite similarly to the previous example

java -cp cajo.jar gnu.cajo.invoke.JClient //hostName:1198/main

This time we will specify the server item name explicitly. When it is not specified it is "main" by default. It can of course be any name you like, and you can bind as many objects as you wish.

And for the interactive part:

Again, when the client VM user presses the button in the proxy GUI, the remote server proudly exclaims:

I heard that!

Swing Applets:

Here is the real crux of the demonstration, a genuine Swing graphical proxy applet. Go to your favourite browser, and enter in the codebase service URL:

http://hostName:9000/-main

Behold the Swing graphical proxy widget!

And finally:

To demonstrate that JClient really is a proper superset of the standard generic Client class, add the following line from JProxyTest:

new CodebaseServer(null, 9000, "gnu.cajo.invoke.JClient");

into the static main method of the ProxyTest example, in place of its CodebaseServer constructor. You will notice the AWT applet is rendered correctly as well. The AWT Client will remain the default viewer for backward compatibility, but more importantly because our JME community does not get Swing libraries unfortunately. To change the default client could cause their clients to crash, and force them to rewrite all their applications to specify the original AWT client in their CodebaseServer startups.

Topic JProxyTest . { Edit | Ref-By | Printable | Diffs r12 < r11 < r10 < r9 < r8 | More }
 XML java.net RSS

Revision r12 - 01 Aug 2007 - 18:31:10 - JohnCatherino
Parents: WebHome > ThecajoProject > ExampleSection > ProxyUsage