Is there a maven plugin that makes mvn verify of an aggregating project fail when its submodules or their transitive dependencies depend on things they oughtn't.
I'd like to be able to restrict uses of public APIs to express policies like
Only classes or packages on a whitelist can invoke this public constructor/method.
This public setter that was produced by a code generator should not be called -- it should really have been package-private.
Motivation & Caveats
I realize that there are ways to work around these requirements using reflection and deserialization. My end goal is to allow system-architects & tech-leads to set a policy like
All uses of security-critical APIs should be in modules reviewed by security. Contact them if you need the whitelist expanded.
These deprecated APIs are banned in favor of new ones. There's a whitelist for grandfathered code which should shrink over time.
The system architect treats trusts application developers but we want naive policy violations flagged with useful error messages, and we want developers who hack around the policy to not be able to plausibly deny that they did so.
Tricks like reflection and deserialization fall into that not-plausibly-deniable hacking.
This is kind of like some of the aims of Jigsaw, where a module (group of packages) can declare that its public interface is limited to just some packages, but jigsaw isn't widely available.
This question differs from "Make Java methods visible to only specific classes" because I'm not asking about ways to do this from within the Java language.
You can use checkstyle to perform such checks, for your use case you could use import control:
It seems that this doesn't support fully-qualified imports, based on following answers:
Checkstyle rule to limit interactions between root packages (with ImportControl?)
How to prevent fully qualified names in Java code
As the second answer suggest you could work around that by forbidding fully qualified imports by using another tool - PMD.
As for JSPs, these are usually compiled in the servlet container, nevertheless there is a way to pre-compile these as well, using maven plugin.
Related
I'm looking for different ways to prevent internals leaking into an API. This is a huge problem because once these internals leak into the API; you can run either into unexpected incompatibility issues or into frozen internals.
One of the simplest ways to do so is just make use of different Maven modules; one module with API and one module with implementation. This way it is impossible to expose the implementation from the API.
Unfortunately not everyone agrees this is the best approach; But are there other alternatives? E.g using checkstyle or other 'architecture checking' tools?
PS: Java 9 for us is not usable, since we are about to upgrade to Java 8 and this will be the lowest supporting version for quite some time to come.
Following your checkstyle idea, it should be possible to set up rules which examine import statements in source files.
Checkstyle has built-in support for that, specifically the IllegalImport and ImportControl rules.
This of course works best if public and internal classes can be easily separated by package names.
The idea for IllegalImport would be that you configure a TreeWalker in checkstyle which only looks at your API-sources, and which excludes imports from internal packages.
With the ImportControl rule on the other hand you can define very detailed access rules for the whole application/module in a separate XML file.
It is standard in Java to define an API using interfaces and implement them using classes. That way you can change the "internals" however you want and nothing changes for the user(s) of the API.
One alternative is to have one module (Jar file) for API and implementation (but then again, is it an API or just any kind of library?). Inside one separates classes and interfaces by using packages, e.g. com.acme.stuff.api and com.acme.stuff.impl. It is important to make classes inside the latter package protected or just package-protected.
Not only does the package name show the consuming developer "hey, this is the implementation", it is also not possible to use anything inside (let's omit reflections at this point for the sake of simplicity).
But again: This is against the idea of an API, because usually the implementation can be changed. With this approach one cannot separate API from implementation, because both are inside the same module.
If it is only about hiding internals of a library, then this is one (not the one) feasible approach.
And just in case you meant a library instead of an API, which only exposes its "frontend" (by using interfaces or abstract classes and such), use different package names, e.g. com.acme.stuff and com.acme.stuff.internal. The same visibility rules apply of course.
Also: This way one does not need Checkstyle and other burdens.
Here is a good start : http://wiki.netbeans.org/API_Design
Key point : Do not expose more than you want Obviously the less of the implementation is expressed in the API, the more flexibility one can have in future. There are some tricks that one can use to hide the implementation, but still deliver the desired functionality
I think you don't need any checkstyle or anything like that, just a good old solid design and architecture should be enough. Polymorphism is all you need here.
One of the simplest ways to do so is just make use of different Maven
modules; one module with API and one module with implementation. This
way it is impossible to expose the implementation from the API.
Yes, I totally agree, hide as much as possible, separate your interface in a standalone project.
How can I manage (=restrict) visibility in large Java projects?
More specifically:
Given the features "Big-A", "Big-B", some sub-features "Little-A-1", "Little-A-2" and some sub-sub-features "Tiny-A-1-I" and "Tiny-A-1-II". With the "Little-A-" features being sub-features of "Big-A" and the "Tiny-A-1-" features being sub-features of "Little-A-1".
There are classes and methods in "Little-A-1" which need to be visible for its enclosing feature "Big-A" and all its (sub-)sub-features, but not for "Big-B". Other classes and methods should only be visible for "Little-A-1" and its sub-features, but not for "Big-A" and not for "Little-A-2". And of course "Tiny-A-1-I" has classes and methods, too, which should only be visible within "Tiny-A-1-I" but not for "Tiny-A-1-II", or only within "Little-A-1" and its sub-features, but not for "Little-A-2" and so on.
In short: I have a large hierarchy of features and need to restrict visibility to parts of that hierarchy. How can this be done in Java, given that the built-in visibilities are not powerful enough for that?
Is there any Java built-in feature that can be used for that? Or any external project? (Or an Eclipse plugin, at least?) I looked at Annotation processors, but it seems they are not powerful enough, too, as they have no access to the actual code. Only to its static structure. Given that Java is used for many big projects, I guess there has to be some solution to that problem. I am probably not the only one having that problem.
The ideal solution would allow me to apply it step wise feature by feature and not require that I have to adapt all the code at once, as that is not possible for me. (Lack of time and resources.) And integration into Eclipse (showing errors when an invisible element is accessed) would be nice, too. :-)
[Clarification] Forgive the lack of clarity in the initial description. Allow me to re-phrase the question.
Does there exist a way to perform runtime compilation using the javax.tools API, usable in OSGi (again stressing runtime), which understands a bundle's dependencies and security constraints?
[update]
Please see https://github.com/rotty3000/phidias
It's a well formed OSGi bundle.
The readme provides all the details of the very tiny 4 class API (8k module).
In order to get from a set of package imports and exports to a list of bundles which can be used for compilation, you'll need some sort of repository of candidate bundles, and a provisioner to work out which bundles best provide which packages. If you're using 'Require-Bundle' (not a best practice), you'll know the bundle names, but not necessarily the versions, so some provisioning is still required.
For example, in Eclipse PDE, the target platform is used as the basic repository for compilation. You can also do more sophisticated things like using Eclipse's p2 provisioning to provision your target platform, so you can use an external p2 repository as your repository instead of setting one up yourself. For command line builds, Tycho allows Maven builds to use the same sort of mechanisms for resolving the classpath as Eclipse itself uses.
An alternative approach is to list your 'classpath' as Maven dependencies, and let the maven bundle plugin (based on bnd) generate your manifest for you.
If you can't take advantage of existing command line tools because you're compiling programatically (it's not entirely clear from your question what problem you're trying to solve), your best best is probably to take advantage of an existing provisioning technology, like OBR, Eclipse p2, or Apache Ace to work out the bundles which should be on the class path for compilation.
This is exactly what we do in bndtools ... If I had a bit of time I would add a compiler to bnd so it could also do this.
Sure you can, you just have to write a custom JavaFileManager which will supply the right classes to compile against to the JavaCompiler.
For example you can write one that gets its classes from an OSGi runtime. If you don't mind having a dependency from your compiler bundle to the libraries you need then it's pretty easy, otherwise you can use the wiring api to look to other bundles as well. (OSGi 4.3+ only). If you intercept which packages it requests while compiling you can generate Package-Import statements so you can generate a bundle.
I made a rough GitHub example a few months back:
https://github.com/flyaruu/test-dynamic-compiler
There were some issues (I couldn't get the Eclipse ecj compiler to work for example, I didn't look into bundle security at all, and due to the dynamic nature of OSGi you have to listen to bundle changes to update your compilation path.), but it works fine.
I've so far found that the real answer is "No there is not!"
The predominant runtime compilation scenario currently for java is JSP compilation. An investigation of the app servers I've had the occasion to review use one of these methods:
invocation of javac (through a system call)
use of ecj/jdt
uses javax.tools in a non-OSGi aware way
All of these approaches are based on collecting the available classpath by directly introspecting jars or classes in the file system.
None of the current approaches are aware of OSGi characteristics like the dynamic nature of the environment or the underlying restrictions imposed of the framework itself.
I always doubt when creating packages, I want to take advantage of the package limited access but at the same time I want to have similar classes divided into packages.
The problem comes when you understand that packages are not hierarchical in Java:
At first, packages appear to be
hierarchical, but they are not.
source
Imagine I have an API defined with its classes at foo.bar, only the classes the API client needs are set public. Then I have another package with some internal objects I need in the API defined at foo.bar.pojos, this classes need to be public so they can be accessed by foo.bar but this means the API client could also access them if the package foo.bar.pojos is imported.
What is the common package politic that should be followed?
I've seen two ways of doing.
The first one consists in separating the public API and internal classes into two different artefacts (jars). The documentation is separated as well, and it's thus easy for the end user to make the distinction between what is internal and what is not. But it sometimes make things more complex to have two jars, two source trees, etc.
The second one consists in delivering a single jar, but have a good documentation allowing to know what's internal and what's not. The textual documentation can explain how to use the API (and thus avoids talking about the internals). And the javadoc can specify that a class is for internal use and is thus subject to changes.
Yes, Java packages don't give you enough control over your dependencies. The classic way to deal with this is to put external APIs in one package and internal implementation classes in another, and rely on people's good sense to avoid creating dependencies on the latter.
With Maven and OSGI, you have an additional mechanism for managing dependencies between modules / bundles of packages. In the case of OSGI, you can explicitly declare some packages as not exported, and an OSGI aware development environment will prevent people creating harmful dependencies. Maven's module support is weaker, but at least it controls dependency cycles.
Finally, you could use custom PMD rules to enforce your project's modularization conventions ... in the same way that there are rules to discourage dependencies on Java's "com.sun.*" package tree.
It is a mess.
Using only what Java itself offers, you have to put everything in the same package. You end up with a single (or a few) packages with lots of classes, and no good way to group them for yourself (but at least that problem does not leak outside). Most people don't do that, though, and as a result, your (as a developer on top of these libraries) public classpath is littered with stuff you should never need to see.
You might like OSGi, which has (and enforces) the concept of bundle-private packages. Those are not exported to the outside world.
Our software currently has many features. I've been asked to make a free trial version which consist of a lightweight version of the full software. Only a few specific features would be available in the trial. And since a jar file isn't encrypted, I was relunctant to release a full version with hardcoded restrictions. I want to be able to have 2 jars : 1 containing the basic features and 1 with the advanced features.
The features are in different menus. It would be nice it the code would try to load the jar with the extra features at start up and display a message (like : this feature is unvailable in the trial) when the non-paying user selects an advanced menu. On the other hand, a paying user with access to the advanced features jar wouldn't know the difference (I'm talking about the difference between now, 1 jar, and the new method, 2 separate jars).
Any tip on how to proceed ?
Any warning on common mistakes to avoid ?
Any better strategy than the separate jars ?
Edit : The best suggestion so far describes how to do my crippleware, but also warns me not to do a crippleware. So what do should I do ?
As you mentioned, you would probably have 2 jars:
1 with the basic features and stubs for the advanced features.
1 with the actual advanced features.
You could use a factory class with a config setting to determine whether to create the stub classes or the real classes - e.g. "FancyFeatureClass" or "FancyFeatureClassStub". "FancyFeatureClassStub" would be included in the "lite" distribution, but "FancyFeatureClass" would only be in the advanced features jar.
If they tried to change the config setting to create the real class without having the jar with the real classes, they would just end up getting class not found errors.
When upgrading, add advanced features jar to the classpath and change the config setting to tell the factory class to create the real classes instead of the stubs. Assuming your app could be split up that way, it should work fine.
As for common mistakes - I think you've already made one - probably not your fault though :)
"Crippleware" is a bad way to evaluate a product. Better to release a full featured version with an expiration or a nag screen than to release a crippled product. Not having those advanced features would probably make it difficult for someone to really evaluate the product.
Many technologies allow pluggable features (called plugins or addons usually).
The idea is that your core code (or framework) declares some interfaces (a well-thought API is ideal). Plugins can provide new implementations of an interface, and register it to the framework.
In its boot sequence, your framework will look (through some convention, in a file for example) if there are plugins, and give them a chance to execute their own boot sequence, consisting of the registration.
After the boot phase, a runtime example (for menus) : The framework looks it the registry where he stores the menus. The registry contains the menus the framework himself declared, plus any additionals provided by plugins... It displays all of them.
If you want specifically the behavior you asked for, I would implement this as follow :
menus available in all cases are declared and implemented in the framework
menus available only in the expanded edition are implemented twice :
in the framework, the implementation would simply display a message
(like : this feature is unvailable in the trial
in the plugin (= paid edition), it would override the previous implementation with the real one, that does the real job.
That way, you paying users have all normal functionality, with the trial version shows the warnings.
Many technologies exist to implement this, the best choice depends on what you already know/use/feel confortable with :
interface implementation is plain java, a string in the manifest of your plugin jar can mention the class to start.
Eclipse RCP has full implementation of menus that way (so you would have nothing to code, only configuration)
Spring is also pretty good when you use interfaces ...
Using a separate Jar which implements the paid for features (and a separate lite front end that displays a suitable message like 'not available' if it gets a ClassNotFoundException) would be the simplest way to do it.
There are many frameworks, up to and including Eclipse RCP for distributing UI applications in a modular form - but this will be overkill for your needs.
In fact, you don't even need two Jars, as long as your build process has the option to compile/package a sub-set of the Java classes in the Jar that you build as part of the distribution process. Just don't include the paid for features.