How to communicate between browser and Java Web Start applet

Jaims picture Jaims · Jun 16, 2016 · Viewed 7.3k times · Source

Current situation

We currently use an applet to perform some operations, after which it redirects the current page. In its core, you could see the applet as the following:

public class ExampleApplet extends Applet {
    @Override
    public void init() {
        Button redirect = new Button("Redirect");
        this.add(redirect);
        final String target = this.getParameter("targetPage");
        redirect.addActionListener((ActionEvent e) -> {
            try {
                getAppletContext().showDocument(new URL(target), "_parent");
            } catch (MalformedURLException ex) {}
        });
    }
}

with the applet being called in its simplest way:

<applet code="com.example.applet.ExampleApplet.class" archive="${appletUrl}" width="100" height="30">
    <param name="targetPage" value="http://localhost:8080/applet/"/>
</applet><br/><br/>

where ${appletUrl} returns the location of the applet JAR.

So the applet is nothing more than a simple button that calls getAppletContext().showDocument(new URL(target), "_parent"); to refresh the current page. This has done its job correctly for a long time. Now here's the issue.

Migration

As many may know, Chrome does not support Applets. Which was put aside for a while since IE and FireFox still supported them. At the end of 2016, they will also no longer support them. So we decided to migrate the applet using JWS and JNLP.

Migrating this simple redirect button example would give the following html snippet and JNLP file:

<a href="${jnlpUrl}">Launch JNLP</a>

${jnlpUrl} returns the location to the JNLP file which is:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:8080/applet/assets/1.0-SNAPSHOT-DEV/app/assets/" href="jnlp/example.jnlp">
    <security>
        <all-permissions/>
    </security>
    <resources>
        <j2se version="1.5+" initial-heap-size="32m" max-heap-size="128m" />
        <property name="jnlp.versionEnabled" value="false"/>
        <jar href="applets/ExampleApplet.jar" main="true"/>
    </resources>
    <applet-desc name="code" main-class="com.example.applet.ExampleApplet.class" width="30" height="30" >
        <param name="targetPage" value="http://localhost:8080/applet/"/>
    </applet-desc>
</jnlp>

So far so good, the same applet successfuly deploys as a JWS application. Allowing it to be used from any browser since it's executed outside of it. Which also is kind of the problem.

The problem

The line getAppletContext().showDocument(new URL(target), "_parent"); still does a redirect, but it's using the default browser as stated in the migration documentation.

For AppletContext.showDocument(URL url, String target), the target argument will be ignored by Java Web Start technology.

Similar to AppletContext.showDocument, Java Web Start applications are capabile of showing an HTML page using the system's default web browser by using the BasicService.showDocument API.

So if my default browser is FireFox, but I happen to be browsing to this JWS-enabled applet in IE/Chrome, a new tab will be opened in FireFox. This is a problem, since I have information (e.g. login) stored in the original browser!

Findings

Since the application is running outside of the browser, I'm having issues to think of possibilities to communicate back to the original browser. I can't use JavaScript since it doesn't run within the browser. I can't really define a system-independent way to open a tab in the original browser. I've also thought of WebSockets since they could allow direct communication, but from what I've read it's pretty high-level and requires a server, not a simple applet.

Is there any possibility of communicating between the original browser (e.g. websockets and parameters) or passing the session from one browser to another when the applet opens a new window?

Answer

Jaims picture Jaims · Jun 22, 2016

I have found out a working solution.

Since the applet loses all connection to the browser and its session, another way to provide communication is with WebSockets or Comet. In my case I used Comet with the Atmosphere framework and a Tapestry-Atmosphere implementation, since Tapestry is the view-framework I'm using.

Without going too deep into the Tapestry implementation, I have done the following:

  • Set up a Topic on the client-side browser which will listen to broadcasted messages in a typical publish/subscribe manner.
  • Provide the Topic's ID which is unique to the current browsing user into the Applet, along with a URL to send a request to. Using the Topic ID as a request parameter for the url.
  • Server-side I have the endpoint of the request which receives the Topic as a request parameter. Using this parameter, it sends a broadcast (possibly empty) to the topic.
  • The client-side Topic receives the notification and executes an event in itself. The event being to re-render specific content of the page.

Since it's using Comet (but could also use WebSockets) it happens directly in the browser. Every browser subscribed to that Topic actually, but here there's only one.

Making it in fact possible, to update the page from a simple request from the applet. The applet only had the line getAppletContext().showDocument(new URL(target), "_parent"); changed to new URL(target).openConnection().getInputStream();. With target being a URL with the included request parameter.