Multiple entry points (main classes) inside jar's top level - java

It worth to mention that I am using maven as my build management tool. I have a jar (let's call it dep.jar) which will be included into the final project (final.jar) as dependency.
dep.jar has a class with main method.
I need to have several entry points (classes with main methods) within my final.jar's top level directory so I can use entry point depending on my need. Including one from dep.jar.
I considered:
Changing META-INF/MANIFEST.MF file within jar. As Oracle stated that is not possible to reference main classes inside jar's dependencies (BOOT-INF/lib directory) -> https://docs.oracle.com/javase/tutorial/deployment/jar/downman.html;
Uber jar - not an option, I am dependent on jar directory structure inside Java code base
Using special class loader like this one http://www.jdotsoft.com/JarClassLoader.php. But it implies changing final.jar's main method which I cannot do due to project restrictions.
Using maven-dependency-plugin but it can unpack inner jar (dep.jar) and copy classes to maven working directory target which during packaging phase will be packed to BOOT-INF/classes directory. Again, I cannot reference main classes from there. If I unpack and copy them somewhere different than target - copied classes will not appear in my final.jar
Is there any other plugin or option how to add classes from final.jar dependant jar dep.jar during JAR build to final.jar's top level?
EDIT:
final.jar project looks like this:
final.jar
|_______BOOT-INF
|_______lib
| |_______dep.jar (contains main class I want to invoke)
|_______classes
|__________dir (directory I want to copy on demand with help of CLI)

I found a solution here Spring Boot - How to specify an alternate start-class? (Multiple Entry Points). Ended up using -Dloader.main property when launching jar.
Command line looks like these: java -jar -Dloader.main=<main_class> ./final.jar

Related

IntelliJ & Gradle : test module/sourceSet can't see main resources

Here is the simplified structure of each module of my project :
src
|- main
|- java
<My classes>
|- resources
config.properties
|- test
|- java
MainTest
|- resources
I recently updated IntelliJ to version 2019.2. As Jetbrains removed the "uncheck create separate modules per source set" (see this post), when I import a Gradle project, I end up with one module having two sub-modules : main and test.
So, in Intellij project structure, I have this :
main sub-module
test sub-module
In some class, I load the config.properties file this way :
InputStream inputStream = MyClass.class.getClassLoader().getResourceAsStream("config.properties");
But when I run MainTest.main() in IntelliJ, which at some point calls the bit of code above, the resulting InputStream is null.
I guess that the test module classpath doesn't include the main resources folder...
How can I fix that ?
When I could check the box "uncheck create separate modules per source set", I had just one module, with one classpath, and my life was easier...
Is there a way, maybe using Gradle configuration in build.gradle file, to enforce one unique module/source set ?
IntelliJ 2019.2 defaults to delegating all build and run tasks to Gradle, so it shouldn't matter how you have structured the project in IntelliJ. You could also try running your test on the command line with gradle test to see if you get the same problem (which you should).
The 'test' runtime classpath does includes the resource folder from both the 'test' and the 'main' folder. So this also shouldn't be a problem.
In the screenshot you've shown of the main modules, there are no config.properties file in the resource folder. Did you put it in the META-INF sub-folder instead? If it actually is in the root of the resource folder, try referencing it with a slash (/) in the path, e.g.:
MyClass.class.getClassLoader().getResourceAsStream("/config.properties")
^^^

Getting resource file from inside jar

I need to get a resource from inside the root of the application when its packed into jar. My project is like this:
ProjectRoot
resource.txt //want to access from here
src
main
java
package1
package2
package3
Main.java
target
app.jar
classes
resource.txt //works when here
package1
package2
package3
Main.class
I use this code:
Path path = Paths.get("resource.txt");
When run before packaging into a jar, it finds the file just fine (inside ProjectRoot). When running the jar, it can't find it, and transforms this path to target/resource.txt.
This code:
BufferedReader br = new BufferedReader(new InputStreamReader(new Main().getClass().getClassLoader().getResourceAsStream(
"resource.txt")));
when run before packaging looks for the resource inside target/classes. After packaging it claims to taking the resource from .../target/app.jar!/resource.txt.
This code:
BufferedReader br = new BufferedReader(new InputStreamReader(new Main().getClass().getClassLoader().getResourceAsStream(
"/resource.txt")));
I can't understand where's looking for the resource, but it doesn't seem to be ProjectRoot.
All I want to do is to place the resource inside ProjectRoot and be able to access it from both outside jar (when running the class files from IDE) and inside (after having packaged the files into a jar file using Maven).
EDIT: I NEED THE CODE TO WORK BOTH FOR PRE- AND POST- packaging. MEANING: If I run a Main.java FROM INSIDE IDE IT WOULD GET THE RESOURCE; IF I PACKAGE EVERYTHING INTO JAR AND RUN JAR IT WOULD GET THE RESOURCE - ALL WITH THE SAME CODE.
Use: Main.class.getResource("/resource.txt").
Note that your attempt using any call to getClassLoader is strictly worse (it's more text, and will fail more often, because that class loader can in exotic cases be null (specifically, when you're part of the bootstrap loader), whereas calling getResource directly on the class always works.
The reason your snippet does not work is because when invoking getResource on the classloader, you must NOT start the resource with a slash. When invoking on a class directly, you can (if you don't, it'll be relative to the package of the class you're calling it on, if you do, it'll be relative to the root).
TL;DR: Of the forms SomeClass.class.getClassLoader().getResource, getClass().getResource and MyClass.class.getResource, only the last one is correct, the rest are strictly inferior and therefore should not be used at all.
Maven uses something called the Standard Directory Layout. If you don't follow this layout then the plugins can't do their job correctly. Technically, you can configure Maven to use different directories but 99.999% of the time this is not necessary.
One of the features of this layout is that production files go in:
<project-dir>/src/main/java
All *.java files
<project-dir>/src/main/resources
All non-*.java files (that are meant to be resources)
When you build your project the Java source files are compiled and the *.class files are put into the target/classes directory; this is done by the maven-compiler-plugin. Meanwhile, the resource files are copied from src/main/resources into target/classes as well; the maven-resources-plugin is responsible for this.
Note: See Introduction to the Build Lifecycle for more information about phases and which plugins are executed by which phase. This Stack Overflow question may also be useful.
When you launch your application from the IDE (possibly via the exec-maven-plugin) the target/classes directory is put on the classpath. This means all the compiled classes from src/main/java and all the copied resources from src/main/resources are available to use via the classpath.
Then, when you package your application in a JAR file, all the files in target/classes are added to the resulting JAR file (handled by the maven-jar-plugin). This includes the resources copied from src/main/resources. When you launch the application using this JAR file the resources are still available to use via the classpath, because they're embedded in the JAR file.
To make resource.txt available on the classpath, just move:
<project-dir>/resource.txt
To:
<project-dir>/src/main/resources/resource.txt.
Then you can use Class#getResource with /resource.txt as the path and everything should work out for you. The URL returned by getResource will be different depending on if you're executing against target/classes or against the JAR file.
When executing against target/classes you'll get something like:
file:///.../<project-dir>/target/classes/resource.txt
When executing against the JAR file you'll get something like:
jar:file:///.../<project-dir>/target/projectname-version.jar!/resource.txt
Note: This all assumes resource.txt is actually supposed to be a resource and not an external file. Resources are typically read-only once deployed in a JAR file; if you need a writable file then it's up to you to use a designated location for the file (e.g. a folder in the user's home directory). One typically accesses external files via either java.io.File or java.nio.file.*. Remember, resources are not the same thing as normal files.
Now, if you were to put resource.txt directly under <project-dir> that would mean nothing to Maven. It would not be copied to target/classes or end up in the JAR file which means the resource is never available on the classpath. So just to reiterate, all resources go under src/main/resources.
Check out the Javadoc of java.lang.Class#getResource(String) for more information about the path, such as when to use a leading / and when not to. The link points to the Javadoc for Java 12 which includes information about resources and modules (JPMS/Jigsaw modules, not Maven modules); if you aren't using modules you can ignore that part of the documentation.

How to get resources folder(files) from other maven module?

I have some amount of child maven modules in parent module.
How can I get access from one module to resources of another and to have this solution stable in jar file ?
I tried something like: (resource.txt - is situated in first module, and code is executing in second)
String pathToFirstModuleResource = ClassFromFirstModule.class.getClassLoader().getResource(resource.txt).getFile();
But it points me to ...firstmodule/target/classes/resource.txt, so it works almost good, but returns not a resource folder, but target/classes. Why it returns this targets folder, though should to return ...firstmodule/resources/resource.txt?
Why it returns this targets folder, though should to return ...firstmodule/resources/resource.txt?
No.
It looks like you configured resources being threaded by your IDE as a souce folder.
Therefore the folder resources only exist in your development environment. At runtime the file is simply copied to the root of your jar, which merely is the content of target/classes.

Factual API and coldfusion

I took the java implementation of the Factual API (reference http://developer.factual.com/) and made a JAR file for factual. I did this by opening a new project in eclipse with the factual java files and then exporting to a new jar file.
I put that jar file in my coldfusion installation's /WEB-INF/lib/ folder.
After restarting Coldfusion, I tried to create a new cfobject like so
<cfscript>
// Initialize the Java class.
factualClass=CreateObject("java", "src.main.java.com.factual.driver.Factual");
</cfscript>
I get an error indicating that it cannot find the Factual class.
Can anybody give me some guidance?
(Summary from comments)
It sounds like you may be exporting the source files ie *.java rather than the compiled class files, ie *.class. In the Jar Export wizard, be sure to select the "Export generated class files and resources" option. (To automatically compile the project sources before expi, enable the setting: JAR packaging > Build projects if not build automatically option). If you prefer you can also find pre-compiled jars in the MVN repository.
put that jar file in my coldfusion installation's /WEB-INF/lib/
folder.
CF10+ also supports dynamic class loading via a new application level setting THIS.javaSettings.
// Initialize the Java class.
factualClass=CreateObject("java", "src.main.java.com.factual.driver.Factual");
Just as a point of interest, src/main/java/ is not actually part of the libary class name. It is a standard directory structure used in Maven projects. It is probably included when exporting the sources, but not the compiled classes.
You can always verify the correct path and class name either by examining the API ie javadocs or by viewing one the source files. Package declarations are always at the top of the source file, such as on line 1 of src/main/java/com/factual/driver/Factual.java:
package com.factual.driver; // ie "com.factual.driver"
.. and the class declaration on line 39.
public class Factual { // ie "Factual"
Combined that gives you the exact (case-sensitive) path to use with createObject:
factualClass=CreateObject("java", "com.factual.driver.Factual");

Java 6 : building a jar whose main class package is not at root of the jar

I would like to build a Jar whose main class package is not at the root of the jar.
In the application, I've just written a Main class
com.gmail.bernabe.laurent.test.HelloWorld
I could simply make a Runnable jar from the Eclipse IDE, but I would like to go a step further
=> Having the final jar organized like this
Root
=> META-INF
=> Lib
=====> com
==========> gmail
...
Instead of :
Root
=> META-INF
==> com
======> gmail
...
Is it possible to configure the Manifest file so that the Jar runs properly ?
Thanks in advance
Why do I want to do this ?
I want to build a JOGL-based jar organized as follow
=> JOGL (subdirectories are JOGL just needed files)
=> CORE (subdirectorie is the core application package)
=> LAUNCHER (subdirectory is the custom classloader package)
=> META-INF
Yes and no.
No, because you should not do that: imagine someone supporting your code. Or even yourself, two years later, returning to the project: you saw hundreds of jars organised as defined by JarClassLoader, you worked in tens of "usual" projects... Now, well, now you see a JAR file. Will you expect it to have a tricky custom loading scheme? I doubt so.
Yes, because you can add one more main class, use it to set up a classloader, and after that load classes from any layout. You will need to keep manifest and the startup class at the "proper" places, though.
The tutorial on class loading can be found at http://java.sun.com/developer/onlineTraining/Security/Fundamentals/magercises/ClassLoader/help.html

Categories

Resources