2

I've got a large Java library and I want to develop several smaller applications that interface this library. The library will be present on the target device's class-path as JAR, but I would like to avoid the need to have the entire library (either JAR or source) present at compile-time if possible. (If it matters, the JAR is quite huge and I want to protect intellectual property too, though it's not my primary concern.)

In C++, I would solve this issue by creating a DLL (.so) and copying just the relevant class and function definition headers to my project and adding them to include path at compile time, let the dynamic linker do the job at runtime.

How to do this in Java? One idea I have would be to remove private methods/members, and strip methods of relevant classes so that their bodies are empty and the real classes and methods with same signatures are loaded at runtime. However, this approach seems quite ugly and rudimental, plus some tool would be needed to automate this process. Is there a tool to do this? Is there a better way?

I don't think it's a duplicate of this question. The point of that question is to minimize size of the resulting JAR file at compile time, by removing unnecessary classes. My point is not to remove unused definitions, but to avoid the need of having the complete library JAR at compile time at all. Though these are simmilar and there may be a way how to achieve what I want using ProGuard, the linked question does not discuss it.

Jan Hadáček
  • 177
  • 4
  • 8
  • 2
    There are no equivalent of header files in Java. This is a very, very, very good thing. Yep, three 'very's. – Michael Dec 06 '16 at 14:06
  • Possible duplicate of [Generate minimized jar with only used classes](http://stackoverflow.com/questions/9518400/generate-minimized-jar-with-only-used-classes) – Michael Dec 06 '16 at 14:08
  • 1
    Well, header files can certainly be a headache. But that was not the point of my question. The point was: how to separate implementation - which should not need to be known during compile-time - from library interface, which has to be known on compile-time, in Java. – Jan Hadáček Dec 06 '16 at 14:10
  • There is no way and more no need to do it all. You have to have your dependency jars in the class path both in compile and run time. nothing more, nothing less. period. – Vadim Dec 06 '16 at 14:13
  • JAR file is not a library in any C/C++ meaning. It is just a directory with .class files packaged into ZIP archive. Class Path can have not a jar but exploded directory from it as well. Ther is no difference for Java. – Vadim Dec 06 '16 at 14:15
  • @Vadim Well, correct me if I'm wrong, but AFAIK the implementations are NOT really needed during compilation, the only thing that is needed are the classes definitions and method signatures. It seemed quite illogical that Java, by default, actually forces me to provide full bytecode of all referenced classes at compile-time, albeit they are not needed. I asked the question in hope there would be a standard way to solve this. – Jan Hadáček Dec 06 '16 at 14:40
  • 1
    You are right and I used that technique you proposed sometime, but completely for other reasons. In that case what this is for? there are not enough space in hard drive to hold JAR during compile time? I guess it is just a C++ way of thinking. Java and JVM was initially created to get out of that. What is a purpose of headers in C++? It is there to abstract my code from actual library implementation. i.e. same library can be different in Linux, Solaris, Windows etc. JVM is there to bypass that problem. Is there any intention to have different implementations of that JAR in compile and runtime? – Vadim Dec 06 '16 at 14:55
  • I have seen this attempted, specifically because someone wanted to force a Java application to conform to C++ practices. Creating an interface for every single class resulted in an ugly mess that was painful to read and nightmarish to maintain. – VGR Dec 06 '16 at 15:01
  • @VGR exactly right. Java and JVM was created after C exactly to avoid it. – Vadim Dec 06 '16 at 15:09
  • " The point of that question is to minimize size of the resulting JAR file" - who told you that your resulting JAR will have classes from your "huge JAR"? - They will not be there. Unless you do not explicitly ask Maven to join them into one JAR or your project is WAR or EAR. – Vadim Dec 06 '16 at 15:13

2 Answers2

4

There's no exact equivalent for header files in Java, but compiling against the "header" (in the meaning of "contract") without having the actual implementation can be achieved using interfaces:

  • Create interface for every class you want the "header" for, with relevant methods
  • Make the actual classes implement the respective interfaces
  • Pack the interfaces into one JAR, and the implementations into another one
    • (if you're using a build tool like Maven, use two projects, and let the implementation project depend on the interface one)
  • Only provide the interface JAR at compile time, and both at run time

There of course will need to be some artifact that knows the actual implementations and can instantiate them, or you'll have to use some lookup that searches classpath for a suitable implementation.

Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43
  • Okay, good suggestion. I was thinking about something like this. It would be nicer if I didn't have to change the classes and introduce Java interfaces, but seems like it can't be helped... – Jan Hadáček Dec 06 '16 at 14:14
  • what can be a reason for such huge work? and I can image what it will take to make couple hundred thousands interfaces in appropriate packages... for what? – Vadim Dec 06 '16 at 14:17
  • @JanHadáček The answers in the possible duplicate question I posted provide solutions that don't require the boilerplate. – Michael Dec 06 '16 at 14:17
2

If your problem is only due to the minimization of the final jar and you use Apache Maven, you could try to use the option "provided" when you declare a dependency in the pom.xml.

For example I use this dependency declaration:

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>1.48</version>
        <scope>provided</scope>
    </dependency>

This means that my java compiler use the bouncycastle library to compile, but finally it doesn't include it in the final jar. You should provide it during execution.

0bot
  • 149
  • 2
  • 6
  • Thank you. This actually solves one part of my problem. If I combine it with @Jiri Tousek's approach with interfaces, it could be the solution. – Jan Hadáček Dec 06 '16 at 14:32
  • forget about it. provided means your huge JAR will be on local machine and will be in class path during compile time. Also as long as it is Maven and if project is not EAR or WAR (i.e. it is JAR) "huge JAR" will not be included anywhere. It must be in class path. Again what do you try to achieve with that? Is there any intention that your "huge JAR" will be different in compile and run time? Even in that case "interface" idea is bad. – Vadim Dec 06 '16 at 15:01
  • Look when "interface" technique has a meaning. There is javax.servlet.api library and (in general) it has only interfaces (or abstract classes). Then different Servlet containers have different implementations (i.e. WebLogic, WebSphere, TomCat, JBoss AS etc.). So I can write code using only javax.servlet.api and it will work on any of those containers. So, do you face similar requirements with your "huge JAR"? – Vadim Dec 06 '16 at 15:07