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:
@@ -15,7 +15,7 @@
|
|||||||
<condition property="maven-ant.exists"><available file="${maven-ant.jar}"/></condition>
|
<condition property="maven-ant.exists"><available file="${maven-ant.jar}"/></condition>
|
||||||
<target name="-download-maven-ant" unless="maven-ant.exists">
|
<target name="-download-maven-ant" unless="maven-ant.exists">
|
||||||
<mkdir dir="${maven-ant.dir}"/>
|
<mkdir dir="${maven-ant.dir}"/>
|
||||||
<get src="${maven-ant.url}/maven-ant-tasks-${maven-ant.vers}.jar"
|
<get src="${maven-ant.url}/maven-ant-tasks-${maven-ant.vers}.jar"
|
||||||
dest="${maven-ant.jar}" usetimestamp="true"/>
|
dest="${maven-ant.jar}" usetimestamp="true"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
@@ -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"/>
|
||||||
@@ -124,13 +128,14 @@
|
|||||||
<proguard>
|
<proguard>
|
||||||
<injar path="${getdown.jar}"/>
|
<injar path="${getdown.jar}"/>
|
||||||
<injar path="${jregistrykey:jregistrykey:jar}" filter="!META-INF/**"/>
|
<injar path="${jregistrykey:jregistrykey:jar}" filter="!META-INF/**"/>
|
||||||
<injar path="${com.samskivert:samskivert:jar}" filter="!META-INF/**,
|
<injar path="${com.samskivert:samskivert:jar}" filter="!META-INF/**,
|
||||||
!**/Log4JLogger*,!**/*.java,com/samskivert/Log.class,**/samskivert/io/**,
|
!**/Log4JLogger*,!**/*.java,com/samskivert/Log.class,**/samskivert/io/**,
|
||||||
**/samskivert/swing/**,**/samskivert/text/**,**/samskivert/util/**"/>
|
**/samskivert/swing/**,**/samskivert/text/**,**/samskivert/util/**"/>
|
||||||
<injar path="${commons-codec:commons-codec:jar}" filter="!META-INF/**"/>
|
<injar path="${commons-codec:commons-codec:jar}" filter="!META-INF/**"/>
|
||||||
<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.**">
|
||||||
@@ -149,7 +154,7 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<property name="maven.deploy.repo" value="file://${user.home}/.m2/repository"/>
|
<property name="maven.deploy.repo" value="file://${user.home}/.m2/repository"/>
|
||||||
<target name="maven-deploy" depends="dist"
|
<target name="maven-deploy" depends="dist"
|
||||||
description="Deploys our build artifacts to a Maven repository.">
|
description="Deploys our build artifacts to a Maven repository.">
|
||||||
<jar basedir="${src.dir}" destfile="${deploy.dir}/${ant.project.name}-sources.jar"/>
|
<jar basedir="${src.dir}" destfile="${deploy.dir}/${ant.project.name}-sources.jar"/>
|
||||||
<artifact:deploy file="${deploy.dir}/${ant.project.name}.jar" uniqueVersion="false">
|
<artifact:deploy file="${deploy.dir}/${ant.project.name}.jar" uniqueVersion="false">
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ public class GetdownAppletConfig
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
* When a number is reached for which no value exists, we stop looking. */
|
* When a number is reached for which no value exists, we stop looking. */
|
||||||
public static final String APPARG_PREFIX = "appargs";
|
public static final String APPARG_PREFIX = "appargs";
|
||||||
|
|
||||||
public String appbase;
|
public String appbase;
|
||||||
|
|
||||||
public String appname;
|
public String appname;
|
||||||
@@ -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