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

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")
^^^

Related

IntelliJ is converting file name to lower case and build is failing

I cloned a maven project from git and this was the initial class structure under the src folder:
src
|-main
|- java
|-com.controller
|- CommonController.java
Now, when I build this project, I don't understand how but the file name in which class is defined, changed to lower case and the folder structure is changing to below:
src
|-main
|- java
|-com.controller
|-commoncontroller.java
|- CommonController
and then IntelliJ throws this error since the file name has got changed:
java: class CommonController is public, should be declared in a file named CommonController.java
I searched a lot to find what is causing this but could not find any valid reason.
For other folks, this is not the case. So, I'm suspecting it's IntelliJ settings. Also, this issue is not for one codebase, I'm facing this problem with other projects as well. So thought to get it's root cause and fix it permanently.

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

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

Dependencies path difference in Gradle vs in Maven

I have a question regarding to the difference between Gradle depedencies mechanism vs Maven dependency mechanism:
My project structure is following and app is dependent on common:
project
common
conf
src
java
test
app
conf
src
java
test
and build.gradle in app is:
dependencies {
compile project(':common')
....
}
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'conf'
}
}
test {
java {
srcDir 'src/test'
}
}
}
When I use ant dist. The class paths contain /common/conf folder, which contains lots of configuration files.
When I use Gradle build. The class paths contain build/common.jar instead of /common/conf.
Is there a way I could make Gradle do the same thing as Maven does (make class paths contain /common/conf instead of build/common.jar)?
Because app will read xml configuration files under common/conf folder when I run test cases under app but app is not able to read xml from a jar file. Because right now my code is not able to handle the inputStream from Jar.
Is it using:
res.getFile()
where res is the reference to xml configuration files.
I am a newbie to both Maven and Gradle. Could someone please help? Really appreciate!
If what you're trying to achieve is have those xml files available at runtime from within the Jar, then just add your XML files to /src/main/resources.
Anything in that directory automatically gets added to the Jar file and available as classpath resources. Both Maven and Gradle use convention over configuration, where it's a convention to put classpath resources into /src/main/resources as it is to put application Java code in /src/main/java and unit test classpath resources in /src/test/resources and unit test Java code in /src/test/java.
Following Mavan/Gradle conventions will make your configuration simpler too. Unlike Ant where everything needed to be configured.
If your xml files are in common.jar (by putting the xml in common/src/main/resources, and common is on the class path for for app then you should be able to read those files by getting via class loader. E.g SomeClassFromCommon.class.getClassLoader().getResourceAsStream("somefile.xml")

Change how Gradle's build directory is organized

So, I've recently (partially) completed a Java project with Gradle. Importantly, the project uses absolute pathing to access files in my resources folder, since those files will change after the JAR is made. When I use Eclipse's "export as runnable JAR" functionality, I get something that works perfectly - putting the .jar file in my main directory lets it find everything. However, using Gradle's build function doesn't, because Gradle adds extra layers between the .jar and the resources. To demonstrate, here's my "normal" directory:
./program root directory
|_program.jar
|_resources
|_[actual resources]
And here's the directory Gradle makes:
./build folder
|_libs
| |_program.jar
|_resources
|_main
|_[actual resources]
What I want from Gradle is:
./build folder
|_program.jar
|_resources
|_[actual resources]
Yes, I could manually move the resources and program.jar around in the directory to achieve this, but that feels wrong - this is exactly what Gradle is supposed to do for me, right? I know there has to be SOME way to do it. I just don't know how. So that's why I'm asking for help - how do I do this?
To change the output of resources:
sourceSets.main.output.resourcesDir = "$buildDir/resources"
To change where the JAR file is put:
jar {
// use destinationDir for Gradle < 5.1
destinationDirectory = buildDir
}
If all your resources are meant to be external you may want to exclude them from the JAR file:
jar {
include '**/*.class'
destinationDirectory = buildDir
}
That will only include .class files from the jar task's input. You can customize this using the include and exclude options.

How to make a maven project recognize a file from its own directory instead of working directory?

I currently have two projects:
api-test
...
/config/config.json
...
and
ui-test
...
/config/config.json
...
In eclipse, I am adding api-test in the build path of ui-test, so that api-test is the dependency of ui-test.
However the build failed, because api-test is looking for the config.json located in api-test/config/config.json by calling:
System.getProperty("user.dir") + "/config/config.json"
which does not exist in ui-test project.
the two config.json include different contents - what would be the best solution to let each project refer to their own config.json while ui-test is referring to api-test project?
Put the files into the projects' src/main/resources directories as suggested by Maven's Standard Directory Layout. You can use relative paths to access these resources then.
See How to get file resource from Maven src/test/resources/ folder in JUnit test? For instance:
Test file existence
#Test
public void testStreamToString() {
assertNotNull("Test file missing", getClass().getResource("/sample.txt"));
...
}

Categories

Resources