Issue
I'm using one of ee-jars in my android project. It uses ResourceBundle.getBundle(String path)
for i18n. Since resources are stripped in jar and .apk does not contain resources and i'm getting an exception:
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$1.run(SelectChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:841)
What can i do in order to fix it? My idea is to get resources from jar and put them into resources folder in Android project. Where is the correct place for it? Android does support ResourceBundle.getBundle(String path)
(reference) but it's not written where to put the resources.
Solution
I had to:
- introduce resource loader interface (
WSS4JResourceBundle.ResourceBundleLoader
) - introduce static resource loader variable to load resources (
public static ResourceBundleLoader sharedLoader
) - replace loading resource using
ResourceBundle.getBundle
to static var method invocation (wss4jSecResourceBundle = sharedLoader.getBundle("messages.wss4j_errors");
f.e.) - create default (
DefaultResourceBundleLoader
) and android implementation (AndroidResourceBundleLoader
) - repackage resources to android's project
assets
folder - inject android impl instead of default one on app launch (
WSS4JResourceBundle.sharedLoader = new AndroidResourceBundleLoader(ctx, "wss4j/");
)
modified 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");
}
}
new 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());
}
}
Inject android impl like this:
WSS4JResourceBundle.sharedLoader = new AndroidResourceBundleLoader(ctx, "wss4j/");
Answered By - 4ntoine
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.