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"
|
<taskdef resource="org/apache/maven/artifact/ant/antlib.xml"
|
||||||
uri="urn:maven-artifact-ant" classpath="${maven-ant.jar}"/>
|
uri="urn:maven-artifact-ant" classpath="${maven-ant.jar}"/>
|
||||||
<artifact:pom id="pom" file="pom.xml"/>
|
<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"/>
|
<artifact:dependencies pathId="testlibs.classpath" pomRefId="pom" useScope="test"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="-prepare" depends="-init-maven-ant">
|
<target name="-prepare" depends="-init-maven-ant">
|
||||||
<mkdir dir="${deploy.dir}"/>
|
<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 id="built.classpath">
|
||||||
<path refid="compile.classpath"/>
|
<path refid="compile.classpath"/>
|
||||||
<pathelement location="${deploy.dir}/classes"/>
|
<pathelement location="${deploy.dir}/classes"/>
|
||||||
@@ -131,6 +135,7 @@
|
|||||||
<outjar path="${deploy.dir}/getdown-pro${version.suff}.jar"/>
|
<outjar path="${deploy.dir}/getdown-pro${version.suff}.jar"/>
|
||||||
<libraryjar name="${rt.jar}"/>
|
<libraryjar name="${rt.jar}"/>
|
||||||
<libraryjar name="${org.apache.ant:ant: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 the whole nine yards, just trim/obfuscate our depends -->
|
||||||
<keep name="com.threerings.getdown.**">
|
<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. */
|
/** 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";
|
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.Container;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -44,6 +49,11 @@ import java.security.cert.CertificateFactory;
|
|||||||
import javax.swing.JApplet;
|
import javax.swing.JApplet;
|
||||||
import javax.swing.JPanel;
|
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;
|
import static com.threerings.getdown.Log.log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,6 +62,35 @@ import static com.threerings.getdown.Log.log;
|
|||||||
public class GetdownApplet extends JApplet
|
public class GetdownApplet extends JApplet
|
||||||
implements ImageLoader
|
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
|
@Override // documentation inherited
|
||||||
public void init ()
|
public void init ()
|
||||||
{
|
{
|
||||||
@@ -108,6 +147,19 @@ public class GetdownApplet extends JApplet
|
|||||||
return GetdownApplet.this;
|
return GetdownApplet.this;
|
||||||
}
|
}
|
||||||
@Override
|
@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) {
|
protected void exit (int exitCode) {
|
||||||
_app.releaseLock();
|
_app.releaseLock();
|
||||||
_config.redirect();
|
_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
|
// implemented from ImageLoader
|
||||||
public Image loadImage (String path)
|
public Image loadImage (String path)
|
||||||
{
|
{
|
||||||
@@ -163,6 +271,25 @@ public class GetdownApplet extends JApplet
|
|||||||
_getdown._app.releaseLock();
|
_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.
|
* 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. */
|
/** An error encountered during initialization. */
|
||||||
protected String _errmsg;
|
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 ERRORBGIMAGE = "errorbgimage";
|
||||||
public static final String PARAM_DELIMITER = ".";
|
public static final String PARAM_DELIMITER = ".";
|
||||||
public static final String DIRECT = "direct";
|
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 = "redirect_on_finish";
|
||||||
public static final String REDIRECT_ON_FINISH_TARGET = "redirect_on_finish_target";
|
public static final String REDIRECT_ON_FINISH_TARGET = "redirect_on_finish_target";
|
||||||
|
|
||||||
@@ -107,6 +108,11 @@ public class GetdownAppletConfig
|
|||||||
* separate java process (false). */
|
* separate java process (false). */
|
||||||
public boolean invokeDirect;
|
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. */
|
/** Optional default bounds for the status panel. */
|
||||||
public Rectangle statusBounds;
|
public Rectangle statusBounds;
|
||||||
|
|
||||||
@@ -183,6 +189,9 @@ public class GetdownAppletConfig
|
|||||||
String direct = getParameter(DIRECT, "false");
|
String direct = getParameter(DIRECT, "false");
|
||||||
invokeDirect = Boolean.valueOf(direct);
|
invokeDirect = Boolean.valueOf(direct);
|
||||||
|
|
||||||
|
String connect = getParameter(CONNECT, "false");
|
||||||
|
allowConnect = Boolean.valueOf(connect);
|
||||||
|
|
||||||
String redirectURL = getParameter(REDIRECT_ON_FINISH);
|
String redirectURL = getParameter(REDIRECT_ON_FINISH);
|
||||||
if (redirectURL != null) {
|
if (redirectURL != null) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user