Sorry if I've missed a response that would cover this, but I've tried to be diligent about finding anything similar.
I'm really confused by Proguard's behaviour, and I'm wondering if I'm reading the docs wrong or if the behaviour is wrong.
I want to retain annotated fields and members in a class, if that class is kept. So I used keepclassmembers like so:
-keepclassmembers class com.mycompany.** {
#com.mycompany.**
public com.mycompany.** *;
#com.mycompany.**
public void *(com.mycompany.**);
}
(This configuration was built by the GUI, but I think I see what it's doing.) The Proguard docs say
-keepclassmembers [,modifier,...] class_specification
Specifies class members to be preserved, if their classes are preserved as well. For example, you may want to keep all serialization fields and methods of classes that implement the Serializable interface.
Sounds good. But I'm getting classes I don't expect in the output, just because they have #annotated listeners. -whyareyoukeeping class com.company.MyServiceImpl?
com.mycompany.MyServiceImpl
is invoked by com.mycompany.MyServiceImpl: void handleEvent(com.mycompany.MyEvent) (34:35)
is kept by a directive in the configuration.
handleEvent just calls another method in MyServiceImpl, so it doesn't seem to be a valid reason to keep the whole class, but that's the only think I can think of.
Why does keepclassmembers appear to force keep on any class containing a specified member, not only "if their classes are preserved as well"? But more directly, how can I keep public annotated event listeners in classes that Proguard is already keeping via other rules?
Your initial configuration and interpretation of it look correct. It also works if I try it on a simple example: annotated fields and methods are kept if their classes are already kept. Of course, those annotated methods may contain code that drags in more classes.
The output of -whyareyoukeeping only tells half the story and isn't very helpful in this case. You could check if -printseeds provides some hints.
The workable configuration with 'allow' modifiers doesn't seem to make much sense; I wouldn't trust it too much.
If you still think there is a bug, you can report it in the ProGuard bug tracker, preferably with an example that allows me to reproduce the problem.
Related
What I have known are:
annotation was added in java 5
annotation can be using in method, class, and property
annotation can work in RUNTIME, CLASS, SOURCE( I don't know how to work with CLASS and SOURCE, and their's features)
annotation with retention which is RUNTIME can be implement when java program is running.
And I want to implement a annotation to have follows features:
ensure class only being allowed to create a instance
ensure methods only being allowed to access method in the class
it is like as friend in c++
it is same as public and private , but more dynamicall, like
#MyAnnotation(allowMethods={xxx.doSomething})
public void getValue(){}
the getValues method only can be accessed in the instance self and xxx.doSomething() method
What should I do and learn in next?
And Where can I learn about these?
I think you might be misunderstanding something there. Annotations are descriptive elements, not parts of your program. You can write as many annotations as you want, and people who use your code will still be able to ignore them.
That said, an annotation that enforces a policy (as yours does) can actually be implemented, either at compile or at runtime, but you need an external mechanism to help you. I can think of 3:
Annotation processing lets you interact with the compiler and process annotations by generating code or by omitting compiler errors. Unfortunately, I don't think it will work for your case, as you want to protect your annotated type from instantiation, and that means the call site doesn't actually have an annotation. Annotation processing only gives you access to the actual code pieces that have annotations, not to those that refer to them.
AspectJ allows you to write policy enforcement aspects and omit compiler errors, based on static pointcuts. The problem here is that static pointcuts have very limited semantics, so while you could forbid the instantiation of your class altogether, or from certain packages, you could not limit the your class instantiations to 1.
The third way, and probably the only sane way is that you use a container like Spring or Guice and configure your class as singleton. As long as you only retrieve your class from the container, it will never create a second instance.
Finally: If you want to limit the number of instantiations of your class, you can always use a classic Singleton pattern approach.
I'm trying to create an Android Library that is kept simple in that whoever implements it only sees one class of that library. I've gotten ProGuard to hide most everything I need hidden, but some package names still appear even after the class names are obfuscated. Example:
com.app.parsers
com.app.camera
com.app
These are my packages, and when I export my .aar into another project, I can still see the package names. However, once I get there, there is no other need for it. Most of the classes are protected, with a a Public class and a Public interface that the public class extends.
For reference, in my project view, the following is visible:
com.app.parsers.jsonParser
com.app.parsers.xmlParser
com.app.parsers.csvParser
com.app.parsers.ManagerInterface(public)
com.app.parsers.ParserManager(public)
Essentially the reason I have parser manager public (and it's interface) is because it acts as an entry point (or proxy) to my various different parsers.So I keep it organized this way. So when I use proguard I don't see anything here I just see an empty package that says com.app.parsers and then a list of just generic cast, instanof or what have you. I want to hide this so that it's not distracting for anyone to use or they wonder why it exist or whats in there.
I don't think it's a huge deal having them exposed, as long as your code is well documented.
But you could try either using either
#hide annotation
or the Facade pattern
I have an issue where one of my subscribed methods does not get called upon a post of the correct event type unless that subscribed method is used (called) elsewhere.
Here is some relevant information about the code:
A method of one of my classes is annotated with #Subscribe.
By stepping through the code with the debugger, I find that under my specific circumstance, the class has no methods annotated with #Subscribe.
Unless I call the method directly at some point in time (doesn't matter when, or even if it actually gets called at runtime) elsewhere, my post does not work.
The IDE (Android Studio) notifies me that the "method is never used"
I can certainly call the method in a block of code that I am confident will never fire, but this is obviously terrible practice, and defeats the purpose of this post/subscribe paradigm.
Or I can make the method static, but I'd rather not because I use member variables inside of it.
Any solutions to why this is occuring even though Otto's example uses a similar pattern
Turns out it was a ProGuard issue. Fixed it by adding the following lines:
-keepclassmembers class ** {
#com.squareup.otto.Subscribe public *;
#com.squareup.otto.Produce public *;
}
I've been poking through this for about a week or so, now, and haven't found anything. I'm building an application with GWT, Hibernate, and Gilead, and I'm attempting to make an rpc call that loads a list of LightEntity objects from the database. This call worked perfectly, right up until I made a minimal change to my rpc interface - I added a deleteLightEntity method. Then I started receiving this error:
Type 'com.blah.shared.DomainObject' was not included in the set of types which can be
serialized by this SerializationPolicy or its Class object could not be loaded. For
security purposes, this type will not be serialized."
... which is normally characteristic of objects that don't have a no-args constructor, or perhaps don't implement Serializable or IsSerializable. Except my DomainObjects all do. And they all worked properly before I added this method to the rpc. I've even tried removing the method I added and recompiling, and it doesn't seem to work. I have also manually deleted the generated .gwt.rpc files, and cleared my browser cache. If anyone has any idea what could be causing these troubles, I would be very glad to hear it :)
If your class implements Serializable (and not IsSerializable), it will only be included in the serialization policy if it is referenced in the RPC interface, so check that.
If you have a reason not to reference that class you could use this workaround.
Also, since the error mentions the class DomainObject, which I assume is your global superclass, I would try to make it implement Serializable or IsSerializable too (in addition to its subclasses).
It would also help if you show us some source code.
Is there a feasible way to get my own code run whenever any class is loaded in Java, without forcing the user explicitly and manually loading all classes with a custom classloader?
Without going too much into the details, whenever a class implementing a certain interface read its annotation that links it with another class, and give the pair to a third class.
Edit: Heck, I'll go to details: I'm doing an event handling library. What I'm doing is having the client code do their own Listener / Event pairs, which need to be registered with my library as a pair. (hm, that wasn't that long after all).
Further Edit: Currently the client code needs to register the pair of classes/interfaces manually, which works pretty well. My intent is to automate this away, and I thought that linking the two classes with annotations would help. Next, I want to get rid of the client code needing to keeping the list of registrations up to date always.
PS: The static block won't do, since my interface is bundled into a library, and the client code will create further interfaces. Thus, abstract classes won't do either, since it must be an interface.
If you want to base the behavior on an interface, you could use a static initializer in that interface.
public interface Foo{
static{
// do initializing here
}
}
I'm not saying it's good practice, but it will definitely initialize the first time one of the implementing classes is loaded.
Update: static blocks in interfaces are illegal. Use abstract classes instead!
Reference:
Initializers (Sun Java Tutorial)
But if I understand you right, you want the initialization to happen once per implementing class. That will be tricky. You definitely can't do that with an interface based solution. You could do it with an abstract base class that has a dynamic initializer (or constructor), that checks whether the requested mapping already exists and adds it if it doesn't, but doing such things in constructors is quite a hack.
I'd say you cleanest options are either to generate Code at build time (through annotation processing with apt or through bytecode analysis with a tool like asm) or to use an agent at class load time to dynamically create the mapping.
Ah, more input. Very good. So clients use your library and provide mappings based on annotations. Then I'd say your library should provide an initializer method, where client code can register classes. Something like this:
YourLibrary.getInstance().registerMappedClasses(
CustomClass1.class,
CustomClass2.class,
CustomClass3.class,
CustomClass4.class
)
Or, even better, a package scanning mechanism (example code to implement this can be found at this question):
YourLibrary.getInstance().registerMappedClassesFromPackages(
"com.mycompany.myclientcode.abc",
"com.mycompany.myclientcode.def"
)
Anyway, there is basically no way to avoid having your clients do that kind of work, because you can't control their build process nor their classloader for them (but you could of course provide guides for classloader or build configuration).
If you want some piece of code to be run on any class loading, you should:
overwrite the ClassLoader, adding your own custom code at the loadClass methods (don't forget forwarding to the parent ClassLoader after or before your custom code).
Define this custom ClassLoader as the default for your system (here you got how to do it: How to set my custom class loader to be the default?).
Run and check it.
Depending on what kind of environment you are, there are chances that not all the classes be loaded trouugh your custom ClassLoader (some utility packages use their own CL, some Java EE containers handle some spacific areas with specific classLoaders, etc.), but it's a kind of aproximation to what you are asking.