简体   繁体   English

通过套接字在Java桌面应用程序和Java浏览器小程序之间建立通信

[英]establishing communication between Java desktop app and Java browser applet via sockets

I'm new to network I/O programming, and I've run into a snag-- basically what I want to do is have a desktop app talk to the google maps javascript API. 我是网络I / O编程的新手,我遇到了一个麻烦-基本上,我想做的是让桌面应用程序与google maps javascript API对话。 In order to facilitate this, I have built a java applet which will act as a bridge between the desktop app and the browser javascript app. 为了简化此操作,我构建了一个Java小程序,它将充当桌面应用程序和浏览器javascript应用程序之间的桥梁。 When I run the desktop app and applet together in Eclipse they can communicate perfectly, and I am able to invoke applet functions by writing strings to a Socket bound to the same port the applet has established a ServerSocket connection with. 当我在Eclipse中同时运行桌面应用程序和applet时,它们可以完美通信,并且我可以通过将字符串写入绑定到与applet建立了ServerSocket连接的同一端口的Socket来调用applet函数。 For testing purposes in Eclipse, I send the string "sendJSAlertTest" to the socket's outputstream, then derive a Method instance using the java.lang.reflect API from the ServerSocket inputstream, and then finally invoke the resulting method in the applet. 为了在Eclipse中进行测试,我将字符串“ sendJSAlertTest”发送到套接字的输出流,然后从ServerSocket输入流使用java.lang.reflect API派生一个Method实例,然后最终在applet中调用结果方法。 When the applet is running in a browser I write "sendJSAlert" to the socket instead since it leads to the actual invocation of javascript. 当该applet在浏览器中运行时,我将“ sendJSAlert”写入套接字,因为它会导致javascript的实际调用。 The result in Eclipse using the appletviewer is that the desktop application context prints the output "awesome sendJSAlert" and the applet context prints the output from the sendJSAlertTest() method, "Hello Client, I'm a Server!". 在Eclipse中使用appletviewer的结果是,桌面应用程序上下文打印了输出“ awesome sendJSAlert”,而applet上下文打印了sendJSAlertTest()方法“ Hello Client,我是服务器!”的输出。 The result of passing "sendJSAlert" to the applet running in the browser is that the desktop application prints null, suggesting that for some reason the inputstream of the ServerSocket is empty, and the browser itself does nothing when it should generate a javascript alert box with the text "Hello Client, I'm a Server!". 将“ sendJSAlert”传递给在浏览器中运行的小程序的结果是,桌面应用程序打印为null,这表明出于某种原因ServerSocket的输入流为空,并且浏览器本身在应生成带有以下内容的javascript警报框时不执行任何操作:文本“ Hello Client,我是服务器!”。 The browser I'm using is Google Chrome, and for the moment I am simply running everything on the local machine (eg no remote server involved yet) 我使用的浏览器是Google Chrome,目前,我只是在本地计算机上运行所有内容(例如,尚未涉及任何远程服务器)

Below is the relevant Java code and HTML: 以下是相关的Java代码和HTML:

SocketClient.java SocketClient.java

import java.awt.event.*;
import java.io.*;
import java.net.*;

public class SocketClient {


Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
private InetAddress myAddress;
private String remoteFunction;

public SocketClient(){

}


public void listenSocket(int portNum){
//Create socket connection

 try{
   System.out.println("@Client Trying to create socket bound to port " + portNum);
   socket = new Socket(<my hostname here as a string>, portNum);
   System.out.println("the attached socket port is " + socket.getLocalPort());
   System.out.flush();
   out = new PrintWriter(socket.getOutputStream(), true);
   out.println("sendJSAlertTest");
   in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   String line = in.readLine();
   System.out.println("@CLient side Text received from server: " + line);
 } catch (UnknownHostException e) {
   System.out.println("Unknown host: <my hostname here as a string>.eng");
   System.exit(1);
 } catch  (IOException e) {
   System.out.println("No I/O");
   e.printStackTrace();
   System.exit(1);
 }
}

public void setRemoteFunction(String funcName){
  remoteFunction = funcName;
}
public String getRemoteFunction(){
 return remoteFunction;
}

}

SocketServer.java SocketServer.java

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;

class SocketServer {


ServerSocket server = null;
Socket client = null;
BufferedReader in = null;
PrintWriter out = null;
String line;
private NetComm hNet;
private Method serverMethod;

SocketServer(NetComm netmain){
   hNet = netmain;



}



public void listenSocket(int portNum){

try{
  System.out.println("@server Trying to create socket bound to port " + portNum);
  server = new ServerSocket(portNum);
  System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
  System.out.println("Could not listen on port " + portNum);
  System.exit(-1);
}

try{
  client = server.accept();
  System.out.println("Connection accepted!");
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try{
  in = new BufferedReader(new InputStreamReader(client.getInputStream()));
  out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

while(true){
  try{
     System.out.println("trying to read from inputstream...");
    line = in.readLine();
    System.out.println(line);
    //Now that we have a method name, invoke it
    try {
        serverMethod = hNet.getClass().getDeclaredMethod(line,
  String.class);
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    try {
        serverMethod.invoke(hNet, "Hello Client, I'm a Server!");
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    //Send data back to client
    out.println("awesome " + line);


   } catch (IOException e) {
     System.out.println("Read failed");
     System.out.flush();
     System.exit(-1);
   }

  }
  }

  protected void finalize(){
  //Clean up
  try{
    in.close();
    out.close();
    server.close();
  } catch (IOException e) {
    System.out.println("Could not close.");
    System.exit(-1);
 }
}

public int getBoundLocalPort(){
  return server.getLocalPort();
}

}

NetComm.java NetComm.java

import cresco.ai.att.ccm.core.CCMMain;
import cresco.ai.att.ccm.gui.DataPanel;

import java.io.*;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;

import javax.servlet.*;
import javax.servlet.http.*;

import java.applet.*;


public class NetComm extends JApplet{//HttpServlet{
private CCMMain hMain;
private DataPanel dpLocal;
private SocketServer sockserver;
private Method serverMethod;

String testStr;
Integer testInt;  /*integer */
Character testChar; /*character*/

//Testing this...
ServerSocket server = null;
Socket client = null;
BufferedReader in = null;
PrintWriter out = null;
String line;






@Override
public void init(){
    sockserver = new SocketServer(this);


    //For offline debug (should be disabled in a release to the webapp):
            //initSocketServer is commented out in the release version and
            //invoked in the Eclipse testbed version.  In the webapp,
            //initSocketServer is invoked from javascript (see below js sockPuppet())
    //////initSocketServer(0);




    String msg = "Hello from Java (using javascript alert)";
    try {
        getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
   msg +"\")"));
    }
    catch (MalformedURLException me) { }


}

public void sendJSAlertTest(String message){
    System.out.println("sendJSAlert remotely invoked, with message: " +
    message);

}

public void sendJSAlert(String message){

    try {
        getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
    message +"\")"));
    }
    catch (MalformedURLException me) { }

}



public void initSocketServer(int portNum){
    sockserver.listenSocket(portNum);
}
public void finalizeSocketServer(){
    sockserver.finalize();
}

public int socket2Me(int portNum){

    try {
        socks.add(new ServerSocket(portNum));
        return 0; //socket opened successfully
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return -1; //socket failed to open
    }




}

public int getSocketServerPort(){
    return sockserver.getBoundLocalPort();
}


public void showRectTest(){
    try {
        getAppletContext().showDocument(new
                    URL("javascript:overlayRect()"));
    }
    catch (MalformedURLException me) { }
}




public void setGUI(DataPanel d){
    dpLocal = d;
}


 }

MapViz.html MapViz.html

<html>
<head>
<title>Welcome to Geographic Midpoint Map Vizualization!</title>
<meta name="viewport"
    content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta charset="UTF-8">
<link href="https://google-developers.appspot.com/maps/documentation/javascript
/examples/default.css"
    rel="stylesheet" type="text/css">

...google maps stuff omitted...

<script type="text/javascript">
<script type="text/javascript">
function overlayRect(){
    var c=document.getElementById("myCanvas");
    var ctx=c.getContext("2d");
    ctx.fillStyle="#FF0000";
    ctx.fillRect(0,0,150,75);
}
function doAlert(s){
    alert(s);
}
function testJava(){
    document.ccmApplet.showRectTest();
}
function sockPuppet(){
    var i = parseInt(document.getElementById("args").value,10);
    alert("parsing the input args... got " + i);
    if(i == NaN || i == null){
        i = 0;
    }
    alert("passed NaN OR null block, i is " + i);
    //i = 6672; //because $%*& you, that's why!
    document.ccmApplet.initSocketServer(i);
    //document.ccmApplet.listenSocket(i);
    alert("inittializing socket server...");
    //queryPort();
    alert("querying port...");
    document.ccmApplet.finalizeSocketServer();
    //document.ccmApplet.finalize();
    alert("finalizing socket server...");
}
function queryPort(){
    var d = document.getElementById("debug");
    var s1 = "Last port opened was: ";
    //var s2 = document.ccmApplet.getLastBoundPort();
    var s2 = document.ccmApplet.getSocketServerPort();
    var sFinal = s1.concat(s2);
    d.value = sFinal;
}
</script>
 </head>
 <body>
 <applet width="500" height="50" name="ccmApplet" archive="CCM.jar"
 code="cresco.ai.att.ccm.io.NetComm" MAYSCRIPT></applet>
 <p></p>
<canvas id="myCanvas" width="200" height="100"></canvas>
 <div id="map_canvas"></div>
<input id="args" type="textentry" value="" />
<button height="50" width="50" onClick="sockPuppet()">Test Socket
    Creation</button>
<input id="debug" type="debugthingy" value="debug area..." />
<button height="50" width="50" onClick="testJava()">Test Java Callback</button>
  </body>
 </html>

In the webapp, I fill in the args input with a valid port number on the local machine and press the Test Socket Connection button which invokes the sockPuppet() javascript. 在webapp中,我用本地计算机上的有效端口号填写args输入,然后按Test Socket Connection按钮,该按钮调用sockPuppet()javascript。 This should create a ServerSocket and bind it to the specified port, which I then separately connect my desktop client app to via SocketClient.listenSocket. 这应该创建一个ServerSocket并将其绑定到指定的端口,然后我通过SocketClient.listenSocket单独将我的桌面客户端应用程序连接到该端口。 The result from Eclipse in the desktop app context is "awesome sendJSAlertTest" and in the appletviewer context is the output "sendJSAlert remotely invoked, with message: Hello Client, I'm a Server!". Eclipse在桌面应用程序上下文中的结果是“ awesome sendJSAlertTest”,在appletviewer上下文中的结果是输出“远程调用了sendJSAlert,并显示消息:Hello Client,我是服务器!”。 The webapp, invoking sendJSAlert(), should call the javascript alert function on the same message, creating a popup box with the message "sendJSAlert remotely invoked, with message: Hello Client, I'm a Server!" 调用sendJSAlert()的web应用程序应在同一条消息上调用javascript警报函数,并创建一个弹出框,其中显示消息“远程调用了sendJSAlert,消息:Hello Client,我是服务器!” but instead nothing happens in the browser (nor the Chrome java or javascript debug consoles), and the desktop app output is null instead of "awesome sendJSAlert" as expected 但是相反,浏览器(也不是Chrome Java或javascript调试控制台)中都没有任何反应,并且桌面应用程序的输出为null,而不是预期的“ awesome sendJSAlert”

So the question: What might be the cause of the different results? 那么问题来了:导致不同结果的原因可能是什么? I know the browser's security sandbox could be an issue, but I've included a permissions file which should allow communication via sockets on any localhost port: 我知道浏览器的安全沙箱可能是一个问题,但是我包含了一个权限文件,该文件应允许通过任何本地主机端口上的套接字进行通信:

grant {
 permission java.net.SocketPermission
    "localhost:1024-", 
 "accept, connect, listen, resolve";
};

It's certainly possible though that I have not applied the permissions properly (I used the sun policytool gui); 当然,可能是因为我没有正确地应用权限(我使用了sun policytool gui); what exactly needs to be done in the applet code (if anything) to apply the permissions? 要应用权限,在applet代码中到底需要做什么(如果有的话)? Could a security problem result in the lack of response I'm seeing? 安全问题可能会导致我看到的响应不足吗? I'd expect an exception to be reported in Chrome's java debug console, but there weren't any... 我希望Chrome的Java调试控制台中会报告一个异常,但是没有任何...

any help would be much appreciated, thanks! 任何帮助将不胜感激,谢谢!

-CCJ -CCJ

UPDATE: Okay, some new information: I ran the applet again in Chrome with the javascript console open (could have sworn I tried this before without effect, but evidently not) and received the following console output-- 更新:好的,一些新信息:我在Chrome浏览器中再次打开了javascript控制台,再次运行了该applet(可以宣誓我以前尝试过该操作没有效果,但显然没有效果),并且收到了以下控制台输出-

"Uncaught Error: java.security.AccessControlException: access denied 
("java.net.SocketPermission" "<myipaddress>:4218" "accept,resolve") MapVizApp.html:154
 sockPuppet MapVizApp.html:154 onclick MapVizApp.html:179 Uncaught Error: Error 
 calling method on NPObject. sockPuppet onclick " 

So the question now is why am I tripping this security exception? 因此,现在的问题是为什么我要触发此安全异常? The policy file with the permissions given above is in the same working directory as the html page and the jar file containing the applet, and I added the following to my system's JRE security policy file 具有上述权限的策略文件与html页面和包含applet的jar文件位于同一工作目录中,我将以下内容添加到了系统的JRE安全策略文件中

//Grants my NetComm applet the ability to accept, connect, and listen on unpriv. ports
grant codeBase "file:${user.home}\Desktop\dev\invention\ATT\MapViz\CCM.jar" {
  permission java.net.SocketPermission
    "localhost:1024-", 
  "accept, connect, listen, resolve";
};

I haven't yet signed the applet, but it was my understanding that if the policy files are in order an applet doesn't need to be signed... if I'm wrong on that please let me know. 我尚未对applet进行签名,但是据我了解,如果策略文件正确无误,就不需要对applet进行签名...如果我对此有误,请告诉我。 Anyway, does anyone have any suggestions as to why this security exception is being thrown despite the policy files having the above granted permissions? 无论如何,尽管策略文件具有上述授予的权限,但有人对为何引发此安全异常有任何建议吗? Is there a naming convention for policy files in working directories that the JRE looks for? JRE查找的工作目录中的策略文件是否有命名约定? My working directory policy file for now is just named ccmPolFile, but I'm not clear on how the JRE is supposed to locate it; 我现在的工作目录策略文件只是命名为ccmPolFile,但是我不清楚JRE应该如何定位它。 is there something I need to add to the applet code to point the JRE at the intended working directory policy file? 我需要在applet代码中添加一些内容,以将JRE指向预期的工作目录策略文件吗? Further, shouldn't the system policy file grant that I added be enough by itself to satisfy socket permissions for my applet inside CCM.jar? 此外,我自己添加的系统策略文件授予是否不应该足够满足我在CCM.jar中的applet的套接字权限?

UPDATE 2: I signed the applet and added the line policy.url.3=file:${user.home}\\Desktop\\dev\\invention\\ATT\\MapViz\\ccmPolFile.policy to my java.security file in ${java.home}/lib/security (via http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2 this is apparently how the JRE locates policy files to load)... sadly, the result is exactly the same security exception. 更新2:我签署了该applet,并在$ {java中将行policy.url.3 = file:$ {user.home} \\ Desktop \\ dev \\ invention \\ ATT \\ MapViz \\ ccmPolFile.policy添加到我的java.security文件中。主页} / lib / security(通过http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2,这显然是JRE定位要加载的策略文件的方式)...可悲的是,结果是完全一样的安全例外。 The only thing left that I know of is 我唯一知道的就是

AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
        // perform the security-sensitive operation here
        return null;
    }
});

which should let me do almost anything since the applet is now signed. 自从Applet签署后,这几乎可以让我做任何事情。 I wanted to keep signing out of the equation, but policy files aren't working for some reason. 我一直想退出,但是由于某些原因策略文件无法正常工作。 I'll be back shortly with how that works out 我很快会回来的

righto, so following my update 2 above, I change the listenSocket() method in SocketServer.java code to 好的,所以在上面的更新2之后,我将SocketServer.java代码中的listenSocket()方法更改为

public void listenSocket(int portNum){
  AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
            int portNum = 4444;
try{
  System.out.println("@server Trying to create socket bound to port " + portNum);
  server = new ServerSocket(portNum); 
  System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
  System.out.println("Could not listen on port " + portNum);
  System.exit(-1);
}

try{
  client = server.accept();
  System.out.println("Connection accepted!");
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try{
  in = new BufferedReader(new InputStreamReader(client.getInputStream()));
  out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

while(portNum==4444){
  try{
     System.out.println("trying to read from inputstream...");
    line = in.readLine();
    System.out.println(line);
    //Now that we have a method name, invoke it
    try {
        serverMethod = hNet.getClass().getDeclaredMethod(line, 
     String.class);
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    try {
        serverMethod.invoke(hNet, "Hello from Javascript invoked by the
    desktop app!");
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    //Send data back to client
    out.println("awesome " + line);
    //System.out.println(line);
    //System.out.flush();

  } catch (IOException e) {
    System.out.println("Read failed");
    System.out.flush();
    System.exit(-1);
  }

}

return null;
        }
    });//end doPrivileged
}

obviously this is an unsafe kludge, but it does the trick-- I receive no security exception, and the desktop app prints "awesome sendJSAlert" so I know IO is working between the client and server contexts via sockets. 显然,这是不安全的行为,但确实可以解决问题-我没有收到任何安全异常,并且桌面应用程序会打印“ awesome sendJSAlert”,因此我知道IO通过套接字在客户端和服务器上下文之间工作。 The actual js alert function didn't fire, but I think that has something to do with the horrid infinite while loop in listenSocket() above... 实际的js警报功能没有触发,但是我认为这与上面的listenSocket()中的可怕的无限while循环有关...

Take home message: for some reason, to establish socket connections from an applet in google chrome I needed to sign the applet AND use AccessController.doPrivileged() to invoke my security sensitive code, despite having set my local policy and security files to grant my applet those permissions 带回家的消息:出于某种原因,尽管已设置了本地策略和安全文件来授予我的权限,但我还是要通过Google Chrome中的一个applet建立套接字连接,我需要对该applet进行签名并使用AccessController.doPrivileged()来调用我的安全敏感代码。小程序的那些权限

googlers see refs: googlers看到参考:

http://www.coderanch.com/how-to/java/HowCanAnAppletReadFilesOnTheLocalFileSystem http://www.coderanch.com/how-to/java/HowCanAnAppletReadFilesOnTheLocalFileSystem

http://docs.oracle.com/javase/7/docs/api/java/security/AccessController.html http://docs.oracle.com/javase/7/docs/api/java/security/AccessController.html

http://www-personal.umich.edu/~lsiden/tutorials/signed-applet/signed-applet.html http://www-personal.umich.edu/~lsiden/tutorials/signed-applet/signed-applet.html

http://docs.oracle.com/javase/tutorial/security/tour2/step4.html http://docs.oracle.com/javase/tutorial/security/tour2/step4.html

UPDATE: Finally working 100% :DI changed the listenSocket() method above in SocketServer.java to this: 更新:最终工作100%:DI将上面SocketServer.java中的listenSocket()方法更改为:

public void listenSocket(int portNum){
  AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
            int portNum = 4444;
try{
  System.out.println("@server Trying to create socket bound to port " + portNum);
  server = new ServerSocket(portNum); 
  System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
  System.out.println("Could not listen on port " + portNum);
  System.exit(-1);
}

try{
  client = server.accept();
  System.out.println("Connection accepted!");
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try{
  in = new BufferedReader(new InputStreamReader(client.getInputStream()));
  out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try {
    line = in.readLine();
    System.out.println("line is " + line + " from the inputstream to the 
            serversocket");
} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

if(line != null){
  System.out.println("trying to read from non-null inputstream...");
//line = in.readLine();
System.out.println(line);
//Now that we have a method name, invoke that bitch!
try {
    serverMethod = hNet.getClass().getDeclaredMethod(line, String.class);
} catch (SecurityException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

try {
    serverMethod.invoke(hNet, "Hello From Javascript invoked by a desktop 
             app!");
} catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (InvocationTargetException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

//Send data back to client
out.println("awesome " + line);
//System.out.println(line);
//System.out.flush();

 }

return null;
        }
    });//end doPrivileged
}

The server.accept() method blocks until a connection is made anyway, so for this scenario where I only want to pass one command at a time to the serversocket inputstream a while loop didn't make sense. server.accept()方法一直阻塞直到建立连接为止,因此对于这种情况,我只想一次只将一个命令传递到serverocket输入流,而while循环是没有意义的。 The change to an if allowed the program to actually continue on to the java.reflect stuff which invokes a method in the applet which invokes javascript functions directly. 如果更改为if,则允许程序实际继续到java.reflect东西,该东西将调用applet中的一个方法,该方法直接调用javascript函数。 Since the port is still hard-coded and the applet utilizes doPrivileged(...) this is still not a great solution, but it does satisfy the use case of invoking javascript in a web browser from a desktop java application via a java applet bridge so it makes for a good springboard into more robust implementations! 由于端口仍然是硬编码的,并且小程序使用了doPrivileged(...),所以这仍然不是一个很好的解决方案,但是它确实满足了使用Java小程序桥从桌面Java应用程序在Web浏览器中调用javascript的用例。因此,它为更好的实现提供了良好的跳板!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM