I'm trying to load external classes dynamically in an Android app, using a proxy object to intercept calls to the methods. For definition, for use a proxy object, classes must implement an interface. I thought if I create a proxy with an interface I will be able to call classes that not implement it directly. I tried use the same package name and interface implemented in the external class, but it doesn't work.
I have tried some solutions, but I'm not able to load classes dynamically because I receive a ClassCastException.
So I want to intercept object calls and do a function that is implemented in a external apk, using DexClassLoader to load it. I think it is not possible using proxy pattern.
Any ideas?
Related
I have a library class that is a singleton and does NOT implement any interface (So I'm assuming I cannot use dynamic proxies). But, I need the same functionality as the dynamic proxy. I need to hijack the call, log some data and pass on the call to the library as is.
Is it possible to do this without having to use AspectJ or Spring AOP? (I'm not sure how to use these frameworks, but I will have to look into it if there is no other way).
You can provide your own implementation of the same class, with the same name and package, and try to put it into classpath first. The calling code with such classpath will pick the intercepting class first, and the intercepting class can call others classes of the actual package to provide its actual functionality.
Best would be to remove the overridden class from the library .jar with archive tool.
You could change the import statements in the classes that call f() so that they see a different class that implements f(). The implementation of that class would do the logging and call the real f().
Still requires some editing, but less than changing every call.
BTW: Depending on the size of the project, you may want to consider making wrappers to that "black box" anyway, if this type of requirement will be ongoing.
Like I started in my comment wrapper all the classes you implement from the external library. Then call the external library from your own classes this way you can log in the function(s) you want. If you use the same function name then you don't have to change what you call you only have to change your import(s). Most IDEs provide mass name replace so it shouldn't be too big of a burden it will be tedious however.
I know cglib proxying works by subclassing target class and overriding target class' methods.
Can anyone tell how exactly dynamic proxy works?
I know it uses interface for proxying, but how exactly method invokation happens through proxy?
Using Proxy.newProxyInstance() you can ask for a proxy implementing required interfaces. You need to pass an InvocationHandler too, which is called every time you call any proxy method. Then, in your handler, you know which method is called and its parameters, so you can do what you desire, including using a target object.
How does Java handle this? Well, it's done natively, just as the internals of reflection and a lot of basic functionality. So, you can emulate this behavior using plain Java.
Extended info here.
I'm making an app in conjunction with a websites API, and in the application I created a class that would deal with the OAuth authentication and API calls. Now, how would I be able to instantiate an object from that class, and share the object across various Activities? I'm quite sure what would be the best practice in a situation like this.
Thanks a bunch!
You could create a subclass of Application and store your authorization module there. This would be accessible to all of your activities within that process.
You just need to declare you Application subclass in your manifest and it will be instantiated instead of the default.
From your Activity, you would call getApplication() and cast it to the correct subclass type and access your custom methods
It seems like everybody has had an unpleasant brush with the Java Service Provider, that thing you can do with a file named like META-INF/services/com.example.Interface, but that nobody uses except for trying to load the right XML parser. I'm trying to work with a library that uses the Service Provider API, and trick it so that I can provide some runtime-extended classes (using cglib) that don't actually implement the interface but can be made to do so easily.
Basically, I think the steps I need to perform are:
Create a custom class loader that will respond to getResources(...) and return an "extra" URL
Also have that class loader hook getResourceAsStream(...) to return a list of the classes I am going to manipulate with cglib, when asked for the "extra" resource
Finally, have that class loader load those classes when requested
But here's where I get lost. For example, when the library tries to determine what implementers are out there, it calls getResources(...) which returns a bunch of URLs. But getResourceAsStream(...) doesn't take URLs, it takes "names". Names which seem to be classpath-relative, and therefore the same everywhere. So META-INF/services/com.example.Interface in has the same "name" as META-INF/services/com.example.Interface in their JAR, right? Except somehow this works with those blasted XML parsers...
Of course, all of this assumes they were smart/kind enough to call ClassLoader.getSystemClassLoader() rather than using ClassLoader.getSystemResources(...), ClassLoader.getSystemResourceAsStream(...), etc., as in the latter case there's no way to hook the ClassLoader and provide the faked file.
I guess in that case I could use BCEL to manipulate the class files when my code is being packaged by Maven, rather than waiting until runtime to do it with cglib?
The idea I described was along the right track. The mistake I made was in thinking that using ClassLoader.getResourceAsStream(..) to access the contents of the URLs. Instead, you should just URL.openStream().
Had I found it before posting, java.util.ServiceLoader (#since 1.6) provides some insight into how to do things correctly.
I am working on a big (lots of classes) java project and I have it's source code but most of the classes are dynamically created or downloaded via ClassLoaders.
Anyways, I'd like to be able to "override" the java.net.URL class so I could trace down the calls to open a url. I can not use a sniffer because the content is SSL encrypted.
I tried to extend the java.net.URL but it didn't work because its a final class.
I also opened the source to java.net.URL and modified it and could successfully build it, now how can I make the default java classloader to load my modified copy of java.net.URL instead of the original one?
Any suggestions are welcome! Thanks in advance.
An option would be to use AspectJ and weave your extension code into the URL class instead of extending it. That way you don't have to modify any of the original sources and you can still "extend" a final class. The downside of course is, that you add another dependency to your project.
If you have not yet worked with AOP, you may find a short introduction to AOP at my blog: http://whatiscomingtomyhead.wordpress.com/2010/02/06/aspect-oriented-programming-an-introduction/
You can't extent URL, as you have discovered. You may be able to get the JVM to load your custom version of the class, but IMHO that sounds like a nightmare.
Thankfully you can implement a custom URLStreamHandlerFactory and register it by URL.setURLStreamHandlerFactory(). This will allow you to wrap and monitor when URLs open streams, just as you desire.
EDIT
But you won't be able to use this approach if your app already registers one; URLStreamHandlerFactories are 1/app. And many types of app containers use one (e.g. Tomcat), so if you're using one of those you're SOL.
Have you considered using AspectJ? You could set a pointcut on URL constructors and thus be notified of any new URL instance creation.
If you have modified classes in the standard API, you have to prepend the boot class path with your classes (jars or directories), otherwise the VM internal classes will have priority over any classes added to the normal class path. With Sun's VM, you can use the argument -Xbootclasspath/p: to add new classes with a higher priority than the internal implementations.
Another option, without modifying the URL implementation may be to implement a ProxySelector. Opening a URLConnection would cause the URL implementation to query ProxySelector.select(URI uri) for a suitable proxy for the given address. This will even work if you actually are using proxies. You can obtain the system ProxySelector with ProxySelector.getDefault() before you register your own implementation and delegate the select calls to the original implementation after you've tracked the URI, which is passed to the select method.