java - 实例化新类时,代理后面的Java applet会暂时卡住

标签 java proxy applet classloader

介绍

我们在客户办公室的Java小程序存在一些问题。该小程序旨在通过定期捕获屏幕快照来记录屏幕。它已签名,应以提升的特权运行。

我们遇到的问题是:

  • Java applet有时可能根本无法启动。如果出现Java控制台,它可能很快就会消失。感觉好像整个JVM都崩溃了。
  • 如果Java applet启动,启动后10秒钟有时会崩溃。
  • 如果Java小程序启动,它几乎总是会经历长达12秒的临时冻结。
  • (小程序启动有点慢。)

  • 一个典型的情况是,在启动Windows后,第一次尝试将不会加载该applet。刷新/重新登录/重新启动浏览器(我不太确定需要什么)后,小程序启动,遇到死机,然后崩溃。在第三次尝试时,它不再崩溃,但仍然遇到冻结。

    背景

    客户有一个可能与问题有关的Blue Coat Systems HTTP代理。设置网络后,到Internet的所有流量都必须通过代理。例如,由于无法解析主机www.google.com,因此ping www.google.com将在超时后失败。我发现一些页面描述了Java和Blue Coat代理的问题。我认为这些内容并不能提供太多有用的信息,但它们使我更加相信代理与问题有关。
  • Java not resolving to bluecoat proxy
  • Problem accessing GoToAssist, GoToMeeting, GoToWebinar, or GoToMyPC
  • Problem accessing Salesforce.com
  • I am getting a Java error when accessing RealTimeGraph with Firefox browser.

  • 代理需要NTLM身份验证,这意味着浏览器第一次尝试通过时,我会弹出一个对话框,询问我的用户名和密码。当我尝试使用Chrome访问Google时,这是来自代理的HTTP响应的一部分:
    HTTP/1.1 407 Proxy Authentication Required
    Proxy-Authenticate: NTLM TlRMTVNTUAACAAAADAAMADgAAAAFgokC/+XiO/tpmQMAAAAAAAAAAKQApABEAAAABQLODgAAAA9KAEEATQBQAFQASQACAAwASgBBAE0AUABUAEkAAQASAFAASwBIAEsASQBEAEMAMAAxAAQAHgBwAHUAdQBrAGUAcwBrAHUAcwAuAGwAbwBjAGEAbAADADIAcABrAGgAawBpAGQAYwAwADEALgBwAHUAdQBrAGUAcwBrAHUAcwAuAGwAbwBjAGEAbAAFAB4AcAB1AHUAawBlAHMAawB1AHMALgBsAG8AYwBhAGwAAAAAAA==
    

    环境

    我当时在客户自己的笔记本电脑上体验问题。我的设置是在虚拟机中运行Ubuntu Linux 12.04和Windows 7。我使用Windows和Java 1.6.35进行大多数测试。我确实在Linux上运行过该applet一次(我认为是使用Oracle Java 1.7.0_07),并且它没有遇到死机现象,但是applet拍摄的屏幕截图可能为空白或损坏。

    我已取消选中Java设置中的“在计算机上保留临时文件”复选框,因为有时缓存的Java小程序会干扰开发。在客户办公室调试小程序时,我禁用了临时文件,但是我不相信此设置会影响该问题,因为我们的客户可能已启用了临时文件,并且他们总是会遇到这些问题。

    问题似乎并不取决于浏览器。我已经尝试过IE9,Firefox,Chrome和Opera。

    观察结果

    在Windows中运行小程序时,我将Eclipse调试器附加到小程序。在冻结小程序时,我暂停了该小程序的执行,并检查了它在做什么。这就是堆栈的样子。 “OurOwnClass.doStillMoreSomething”是我们自己代码中正在执行的最后一行,它创建了一个类的新实例。
    Thread [AWT-EventQueue-2] (Suspended)
        Inet6AddressImpl.lookupAllHostAddr(String) line: not available [native method]
        InetAddress$1.lookupAllHostAddr(String) line: not available
        InetAddress.getAddressFromNameService(String, InetAddress) line: not available
        InetAddress.getAllByName0(String, InetAddress, boolean) line: not available
        InetAddress.getAllByName(String, InetAddress) line: not available
        InetAddress.getAllByName(String) line: not available
        InetAddress.getByName(String) line: not available
        Handler(URLStreamHandler).getHostAddress(URL) line: not available
        Handler(URLStreamHandler).hostsEqual(URL, URL) line: not available
        Handler(URLStreamHandler).sameFile(URL, URL) line: not available
        Handler(URLStreamHandler).equals(URL, URL) line: not available
        URL.equals(Object) line: not available
        JarVerifier$VerifierCodeSource(CodeSource).equals(Object) line: not available
        JarVerifier$VerifierCodeSource.equals(Object) line: not available
        HashMap<K,V>.getEntry(Object) line: not available
        HashMap<K,V>.containsKey(Object) line: not available
        HashSet<E>.contains(Object) line: not available
        CPCallbackHandler.isTrusted(CodeSource) line: not available
        CPCallbackHandler.access$1200(CPCallbackHandler, CodeSource) line: not available
        CPCallbackHandler$ChildElement.checkResource(String) line: not available
        DeployURLClassPath$JarLoader.checkResource(String, boolean, JarEntry, JarFile, DeployURLClassPath$PathIterator) line: not available
        DeployURLClassPath$JarLoader.getResource(String, boolean, DeployURLClassPath$PathIterator) line: not available
        DeployURLClassPath.getResource(String, boolean) line: not available
        Plugin2ClassLoader$2.run() line: not available
        AccessController.doPrivileged(PrivilegedExceptionAction<T>, AccessControlContext) line: not available [native method]
        Applet2ClassLoader(Plugin2ClassLoader).findClassHelper(String) line: not available
        Applet2ClassLoader.findClass(String, boolean) line: not available
        Applet2ClassLoader(Plugin2ClassLoader).loadClass0(String, boolean, boolean) line: not available
        Applet2ClassLoader(Plugin2ClassLoader).loadClass(String, boolean, boolean) line: not available
        Applet2ClassLoader(Plugin2ClassLoader).loadClass(String, boolean) line: not available
        Applet2ClassLoader(ClassLoader).loadClass(String) line: not available
        OurOwnClass.doStillMoreSomething(String) line: 701
        OurOwnClass.doMoreSomething() line: 671
        OurOwnClass.doSometihng() line: 655
        OurOwnClass.handleTimer() line: 510
        OurOwnClass.actionPerformed(ActionEvent) line: 391
        Timer.fireActionPerformed(ActionEvent) line: not available
        Timer$DoPostEvent.run() line: not available
        InvocationEvent.dispatch() line: not available
        EventQueue.dispatchEventImpl(AWTEvent, Object) line: not available
        EventQueue.access$400(EventQueue, AWTEvent, Object) line: not available
        EventQueue$2.run() line: not available
        EventQueue$2.run() line: not available
        AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]
        AccessControlContext$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: not available
        EventQueue.dispatchEvent(AWTEvent) line: not available
        EventDispatchThread.pumpOneEventForFilters(int) line: not available
        EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: not available
        EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: not available
        EventDispatchThread.pumpEvents(int, Conditional) line: not available
        EventDispatchThread.pumpEvents(Conditional) line: not available
        EventDispatchThread.run() line: not available
    

    小程序将冻结几行代码。直到我们的代码中的第一行为止,堆栈看起来都是相同的(上面:OurOwnClass.doStillMoreSomething),因此看起来所有冻结都具有相同的原因。我们所有导致此问题的行都在创建新对象。我相信在所有这些情况下,这也是第一次加载特定的类,尽管我没有对此进行检查。其中一个类是我们自己的公共类,但其中大多数是匿名类,其名称类似于OurOwnClass $ 4,其代码如下:
    worker = new SwingWorker<Void,Void>() {
        @Override
        public Void doInBackground() {
            // Do stuff.
            return null;
        }
    };
    

    堆栈顶部的Java方法是Inet6AddressImpl.lookupAllHostAddr。调试器指示此方法正在尝试解析为该applet提供服务的主机的主机名。我想我在某个地方看到了applet JAR文件的完整URL,因此给人的印象是JVM试图再次访问applet JAR文件,尽管它本来应该已经拥有了–它还在执行什么?

    分析

    我最好的猜测是,类加载器需要对即将加载的类进行某种权限检查。 JVM可能正在尝试检查自最初加载JAR文件以来是否已更新,但是JVM在尝试使用本机方法解析服务器的主机名时挂起。当地址解析失败时,将返回类加载器的执行。所有这些都有些奇怪,因为applet可以很好地与我们的服务器通信(它从浏览器获取代理信息),但是我感到JVM在这里所做的任何事情都不使用代理。这个解释听起来合理吗?

    再说一次,我不知道如何解释崩溃以及使applet首先启动的问题。我没有花时间尝试调试这些问题,所以我不知道发生了什么。如果将来我仍然去客户办公室,也许我应该尝试寻找hs_err _ *。log文件。

    如果我有类似客户的设置,这将有助于调试。我可能会尝试建立一个只能使用HTTP代理访问Internet的环境,但我不知道它是否必须是Blue Coat代理。我也不确定是否可以使代理服务器使用NTLM协议而不是其他方法(基本,摘要)进行身份验证。

    无论如何,我希望能帮助您摆脱冻结问题(如果可能的话,还有崩溃和启动问题)。我们可以通过某种方式更改代码或JAR包来避免这些问题吗?是否有一些JVM启动参数可以帮助您?我还应该做些什么来调查这个问题?

    编辑

    我更改了Windows虚拟机的网络设置,以使其只能与主机Linux通信,而不能与整个Internet通信。然后,我在主机Linux上设置了Squid HTTP代理,并告诉Windows将其用作代理。现在,我遇到了类似的问题,例如在客户办公室发生的那些问题,尽管不那么可靠。看来我不需要使代理服务器需要身份验证。

    编辑2

    我使用了模拟环境,并启动了Wireshark,以查看Windows guest虚拟机正在尝试做什么。显然,它正在与主机打乒乓球:它向IP请求我们的服务器,并返回响应“目标不可达(端口不可达)”(类型3,代码3)。这发生五次,并且它们之间的间隔为1、1、2和4秒。然后,在4秒钟后,小程序继续快乐地执行所执行的操作。间隔总计为12秒,这恰好是最常见的延迟长度。我不知道客户办公室的网络是否响应主机不可达或其他问题。 Ping会在该处以及我的此模拟设置中都超时。

    我想知道这是JVM中的错误还是设计错误。我通常不会期望实例化一个类会使线程冻结一段时间。我也希望classloader / JVM使用代理。通常,我认为尝试几次主机名解析是有意义的,但是我不确定在这种情况下这样做是否正确。

    编辑3

    一个评论者是对的:是操作系统在打乒乓球。我通过捕获ping www.google.com生成的网络流量来尝试了此操作,并得到了相同的行为。

    最佳答案

    查看URLStreamHandler中有问题的JRE代码:

    protected synchronized InetAddress getHostAddress(URL u) {
        if (u.hostAddress != null)
            return u.hostAddress;
    
        String host = u.getHost(); 
        if (host == null || host.equals("")) {
            return null;
        } else {
            try {
                u.hostAddress = InetAddress.getByName(host); // Hanging here
            } catch (UnknownHostException ex) {
                return null;
            } catch (SecurityException se) {
                return null;
            }
        }
        return u.hostAddress;
    }
    

    如果此代码失败并返回null,则父hostsEqual方法将返回String比较,这很好。因此,我们需要UnknownHostExceptionSecurityException

    看起来防火墙正在丢弃DNS请求,从而导致超时,而不是返回NXDOMAIN(或更妙的是,ICMP在管理上被禁止)。

    您能否说服客户端正确配置防火墙? (您可以通过在Ubuntu盒子上阻止传出DNS(代理以外的DNS)进行测试)

    另一种选择是强制SecurityExceptionInetAddress.getByName抛出,这可能会发生:

    如果安全管理器存在并且其checkConnect方法不允许
    操作

    我不知道该怎么做或是否可能-您可以更改小程序的权限吗?

    关于java - 实例化新类时,代理后面的Java applet会暂时卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13025957/

    相关文章:

    java - Java Applet:基本鼓组

    java - 在项目的构建路径中检测到一个循环...eclipse

    java - 如何让 log4j 与 jetty 一起工作?

    c# - 使用 .NET sslstream 了解服务器/代理/客户端证书

    .htaccess - 将代理 https url 重写为 http 是否安全?

    java - 将 .jar 部署到 html 页面中

    java - Springboot和IDEA错误: Could not autowire.没有找到 'EntityLinks'类型的bean

    java - 出现错误 java.lang.NullPointerException : Attempt to invoke virtual method 'double android.location.Location.getLatitude()'

    c# - 通过代理连接到 IRC (.NET)

    java - XSS 和 Applet/Param HTML 关键字