How to share package private data between two packages in Java?
Asked Answered
P

2

6

I have 2 Java packages, A & B. Let's say that some classes in package B want to use some classes in package A however, when a developer comes along and develops package C (or, say, application C), he/she will use my package B but I do not want him/her to be able to use the classes in A that B is using. That is to say, I want my classes in package A to be package private so that they are hidden from the application developer. However, I do want my own package B to be able to access those classes which are package private. Can this be done in Java? Do I basically just need to bite the bullet and make the classes public and just hope that the user doesn't try to use them? Or, do I need to replicate the classes that are in A inside of B?

My preference would be something that is not hack-y (i.e. I don't want to use reflection). Help?

Pritchard answered 12/6, 2012 at 2:30 Comment(11)
If package B can do something with classes package A, then there is nothing to prevent package C from doing the same thing with classes package A. However, it is possible to prevent package C from getting an instance of a class in package A that is member of a class in package B (unless reflection is involved).Auklet
If you want security don't code on top an interpreter?Impostume
sounds like package A and B really should just be in the same package, or that that some sort of factory that returns a private implementation is necessary.Lip
@Auklet -> can you please explain how it would be possible to, as you say, "prevent package C from getting an instance of a class in package A that is member of a class in package B"?Pritchard
@Lip -> can you please explain how to make a "factory that returns a private implementation"?Pritchard
@fatfreddyscat: Just make sure the member is either protected, no modifier or private, and make sure that you don't return an instance of it through any of the methods (let the classes in the same package access it directly with no modifier or protected).Auklet
@Auklet -> sorry; I misunderstood your initial comment. I missed the "that is a member of a class in package B". Yeah, I know how that works already (wasn't really my question). Thanks anyway!Pritchard
UPDATE I have a super weird "solution" -> since package C should be able to get access to the methods within the public classes of package A as well as package B and since there are some methods (in a package-private class) in package A that B should be able to access, I am going to make one of the methods that are publicly viewable from A be a varargs method. Package B will call that method with a 'secret code' embedded in the varargs; package A will 'decipher' this secret code and call the appropriate method. Application developer won't have a clue what the secret codes are :-)Pritchard
I welcome any and all comments on my super-hackjob "solution".... ??!Pritchard
@fatfreddyscat: That works, but not a good solution SE-wise.Auklet
I completely agree. However, can you (or anyone) suggest a better solution?Pritchard
A
6

You can do it with JDK 8 and its Project Jigsaw. You might want to give a look to Project Jigsaw Quickstart Guide.

Unfortunately, Jigsaw is part of the JDK8, and it is not totally ready yet. It is not expected to be feature complete until January 2013 and will not be released before midyear 2013.

However, you can already compile your classes with the JDK 8 preview and make your ideas work.

In this case, your problem can be solved by dividing your application in independent modules. You could do something like:

module foo {
    exports foo;
    permits bar;
    permits baz;
}

Here the module foo can be required only by modules named bar or baz. A dependence from a module of some other name upon foo will not be resolvable at compile time, install time, or run time. If no permits clauses are present then there are no such constraints.

Not sure if alternative frameworks like OSGI, of which you can find implementations in Apache Felix and Eclipse Equinox offer some kind of functionality to implement these levels of encapsulation. It is probable that you may want to investigate a bit about that.

The problem with OSGi without the existence of Jigsaw is that whatever rules enforced by the framework can be broken by reflection, once Jigsaw is ready for public use, though, these rules will be enforced by Java itself, as you read above, at compile time, runtime and install time.

Able answered 12/6, 2012 at 2:54 Comment(3)
Thank you for a good response (I voted it up). Unfortunately, I am targeting Android and JDK 6... :-/ Any other thoughts on what I might do?Pritchard
@Pritchard OSGI is probably your best shot, but that one will not be easy to implement either.Able
Note that Project Jigsaw did not make it into Java 8, it's now targeted for Java 9.Inhaler
B
2

You can do this with OSGi. Android and JDK 6 as target are not a problem in this case, there are OSGi frameworks running on Android -> e.g. see mBedded Server for Android. You can download a free non-commercial version from the link.

You have several options how to do it in OSGi, depending on what you want to achieve.

Option 1 (recommended): You can put packages A and B in one and the same bundle AB, and export only package B in the manifest of this bundle with Export-Package. Package/application C or any other "user" app can import package B and use it. And it cannot use and doesn't even see package A, because it is internal for the bundle AB. You don't need any special declarations or dependencies on Java level; this will work with ANY Jva version, because the modularity and the separate bundle spaces are part of the OSGi basics and don't depend on the latest Java version or smth.

Option 2: If for some reason you want packages A and B separated in different bundles, you can have them so, you'll export and import the packages in the manifest and then control which bundle has the right to import which package by using Permissions (see OSGi Permission and Conditional Permission services). However, this is more complicated to realize.

Option 3: You can also put package A in a Fragment bundle and allow it to attach to the bundle containing B. In this way B will have access to package A, but at the same time you'll be able to update package A separately during runtime if you want. Since packages in fragments are treated as private for the host bundle (in this case host is the bundle containing package B), Bundle C won't see A. It will only see what is exported by Bundle B.

Since you are not that familiar with OSGi, I will recommend to start witl Option 1 and then later if needed you can upgrade your approach to Option 3 if you want.

@Edwin Dalorzo : It is definitely not true that the rules in OSGi can be broken by reflection. Bundles have separate classloaders in OSGi. You can reflect from Bundle C as much as you won't for the classes A and the only thing you'll get is a ClassNotFound exception - believe me, I have seen it enough times ;)

Bessiebessy answered 14/6, 2012 at 10:34 Comment(2)
Thank you for your detailed answer!! For my case, package C would need to access both package A as well as package B; it's just that I want package B to be able to access 'package private' classes within A... If Java supported the concept of 'friend' (as in C++), then it could work that way. I think that this OSGi approach just might work for me, however, as I am on a severe time constraint I will likely just use my 'hackjob solution' (see my 'UPDATE' comment to my original post) for now and then, when I get time, look into using your method. Again, thank you! Voting up!Pritchard
If you want to export only part of package A in option 1, this is also possible. Use the "include" or "exclude" export directives in the Export-Package entry in the manifest: "include – A comma-separated list of class names that must be visible to an importer. Note that the use of a comma in the value requires it to be enclosed in double quotes. For class filtering, see Class Filtering on page 50."Bessiebessy

© 2022 - 2024 — McMap. All rights reserved.