在 Android 中为 ResourceBundle.getBundle(String path) 放置资源的位置?
Where to put resources in Android for ResourceBundle.getBundle(String path)?
我在我的 android 项目中使用了一个 ee-jars。它使用 ResourceBundle.getBundle(String path)
作为 i18n。由于资源在 jar 中被剥离,而 .apk 不包含资源,我遇到了一个异常:
java.util.MissingResourceException: Can't find resource for bundle 'org/apache/xml/security/resource/xmlsecurity_en_US', key ''
at java.util.ResourceBundle.missingResourceException(ResourceBundle.java:239)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:231)
at org.apache.wss4j.common.crypto.WSS4JResourceBundle.<init>(WSS4JResourceBundle.java:54)
at org.apache.wss4j.common.crypto.WSProviderConfig.initializeResourceBundles(WSProviderConfig.java:199)
at org.apache.wss4j.common.crypto.WSProviderConfig.init(WSProviderConfig.java:65)
at org.apache.wss4j.dom.WSSConfig.init(WSSConfig.java:428)
at org.apache.wss4j.dom.WSSConfig.getNewInstance(WSSConfig.java:435)
at org.apache.wss4j.dom.WSSecurityEngine.getWssConfig(WSSecurityEngine.java:148)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessageInternal(WSS4JInInterceptor.java:215)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:190)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:96)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:122)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:243)
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:261)
at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:70)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1088)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1024)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:982)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1043)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:841)
我该怎么做才能修复它?我的想法是从 jar 中获取资源并将它们放入 Android 项目的 resources 文件夹中。它的正确位置在哪里? Android 确实支持 ResourceBundle.getBundle(String path)
(reference),但没有写出资源的放置位置。
我不得不:
- 引入资源加载器接口(
WSS4JResourceBundle.ResourceBundleLoader
)
- 引入静态资源加载器变量加载资源(
public static ResourceBundleLoader sharedLoader
)
- 将使用
ResourceBundle.getBundle
的加载资源替换为静态 var 方法调用 (wss4jSecResourceBundle = sharedLoader.getBundle("messages.wss4j_errors");
f.e.)
- 创建默认值 (
DefaultResourceBundleLoader
) 和 android 实现 (AndroidResourceBundleLoader
)
- 重新打包资源到android的项目
assets
文件夹
- 在应用程序启动时注入 android 实现而不是默认实现 (
WSS4JResourceBundle.sharedLoader = new AndroidResourceBundleLoader(ctx, "wss4j/");
)
修改WSS4JResourceBundle
:
package org.apache.wss4j.common.crypto;
import java.util.Enumeration;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.I18n;
/**
* ResourceBundle for WSS4J
*/
public class WSS4JResourceBundle extends ResourceBundle {
/**
* Loader API
*/
public interface ResourceBundleLoader {
ResourceBundle getBundle(String path);
ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader);
}
// default
public static ResourceBundleLoader sharedLoader = new DefaultResourceBundleLoader();
/**
* Default IMPL
*/
public static class DefaultResourceBundleLoader implements ResourceBundleLoader {
@Override
public ResourceBundle getBundle(String path) {
return ResourceBundle.getBundle(path);
}
@Override
public ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader) {
return ResourceBundle.getBundle(baseName, locale, loader);
}
}
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(WSS4JResourceBundle.class);
private final ResourceBundle wss4jSecResourceBundle;
private final ResourceBundle xmlSecResourceBundle;
public WSS4JResourceBundle() {
wss4jSecResourceBundle = sharedLoader.getBundle("messages.wss4j_errors");
ResourceBundle tmpResourceBundle;
try {
tmpResourceBundle = sharedLoader.getBundle(
Constants.exceptionMessagesResourceBundleBase,
Locale.getDefault(),
I18n.class.getClassLoader());
} catch (MissingResourceException ex) {
// Using a Locale of which there is no properties file.
LOG.debug(ex.getMessage());
// Default to en/US
tmpResourceBundle =
sharedLoader.getBundle(Constants.exceptionMessagesResourceBundleBase,
new Locale("en", "US"), I18n.class.getClassLoader());
}
xmlSecResourceBundle = tmpResourceBundle;
}
@Override
protected Object handleGetObject(String key) {
Object value = null;
try {
value = wss4jSecResourceBundle.getObject(key);
} catch (MissingResourceException e) {
try {
value = xmlSecResourceBundle.getObject(key);
} catch (MissingResourceException ex) { //NOPMD
//ignore
}
}
return value;
}
@Override
public Enumeration<String> getKeys() {
throw new UnsupportedOperationException("getKeys not supported");
}
}
新 AndroidResourceBundleLoader
:
package org.apache.wss4j.common.crypto;
import android.content.Context;
import android.content.res.AssetManager;
import com.splinex.streaming.Log;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
public class AndroidResourceBundleLoader implements WSS4JResourceBundle.ResourceBundleLoader {
private static final String TAG = AndroidResourceBundleLoader.class.getSimpleName();
private String prefix;
private AssetManager manager;
public AndroidResourceBundleLoader(Context context, String prefix) {
manager = context.getAssets();
this.prefix = prefix;
}
@Override
public ResourceBundle getBundle(String path) {
try {
String fullPath = prefix + path.replace(".", "/") + ".properties";
return new PropertyResourceBundle(manager.open(fullPath));
} catch (IOException e) {
android.util.Log.e(TAG, "Failed to load " + path, e);
throw new RuntimeException(e);
}
}
@Override
public ResourceBundle getBundle(String path, Locale locale, ClassLoader classLoader) {
return getBundle(path + "_" + locale.getLanguage());
}
}
像这样注入 android 实现:
WSS4JResourceBundle.sharedLoader = new AndroidResourceBundleLoader(ctx, "wss4j/");
我在我的 android 项目中使用了一个 ee-jars。它使用 ResourceBundle.getBundle(String path)
作为 i18n。由于资源在 jar 中被剥离,而 .apk 不包含资源,我遇到了一个异常:
java.util.MissingResourceException: Can't find resource for bundle 'org/apache/xml/security/resource/xmlsecurity_en_US', key ''
at java.util.ResourceBundle.missingResourceException(ResourceBundle.java:239)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:231)
at org.apache.wss4j.common.crypto.WSS4JResourceBundle.<init>(WSS4JResourceBundle.java:54)
at org.apache.wss4j.common.crypto.WSProviderConfig.initializeResourceBundles(WSProviderConfig.java:199)
at org.apache.wss4j.common.crypto.WSProviderConfig.init(WSProviderConfig.java:65)
at org.apache.wss4j.dom.WSSConfig.init(WSSConfig.java:428)
at org.apache.wss4j.dom.WSSConfig.getNewInstance(WSSConfig.java:435)
at org.apache.wss4j.dom.WSSecurityEngine.getWssConfig(WSSecurityEngine.java:148)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessageInternal(WSS4JInInterceptor.java:215)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:190)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:96)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:122)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:243)
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:261)
at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:70)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1088)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1024)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:982)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:1043)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:865)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:240)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:841)
我该怎么做才能修复它?我的想法是从 jar 中获取资源并将它们放入 Android 项目的 resources 文件夹中。它的正确位置在哪里? Android 确实支持 ResourceBundle.getBundle(String path)
(reference),但没有写出资源的放置位置。
我不得不:
- 引入资源加载器接口(
WSS4JResourceBundle.ResourceBundleLoader
) - 引入静态资源加载器变量加载资源(
public static ResourceBundleLoader sharedLoader
) - 将使用
ResourceBundle.getBundle
的加载资源替换为静态 var 方法调用 (wss4jSecResourceBundle = sharedLoader.getBundle("messages.wss4j_errors");
f.e.) - 创建默认值 (
DefaultResourceBundleLoader
) 和 android 实现 (AndroidResourceBundleLoader
) - 重新打包资源到android的项目
assets
文件夹 - 在应用程序启动时注入 android 实现而不是默认实现 (
WSS4JResourceBundle.sharedLoader = new AndroidResourceBundleLoader(ctx, "wss4j/");
)
修改WSS4JResourceBundle
:
package org.apache.wss4j.common.crypto;
import java.util.Enumeration;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.I18n;
/**
* ResourceBundle for WSS4J
*/
public class WSS4JResourceBundle extends ResourceBundle {
/**
* Loader API
*/
public interface ResourceBundleLoader {
ResourceBundle getBundle(String path);
ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader);
}
// default
public static ResourceBundleLoader sharedLoader = new DefaultResourceBundleLoader();
/**
* Default IMPL
*/
public static class DefaultResourceBundleLoader implements ResourceBundleLoader {
@Override
public ResourceBundle getBundle(String path) {
return ResourceBundle.getBundle(path);
}
@Override
public ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader) {
return ResourceBundle.getBundle(baseName, locale, loader);
}
}
private static final org.slf4j.Logger LOG =
org.slf4j.LoggerFactory.getLogger(WSS4JResourceBundle.class);
private final ResourceBundle wss4jSecResourceBundle;
private final ResourceBundle xmlSecResourceBundle;
public WSS4JResourceBundle() {
wss4jSecResourceBundle = sharedLoader.getBundle("messages.wss4j_errors");
ResourceBundle tmpResourceBundle;
try {
tmpResourceBundle = sharedLoader.getBundle(
Constants.exceptionMessagesResourceBundleBase,
Locale.getDefault(),
I18n.class.getClassLoader());
} catch (MissingResourceException ex) {
// Using a Locale of which there is no properties file.
LOG.debug(ex.getMessage());
// Default to en/US
tmpResourceBundle =
sharedLoader.getBundle(Constants.exceptionMessagesResourceBundleBase,
new Locale("en", "US"), I18n.class.getClassLoader());
}
xmlSecResourceBundle = tmpResourceBundle;
}
@Override
protected Object handleGetObject(String key) {
Object value = null;
try {
value = wss4jSecResourceBundle.getObject(key);
} catch (MissingResourceException e) {
try {
value = xmlSecResourceBundle.getObject(key);
} catch (MissingResourceException ex) { //NOPMD
//ignore
}
}
return value;
}
@Override
public Enumeration<String> getKeys() {
throw new UnsupportedOperationException("getKeys not supported");
}
}
新 AndroidResourceBundleLoader
:
package org.apache.wss4j.common.crypto;
import android.content.Context;
import android.content.res.AssetManager;
import com.splinex.streaming.Log;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
public class AndroidResourceBundleLoader implements WSS4JResourceBundle.ResourceBundleLoader {
private static final String TAG = AndroidResourceBundleLoader.class.getSimpleName();
private String prefix;
private AssetManager manager;
public AndroidResourceBundleLoader(Context context, String prefix) {
manager = context.getAssets();
this.prefix = prefix;
}
@Override
public ResourceBundle getBundle(String path) {
try {
String fullPath = prefix + path.replace(".", "/") + ".properties";
return new PropertyResourceBundle(manager.open(fullPath));
} catch (IOException e) {
android.util.Log.e(TAG, "Failed to load " + path, e);
throw new RuntimeException(e);
}
}
@Override
public ResourceBundle getBundle(String path, Locale locale, ClassLoader classLoader) {
return getBundle(path + "_" + locale.getLanguage());
}
}
像这样注入 android 实现:
WSS4JResourceBundle.sharedLoader = new AndroidResourceBundleLoader(ctx, "wss4j/");