Provide a means of exchanging messages between the JavaScript context of the
page hosting the applet and the launched application through a simple server socket bound to the loopback address on any available port (where the port is passed to the application as a system property). Not tested yet, but it shouldn't affect any deployments that don't specifically enable it.
This commit is contained in:
@@ -23,12 +23,16 @@
|
||||
<taskdef resource="org/apache/maven/artifact/ant/antlib.xml"
|
||||
uri="urn:maven-artifact-ant" classpath="${maven-ant.jar}"/>
|
||||
<artifact:pom id="pom" file="pom.xml"/>
|
||||
<artifact:dependencies pathId="compile.classpath" pomRefId="pom" useScope="compile"/>
|
||||
<artifact:dependencies pathId="pom-compile.classpath" pomRefId="pom" useScope="compile"/>
|
||||
<artifact:dependencies pathId="testlibs.classpath" pomRefId="pom" useScope="test"/>
|
||||
</target>
|
||||
|
||||
<target name="-prepare" depends="-init-maven-ant">
|
||||
<mkdir dir="${deploy.dir}"/>
|
||||
<path id="compile.classpath">
|
||||
<path refid="pom-compile.classpath"/>
|
||||
<pathelement location="${java.home}/lib/plugin.jar"/>
|
||||
</path>
|
||||
<path id="built.classpath">
|
||||
<path refid="compile.classpath"/>
|
||||
<pathelement location="${deploy.dir}/classes"/>
|
||||
@@ -131,6 +135,7 @@
|
||||
<outjar path="${deploy.dir}/getdown-pro${version.suff}.jar"/>
|
||||
<libraryjar name="${rt.jar}"/>
|
||||
<libraryjar name="${org.apache.ant:ant:jar}"/>
|
||||
<libraryjar name="${java.home}/lib/plugin.jar"/>
|
||||
|
||||
<!-- keep the whole nine yards, just trim/obfuscate our depends -->
|
||||
<keep name="com.threerings.getdown.**">
|
||||
|
||||
@@ -32,4 +32,8 @@ public class Properties
|
||||
{
|
||||
/** This property will be set to "true" on the application when it is being run by getdown. */
|
||||
public static final String GETDOWN = "com.threerings.getdown";
|
||||
|
||||
/** If accepting connections from the launched application, this property
|
||||
* will be set to the connection server port. */
|
||||
public static final String CONNECT_PORT = "com.threerings.getdown.connectPort";
|
||||
}
|
||||
|
||||
@@ -27,12 +27,17 @@ package com.threerings.getdown.launcher;
|
||||
|
||||
import java.awt.Container;
|
||||
import java.awt.Image;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -44,6 +49,11 @@ import java.security.cert.CertificateFactory;
|
||||
import javax.swing.JApplet;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import netscape.javascript.JSObject;
|
||||
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Properties;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
/**
|
||||
@@ -52,6 +62,35 @@ import static com.threerings.getdown.Log.log;
|
||||
public class GetdownApplet extends JApplet
|
||||
implements ImageLoader
|
||||
{
|
||||
/**
|
||||
* Sets the JavaScript callback to invoke when a message is received from the launched app.
|
||||
* The callback should be a function that accepts a single string parameter (the received
|
||||
* message).
|
||||
*/
|
||||
public synchronized void setMessageCallback (JSObject callback)
|
||||
{
|
||||
_messageCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to send a message to the launched app.
|
||||
*
|
||||
* @return true if we succeeded in sending the message, false if the launched app has not (yet)
|
||||
* established a connection to Getdown, or the send failed.
|
||||
*/
|
||||
public synchronized boolean sendMessage (String message)
|
||||
{
|
||||
if (_connectOut != null) {
|
||||
try {
|
||||
_connectOut.writeUTF(message);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
log.warning("Error sending message to app.", "message", message, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public void init ()
|
||||
{
|
||||
@@ -108,6 +147,19 @@ public class GetdownApplet extends JApplet
|
||||
return GetdownApplet.this;
|
||||
}
|
||||
@Override
|
||||
protected void launch () {
|
||||
// if so configured, create a server socket to listen
|
||||
// for a connection from the app
|
||||
if (_config.allowConnect) {
|
||||
try {
|
||||
startConnectServer();
|
||||
} catch (IOException e) {
|
||||
log.warning("Failed to start connect server.", e);
|
||||
}
|
||||
}
|
||||
super.launch();
|
||||
}
|
||||
@Override
|
||||
protected void exit (int exitCode) {
|
||||
_app.releaseLock();
|
||||
_config.redirect();
|
||||
@@ -128,6 +180,62 @@ public class GetdownApplet extends JApplet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to start the server that will accept a connection from the launched app, allowing
|
||||
* it to exchange messages with the JavaScript context.
|
||||
*/
|
||||
protected void startConnectServer ()
|
||||
throws IOException
|
||||
{
|
||||
// bind and set a property with the local port that will be passed through to the app
|
||||
_serverSocket = new ServerSocket(0, 0, InetAddress.getByName(null));
|
||||
System.setProperty(Application.PROP_PASSTHROUGH_PREFIX + Properties.CONNECT_PORT,
|
||||
String.valueOf(_serverSocket.getLocalPort()));
|
||||
Thread thread = new Thread("ConnectServer") {
|
||||
@Override
|
||||
public void run () {
|
||||
while (true) {
|
||||
try {
|
||||
acceptConnection();
|
||||
} catch (IOException e) {
|
||||
if (!_serverSocket.isClosed()) {
|
||||
log.warning("Error accepting connection.", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void acceptConnection () throws IOException {
|
||||
Socket socket = _serverSocket.accept();
|
||||
DataInputStream connectIn = new DataInputStream(socket.getInputStream());
|
||||
synchronized (GetdownApplet.this) {
|
||||
_connectOut = new DataOutputStream(socket.getOutputStream());
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
String message = connectIn.readUTF();
|
||||
synchronized (GetdownApplet.this) {
|
||||
if (_messageCallback != null) {
|
||||
_messageCallback.call("call",
|
||||
new Object[] { _messageCallback, message });
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (!socket.isClosed()) {
|
||||
log.warning("Error reading message.", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
synchronized (GetdownApplet.this) {
|
||||
_connectOut = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
// implemented from ImageLoader
|
||||
public Image loadImage (String path)
|
||||
{
|
||||
@@ -163,6 +271,25 @@ public class GetdownApplet extends JApplet
|
||||
_getdown._app.releaseLock();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public synchronized void destroy ()
|
||||
{
|
||||
if (_serverSocket != null) {
|
||||
try {
|
||||
_serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
log.warning("Error closing server socket.", e);
|
||||
}
|
||||
}
|
||||
if (_connectOut != null) {
|
||||
try {
|
||||
_connectOut.close();
|
||||
} catch (IOException e) {
|
||||
log.warning("Error closing connect socket/output stream.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the specified file and writes the supplied contents to it.
|
||||
*/
|
||||
@@ -207,4 +334,13 @@ public class GetdownApplet extends JApplet
|
||||
|
||||
/** An error encountered during initialization. */
|
||||
protected String _errmsg;
|
||||
|
||||
/** The message callback registered by JavaScript on the containing page, if any. */
|
||||
protected JSObject _messageCallback;
|
||||
|
||||
/** The server socket on which we listen for connections, if any. */
|
||||
protected ServerSocket _serverSocket;
|
||||
|
||||
/** The output stream to the launched app, if a connection has been established. */
|
||||
protected DataOutputStream _connectOut;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public class GetdownAppletConfig
|
||||
public static final String ERRORBGIMAGE = "errorbgimage";
|
||||
public static final String PARAM_DELIMITER = ".";
|
||||
public static final String DIRECT = "direct";
|
||||
public static final String CONNECT = "connect";
|
||||
public static final String REDIRECT_ON_FINISH = "redirect_on_finish";
|
||||
public static final String REDIRECT_ON_FINISH_TARGET = "redirect_on_finish_target";
|
||||
|
||||
@@ -107,6 +108,11 @@ public class GetdownAppletConfig
|
||||
* separate java process (false). */
|
||||
public boolean invokeDirect;
|
||||
|
||||
/** Indicates whether Getdown should allow the launched app to connect to it through a server
|
||||
* socket bound to localhost on any available port in order to allow interaction with
|
||||
* JavaScript code on the page containing the applet. */
|
||||
public boolean allowConnect;
|
||||
|
||||
/** Optional default bounds for the status panel. */
|
||||
public Rectangle statusBounds;
|
||||
|
||||
@@ -183,6 +189,9 @@ public class GetdownAppletConfig
|
||||
String direct = getParameter(DIRECT, "false");
|
||||
invokeDirect = Boolean.valueOf(direct);
|
||||
|
||||
String connect = getParameter(CONNECT, "false");
|
||||
allowConnect = Boolean.valueOf(connect);
|
||||
|
||||
String redirectURL = getParameter(REDIRECT_ON_FINISH);
|
||||
if (redirectURL != null) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user