So, I have a Java project containing several packages (like com.myapp.a , com.myapp.b, com.myapp.c) for better readability and I want to build a jar to use as a library in another project.
But I just want to expose only some classes and interfaces from this jar. The problem is that if I don't declare these classes public then they can't be seen inside the jar file between the packages (for example I have a class A in com.myapp.a package that is used in com.myapp.b package).
So how can I expose just what I want outside of the jar when I have multiple packages defined inside?
Currently Java does not address this problem directly.
OSGi adresses this problem by explicitly defining the exported package list.
Also hopefully this will be addressed with the Java 8 Modularity system as well.
So one option is to use OSGi, but this option does not work if the jar file is used directly rather than as an OSGi bundle.
Another option is to use code obfuscation (like Proguard), to obfuscate the packages you do not want to expose.
Eclipse "solved" this problem by making all classes available, but classes that were not intended to be used by clients were placed in packages whose name contains "internal". For example, that might mean that you have packages named "com.myapp.b" and "com.myapp.internal.b". It's made clear to users of the classes that internal classes are not guaranteed to be upwardly compatible or even present in later releases.
Related
I am building an SDK in Java that has a public API and lots of internal 'private' classes. I would like to keep the public classes as public, but restrict the visibility of all the internals. I have to stick to Java 8, so I can't really take advantage of modularity introduced in later Java versions. We all know that in Java (unfortunately) packages are not really hierarchical - for example com.test1.test2 package is not really a sub-package of com.test1 and thus any class declared with package visibility modifier inside com.test1.test2 will not be visible from class declared inside com.test1. I can't really simply put all the classes in a single directory as that would make working with the project a nightmare.
I was wondering if it's possible to keep the file system hierarchy as usual, but declare classes as if they were inside a single package. For example create 2 files like these:
Class1 under path com/test1/test2/Class1.java
package com.test1;
class Class1 {}
Class2 under path com/test1/Class2.java
package com.test1;
class Class2 {}
So that logically, both of these classes would end up under the same package and be accessible from within one another using package visibility modifiers.
I know this is highly unusual and probably not supported by many IDEs, but I gave it a try using plain old javac and as long as I specify each source file by it's full path it compiles and runs just fine. Do you see any technical problems with that, other than (obviously) breaking the 'good practices'. If it makes any difference it is an Android project but written in Java.
If you want to keep two .java source files in separate directories, but having the Java classes belong to the same package, then you need multiple source root directories.
This is e.g. how test code is kept separate from regular code in a standard Maven project.
So, create the files as follows:
src1/com/test1/Class1.java
src2/com/test1/Class2.java
When compiling, have both src1 and src2 on the source path. Since the source path by default is the same as the classpath, that means using e.g. javac -cp src1;src2 -d bin com/test1/Class1.java, which will correctly find and compile Class2 if it is used by Class1. All the compiled .class files are consolidated in the single destination directory (bin in this example).
Having multiple source roots is common (all Maven projects have them, by default), so it is fully supported by IDEs.
I have a jar file that cannot be modified, but I want to use a different .class file in place of one of the members of the jar. How can I tell Java to use the external .class file when the code within the jar attempts to load it?
You could compile another jar file with replacement classes with exactly the same name and put it ahead of the jar file in the class path. For example, this is what the various slf4j bridge jars do to replace calls to log4j or Jakarta Commons Logging in library code with cognate slf4j code; one need not maintain two sets of logging systems and configurations that way.
If you want to override a java... class, you can use some of the command line options to change the boot class path. Look at the -Xbootclasspath options in http://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html. Heed the warnings.
There is also the lib/endorsed directory if you need to upgrade a third-party jar that Sun uses. Oracle uses other organizations' XML and CORBA libraries; if they release a new version and you need to adopt it, you can.
You can use AspectJ to instrument the code and possibly replace it. An around advice can call the original code if it wants.
You could see if you really need to replace the original code. Some systems provide customization hooks.
You need to make sure the external .class file is loaded first. If a class is already loaded by the class loader then it will not be reloaded. If you are using an application server, then there are ways to configure the preferences for loading classes for class loader. But if you are using a standalone application then you may need to extend the class loader to load the files in the order you want to.
I have a runnable java jar file that I need somehow to run (pass params, fetch output) from another java class I'm working on. How do I do that? Do I import it as a package somehow, call it on runtime? Can I invoke "main" method from it or do I just run it with "exec"? Thanks for your answers.
Simply add it to your CLASSPATH and call either the main() method or any other public method which it provides (and which is documented). There is no difference between "normal" and "runnable" jar files, besides an entry in the manifest.
One subtle detail you might need to consider is that runnable jar files are usually self-contained - that is, they contain all required classes, including classes from third party libraries. If you are using the same third party libraries in your project, make sure that there are no conflicts, e.g. by removing the separate third party jar files from your project.
See Lesson: Packaging Programs in JAR Files for more information.
Import it in your code if you need something, that you can not achieve from command line exec solution. Note, you can get problems, if you trying to use some methods from jar file itself, and author changes it later.
Consider Bridge pattern, to put some abstraction layer between jar file and your code, If you not sure, that you use public API.
If jar file is library with stable API, you can be more confident, while using it in your project.
Besides, importing jar file and use methods from it is faster, than parsing process output.
u can import it to your project and then call the publicly exposed methods in that jar from within your application.
There are a couple of questions on SO that sort of hit this, but I am totally new to Java development and I don't know the correct way to approach this.
I have a C# solution, containing two projects (my app, and a unit test project) and within the app, most things are put into folders eg. Interfaces, Exceptions etc.
I am trying to recreate this in Java / Eclipse, but I don't know how. I ended up with lots of packages, which sounds really bad. I also tried adding a source folder but that ended up being outside of the package.
Could anyone point me in the right direction?
Namely, which of those should I use to represent my unit test project/set of unit tests, and subfolders which exist just for organising stuff.
Edit: It also says use of the default package is not advised. What should I be doing?
Edit 2: Here is what it looks like. Does this look vaguely correct? My original C# solution is on the right.
In a typical java eclipse project, you will have one or more source folders (for example one for app code, one for your unit tests).
Each folder contains a package tree, typically starting with your base package, for example com.mycompany.myapp.
In order to avoid name collisions, packages names are usually start with the domain name of the entity who is the author of the code, starting with the top-level-domain and going backwards (more general to more specific). That way, each class fully qualified name is unique. For example if google creates a class named List, it will be known as com.google.List, and it will not enter in conflict with the existing java.util.List interface.
You can have a unlimited number of packages inside this base package, for example :
com.mycompany.myapp.persistence
com.mycompany.myapp.domain
com.mycompany.myapp.services
com.mycompany.myapp.web
It all depends on your project and the way you want to organize your code and your classes.
At the logical level, packages are named with dots as separator. They contain java classes.
At the physical on disk level, each package is a directory. The java classes are contained in .java files (most frequently one class per file).
In Eclipse a "source folder" is a folder inside your project that is known to Eclipse to contain java source files. It will be compiled included in the output (for example JAR file) when you build your project.
In Eclipse, you usually view them at the logical level, showing packages. When you tell Eclipse to "create a new package", it will create the directory for you. For example, if you tell it to create the com.mycompany.myproject package, it will automatically create a com folder containing a mycompany folder containing a myproject folder.
In java source tree structure must match package structure
so foo.bar package must be laid out in
src/foo/bar
Also default package may not be advised - but you can still use it - better to put things in a package though
In java different project development structure are flowed according to type of project.
So as you are new to java and Eclipse so it's better to install maven plugin and create maven project and choose a archetypes according to your project type like a standalone or web based.
The maven plugin will create the project structure including packages,test packages source folder etc. You can get more about project structure from this
Using the default package may create namespace collisions. Imagine you're creating a library which contains a MyClass class. Someone uses your library in his project and also has a MyClass class in his default package. What should the compiler do? Package in Java is actually a namespace which fully identifies your project. So it's important to not use the default package in the real world projects.
Consider a scenario that a java program imports the classes from jar files. If the same class resides in two or more jar files there could be a problem.
In such scenarios what is the class that imported by the program? Is it the class
with the older timestamp??
What are the practices we can follow to avoid such complications.
Edit : This is an example. I have 2 jar files my1.jar and my2.jar. Both the files contain com.mycompany.CrazyWriter
By default, classes are loaded by the ClassLoader using the classpath which is searched in order.
If you have two implementations of the same class, the one the class loader finds first will be loaded.
If the classes are not actually the same class (same names but different methods), you'll get an exception when you try to use it.
You can load two classes with the same names in a single VM by using multiple class loaders. The OSGI framework can manage lots of the complexitites for you, making sure the correct version is loaded, etc.
First, I assume that you mean that the same class resides in two more jar files...
Now, answering your questions:
Which class is imported is dependent on your classloader and JVM. You cannot guarantee which class it will be, but in the normal classloader it will be the class from the first jar file on your classpath.
Don't put the same class into multiple jar files, or if you are trying to override system classes, use -bootclasspath.
Edit: To address one of the comments on this answer. I originally thought that sealing the jar would make a difference, since in theory it should not load two classes from the same package from different jar files. However, after some experimentation, I see that this assumption does not hold true, at least with the default security provider.
The ClassLoader is responsible for loading the Classes.
It scanns the ClassPath and loads the class that it found first.
If you have the same Jar twice on the ClassPath or if you have two Jars that contain two different versions of the same Class (that is com.packagename.Classname), the one that is found first is loaded.
Try to avoid having the same jar on the classpath twice.
Not sure what you meant by "the same class resides in two more classes"
if you meant inner/nested classes, there should be no problem since they are in different namespaces.
If you meant in two more JARs, as already answered, the order in the classpath is used.
How to avoid?
A package should be in only one JAR to avoid duplicated classes. If two classes have the same simple name, like java.util.Date and java.sql.Date, but are in different packages, they actually are different classes. You must use the fully qualified name, at aleast from one of the classes, to distinguish them.
If you have a problem finding out which version of a class is being used, then jwhich might be of use:
http://www.fullspan.com/proj/jwhich/index.html
If the same class resides in two more jars there should be a problem.
What do you mean exactly? Why should this be a problem?
In such scenarios what is the class that imported by the program? (Class with older timestamp??)
If a class exists in two JARs, the class will be loaded from the first JAR on the class path where it is found. Quoting Setting the class path (the quoted part applies to archive files too):
The order in which you specify multiple class path entries is important. The Java interpreter will look for classes in the directories in the order they appear in the class path variable. In the example above, the Java interpreter will first look for a needed class in the directory C:\java\MyClasses. Only if it doesn't find a class with the proper name in that directory will the interpreter look in the C:\java\OtherClasses directory.
In other words, if a specific order is required then just enumerate the JAR files explicitly in the class path. This is something commonly used by application server vendors: to patch specific class(es) of a product, you put a JAR (e.g. CR1234.jar) containing patched class(es) on the class path before the main JAR (say weblogic.jar).
What are the practices we can follow to avoid such complications.
Well, the obvious answer is don't do it (or only on purpose like in the sample given above).