1

JWS 1.6.0_29が1.4.2_12アプリケーションの起動に失敗した理由を最終的に理解できた後(この質問を参照)、1.4.2_12アプリを起動するときに別の例外に直面しました。JWS1.6.0_29を使用。

ResourceBundleをロードすると、MissingResourceExceptionが発生します。ただし、me​​ssage.propertiesファイルは、それをロードしているクラスと同じパッケージに存在します。

JWS 1.4または1.5を使用してアプリケーションを起動する場合、例外は発生しません。
例外は、アプリの起動時にのみ発生します。JWS1.6を使用。

完全なstackstraceは:

java.lang.ExceptionInInitializerError
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javaws.Launcher.executeApplication(Unknown Source)
    at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
    at com.sun.javaws.Launcher.doLaunchApp(Unknown Source)
    at com.sun.javaws.Launcher.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.util.MissingResourceException: Can't find bundle for base name com.test.hello.messages, locale fr_FR
    at java.util.ResourceBundle.throwMissingResourceException(Unknown Source)
    at java.util.ResourceBundle.getBundleImpl(Unknown Source)
    at java.util.ResourceBundle.getBundle(Unknown Source)
    at com.test.hello.Main.<clinit>(Main.java:10)
    ... 9 more

再現するテストケース

JNLP記述子は次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:80/meslegacy/apps" href="testJwsXXTo142.jnlp">
    <information>
        <title>JWS TEST 1.6 -> 1.4.2</title>
        <vendor>Hello World Vendor</vendor>
        <description>Hello World</description>
    </information>

    <security>
        <all-permissions />
    </security>

    <resources>
        <j2se version="1.4.2_12" href="http://java.sun.com/products/autodl/j2se" />
        <jar href="jar/helloworld.jar" main="true" />
    </resources>

    <application-desc main-class="com.test.hello.Main" />
</jnlp>

com.test.hello.Mainクラスは次のとおりです。

package com.test.hello;

import java.util.ResourceBundle;

import javax.swing.JFrame;

public class Main {

    private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(Main.class.getPackage().getName()+".messages");

    public static void main(String[] args) {
        JFrame frame = new JFrame("Hello world !");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800,600);
        frame.setVisible(true);
    }

}

補完テスト

  • ResourceBundle.getBundle()メソッドにClassLoaderとLocaleを指定しても、問題は解決しません。
  • Main.class.getClassLaoder()とThread.currentThread()。getContextClassLaoder()がテストされ、同じ例外が発生します。
  • 「手動で」リソースをロードすることは機能します(以下を参照)。

リソースを手動でロードするためのテストコード:

ClassLoader cl = Main.class.getClassLoader();
String resourcePath = baseName.replaceAll("\\.", "/");
System.out.println(resourcePath);
URL resourceUrl = cl.getResource(resourcePath+".properties");
System.out.println("Resource manually loaded :"+resourceUrl);

生成します:

com/test/hello/messages.properties
Resource manually loaded :jar:http://localhost:80/meslegacy/apps/jar/helloworld.jar!/com%2ftest%2fhello%2fmessages.properties
  • ただし、リソースを見つけることは可能ですが、リソースコンテンツを取得することはできません。

例:

ClassLoader cl = Main.class.getClassLoader();
String resourcePath = baseName.replaceAll("\\.", "/") + ".properties";
URL resourceUrl = cl.getResource(resourcePath);
// here, resourceUrl is not null. Then build bundle by hand
ResourceBundle prb = new PropertyResourceBundle(resourceUrl.openStream());

どのスポーン:

java.io.FileNotFoundException: JAR entry com%2ftest%2fhello%2fmessages.properties not found in C:\Documents and Settings\firstname.lastname\Application Data\Sun\Java\Deployment\cache\6.0\18\3bfe5d92-3dfda9ef
    at com.sun.jnlp.JNLPCachedJarURLConnection.connect(Unknown Source)
    at com.sun.jnlp.JNLPCachedJarURLConnection.getInputStream(Unknown Source)
    at java.net.URL.openStream(Unknown Source)
    at com.test.hello.Main.main(Main.java:77)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javaws.Launcher.executeApplication(Unknown Source)
    at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
    at com.sun.javaws.Launcher.doLaunchApp(Unknown Source)
    at com.sun.javaws.Launcher.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

より一種のキャッシュの問題のようです...

私はあなたの誰かがヒントを持っていました、それは大いにありがたいです、

読んでくれてありがとう。

4

1 に答える 1

1

この問題の説明と回避策は次のとおりです。

1-説明

この問題は、システムClassCloader(JWS6システムClassLoader)によって返されるURLに起因します。

JWS 1.6では、システムClassLoaderによって返されるURLには、次のようなエスケープシーケンスが含まれています。

jar:http://localhost:80/meslegacy/apps/jar/helloworld.jar!/com%2ftest%2fhello%2fmessages.properties

クラスパスでリソースを見つけることは可能ですが、そのリソースのコンテンツに実際にアクセスする場合、FileNotFoundExceptionが発生します。これがResourceBundleのFileNotFoundExceptionの原因です。

リソースがクラスパスのルートにある場合など、URLにエスケープシーケンスが表示されない場合は、リソースコンテンツにアクセスするのに問題がないことに注意してください。問題は、URLパス部分に%xxのものを取得した場合にのみ発生します。

2-回避策

問題に焦点が当てられたら(これを理解するのに数日かかりました!)、これに対する回避策を見つける時が来ました。特定のローカライズされたコード部分で問題を修正することは可能でしたが、特定のClassLoaderをコーディングしてJNLPClassLoaderを「置き換える」ことで、問題をグローバルに修正できることがすぐにわかりました。私には不可能に思えるので、私は実際に「交換」しませんが、私はむしろ次のことをします:

  1. SecurityManagerを無効にして、カスタムクラスローダーで再生できるようにします
  2. URLを返すときにURLを修正するURLClassLoaderから派生した独自のクラスローダーをコーディングします
  3. JNLPClassLoaderから抽出されたクラスパスを使用してクラスパスを設定します
  4. このカスタムクラスローダーをコンテキストクラスローダーに設定します
  5. このカスタムクラスローダーをAWT-Event-Threadコンテキストクラスローダーに設定します
  6. このカスタムクラスローダーを使用して、アプリケーションのエントリポイントをロードします。

これにより、次のClassLoaderが提供されます

public class JwsUrlFixerClassLoader extends URLClassLoader {

    private final static Logger LOG = Logger.getLogger(JwsUrlFixerClassLoader.class);

    private static String SIMPLE_CLASS_NAME = null;

    private static boolean LOG_ENABLED = "true".equals(System.getProperty("classloader.debug"));

    static {
        SIMPLE_CLASS_NAME = JwsUrlFixerClassLoader.class.getName();
        int idx = SIMPLE_CLASS_NAME.lastIndexOf('.');
        if (idx >= 0 && idx < SIMPLE_CLASS_NAME.length()-1) {
            SIMPLE_CLASS_NAME = SIMPLE_CLASS_NAME.substring(idx + 1);
        }
    }

    public JwsUrlFixerClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    public URL getResource(String name) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("getResource(): getResource(" + name + ")");
        }
        if (LOG_ENABLED) {
            login("getResource(" + name + ")");
        }
        URL out = super.getResource(name);
        if (out != null) {
            out = URLFixerTool.fixUrl(out);
        }
        if (LOG_ENABLED) {
            logout("getResource returning " + out);
        }
        return out;
    }

    public URL findResource(String name) {
        if (LOG_ENABLED) {
            login("findResource(" + name + ")");
        }
        URL out = super.findResource(name);
        if (out != null) {
            out = URLFixerTool.fixUrl(out);
        }
        if (LOG_ENABLED) {
            logout("findResource returning " + out);
        }
        return out;
    }

    public InputStream getResourceAsStream(String name) {
        if (LOG_ENABLED) {
            login("getResourceAsStream(" + name + ")");
        }
        InputStream out = super.getResourceAsStream(name);
        if (LOG_ENABLED) {
            logout("getResourceAsStream returning " + out);
        }
        return out;
    }

    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (LOG_ENABLED) {
            login("loadClass(" + name + ")");
        }
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            try {
                c = findClass(name);
            } catch (ClassNotFoundException cnfe) {
                if (getParent() == null) {
                    // c = findBootstrapClass0(name);
                    Method m = null;
                    try {
                        m = URLClassLoader.class.getMethod("findBootstrapClass0", new Class[] {});
                        m.setAccessible(true);
                        c = (Class) m.invoke(this, new Object[] { name });
                    } catch (Exception e) {
                        throw new ClassNotFoundException();
                    }
                } else {
                    c = getParent().loadClass(name);
                }
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        if (LOG_ENABLED) {
            logout("loadClass returning " + c);
        }
        return c;
    }

    private static void login(String message) {
        System.out.println("---> [" + Thread.currentThread().getName() + "] " + SIMPLE_CLASS_NAME + ": " + message);
    }

    private static void logout(String message) {
        System.out.println("<--- [" + Thread.currentThread().getName() + "] " + SIMPLE_CLASS_NAME + ": " + message);
    }

}

JNLP記述子のメインクラスとして設定したAppBoostrapクラスで、次のようにします。

    System.setSecurityManager(null);
    ClassLoader parentCL = AppBootstrap.class.getClassLoader();
    URL[] classpath = new URL[] {};
    if (parentCL instanceof URLClassLoader) {
        URLClassLoader ucl = (URLClassLoader) parentCL;
        classpath = ucl.getURLs();
    }
    final JwsUrlFixerClassLoader vlrCL = new JwsUrlFixerClassLoader(classpath, parentCL);
    Thread.currentThread().setContextClassLoader(vlrCL);
    try {
        SwingUtilities.invokeAndWait(new Runnable() {

            public void run() {
                Thread.currentThread().setContextClassLoader(vlrCL);
            }
        });
    } catch (Exception e) {
        LOG.error("main(): Failed to set context classloader !", e);
    }

前の抜粋では、AppBootstrapクラスをロードしたClassLoaderを取得し、それをJwsUrlFixerClassLoaderの親クラスローダーとして使用しています。

URLClassLodaer.loadClass()のデフォルトの親委任戦略の問題を修正し、「最初にクラスパスを試し、次に親を試す」に置き換える必要がありました。

それが行われた後、すべてがうまくいき、これまで説明できなかった他のいくつかのバグが消えました。

それは魔法です!でもたくさんの痛みの後...

これがいつか誰かを助けることができることを願っています...

于 2012-10-05T15:56:38.120 に答える