Details
-
Bug
-
Status: Open
-
Trivial
-
Resolution: Unresolved
-
None
-
None
-
None
Description
I'm not sure if you guys ever formally released Felix with Android support for versions greater than 5.10.0. But I collaborated, with Karl Pauls on a 6.0.1 version that actually works really well on Android. I finally found a problem in using it with Android, but there's an easy workaround that can be externally applied. I'll describe the problem and the fix just for documentation purposes.
The problem is with the method
private URLStreamHandler loadBuiltInStreamHandler(String protocol, ClassLoader classLoader)
located in the class: org.apache.felix.framework.URLHandlers
At the bottom of that method, are the adjustments made for it to work with Android. The handler class names are no longer valid for Android. For http and https the classes should be com.android.okhttp.HttpHandler and com.android.okhttp.HttpsHandler. For the file, jar, and ftp protocols, it uses sun.net.www.protocol.<protocol>.Handler. I would have thought that those handlers for them would have resolved earlier in that method. but the call to newInstance() fails for the reason that it can't find a constructor which doesn't take no arguments. When I use reflection to look at Consructors or DeclaredConstructors for those handler class I get a zero length array back. I don't understand why that's the case, but it not import because here's what I did to solve the problem.:
Before starting up Felix I created my own URLStreamHandlerFactory class and the constructor for that class calls URL.setURLStreamHandlerFactory. Here's the code I used to do that:
private class InternalURLStreamFactory implements URLStreamHandlerFactory { URLStreamHandler fileHandler = null; URLStreamHandler jarHandler = null; URLStreamHandler ftpHandler = null; URLStreamHandler httpHandler = null; URLStreamHandler httpsHandler = null; private InternalURLStreamFactory () { boolean fail = false; fileHandler = getHandler("file://test"); jarHandler = getHandler("jar:file:/home/duke/duke.jar!/"); ftpHandler = getHandler("ftp://myname@host.dom/%2Fetc/motd"); httpHandler = getHandler("http://headstone/service/MyPage.html"); httpsHandler = getHandler("https://flintstone/service/MySecurePage.html"); URL.setURLStreamHandlerFactory(this); } private URLStreamHandler getHandler (String exampleURL) { try { URL url = new URL (exampleURL); Field handlerField = url.getClass().getDeclaredField("handler"); handlerField.setAccessible(true); return (URLStreamHandler) handlerField.get(url); } catch (Exception e) { } return null; } @Override public URLStreamHandler createURLStreamHandler(String protocol) { URLStreamHandler handler = null; switch (protocol.toLowerCase()) { case "file": handler = fileHandler; break; case "jar": handler = jarHandler; break; case "ftp": handler = ftpHandler; break; case "http": handler = httpHandler; break; case "https": handler = httpsHandler; break; } return handler; } }
As you can see, I get all the handlers for the supported protocols by generating a dummy URL command for each protocol, extracting the hander from the dummy URL object, and caching handler for each supported protocol. This cached value is passed back when the createURLStream handler is called. Again, this needs to be done before starting Felix. Felix will first use the URLStreamFactory if one is available , before it begins its time consuming hunt for a handler.