I'm using Gradle 7.0 and I made my project using the task Gradle Init. Then I imported it in Eclipse (2021-03 with buildship 3.1.5). Everything is fine but when I try to read or when I create a file in a java method with "/myfile.yaml" as path, it creates it (or try to read it) in the D:\ root folder (the partition where my eclipse is installed).
If I don't use the slash ("myfile.yaml" instead of "/myfile.yaml") the file is created in the root folder of the project. I thought it was supposed to be in src/main/resources until it's not built.
My goal is not really to create a file, it was just easier to test. My goal is to read some Yaml configuration files. What should I do to make sure the file will be in the build package and read in the correct place in both context (eclipse debbugging and the build package directory) ? And also, what is the best practice to set the path of the file (sonarlint alerts me about the way I do it : hardcoded in the method below, I know it's not supposed to be like this... I would use a constant but sonarlint doesn't like either).
The tree of the app :
- Project
- app
- src/main/java (containing java classes)
- src/main/resources (supposing to contain resources, yaml in my case)
- My build.gradle file
- yaml file when I try "myfile.yaml" without any /
- gradle/wrapper/gradle-wrapper.jar & gradle-wrapper.properies
- gradlew & gradlew.bat
- settings.gradle
My settings.gradle :
rootProject.name = 'myapp'
include('app')
My build.gradle:
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
apply plugin:'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
implementation 'com.google.guava:guava:30.0-jre'
// Yaml reader
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.12.3'
testImplementation 'junit:junit:4.13.1'
}
//I tried with and without this sourceSets and the result was the same
sourceSets {
main {
java {
srcDirs= ["src/main/java"]
}
resources {
srcDirs= ["src/main/resources"]
}
}
}
application {
// Define the main class for the application.
mainClass = 'myapp.launchers.App'
}
And here is the method which writes a file using the Jackson library (yaml) :
public static void writeConfiguration() throws JsonGenerationException, JsonMappingException, IOException {
WorkerConfig wc = new WorkerConfig();
WorkerPathConfig wpc = new WorkerPathConfig();
wpc.setTmpdir("\\some\\uri\\file");
wpc.setOutputdir("\\some\\other\\uri\\file");
wc.setPathConfig(wpc);
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
objectMapper.writeValue(new File("/application.yaml"), wc);
}
Java Reading Resource Files
You are managing files in your Java program.
If you are using java.io.File class to get a reference to a specific file it will take those references as actual paths:
Absolute path: "/myfile.yaml" -> will point to the root of the hard drive, in your case D:\myfile.yaml
Relative path: "myfile.yaml" -> will point to the root of your java project
Now your goal should be to get the project specific path to load configuration files. This can be achieved with ClassLoader instance. You can read the content of the file like in the example below. classLoader.getResourceAsStream() will search for a file inside your resources.
String fileName = "myfile.yaml";
ClassLoader classLoader = getClass().getClassLoader();
try (InputStream inputStream = classLoader.getResourceAsStream(fileName);
InputStreamReader streamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
2 Gradle build script
If you have your classes in "src/main/java" and resource files in "src/main/resources" then this is compliant with the convention of gradle java plugin and you don't need to specify explicitly sourceSets block in your Gradle build script.
Sonar warning
You can extract file name as a constant and pass it as a parameter when loading the configuration file. I suppose this file "myfile.yaml" is convention of your application. In a best case it's documented somewhere.
At least this will greatly ease up development, instead if you would want to have it provided dynamically you would need to read file name from environmental variable or an argument provided in main method.
You can suppress SonarQube warning if it doesn't apply in your case and explain why you ignored it.
Related
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")
I am creating a custom maven plugin. In one of its Mojos, I am reading a Xpp3Dom object from XML file using following code piece:
File pluginsFile = new File(
"absolute-path-to-file/plugins.xml");
Xpp3Dom Xpp3DomObject = new Xpp3Dom("plugins");
try {
FileReader reader = new FileReader(pluginsFile);
Xpp3DomObject = Xpp3DomBuilder.build(reader);
reader.close();
} catch (Exception e) {
// TODO throw exception
}
The XML file from which I am reading (plugins.xml) is stored in src/main/resources of the maven plugin itself. My question is, how do I point to that XML file without explicitly stating the absolute path to that file?
To be clear: I want this file to be under the directory of my maven plugin. It cannot be outside the maven plugin as it is a necessary part of the maven plugin and should not be changed by other maven projects that consume this plugin.
I have searched for a variable/method in Maven Mojo that would give me the absolute location of the maven plugin itself. If I get that, then I can just give the location as value-of-that-variable/src/main/resources/plugins.xml. But I am unable to find such variable. I have also tried for a way to pass properties from Maven plugin POM to one of its Mojos so that I can pass project.build.directory, but cannot find a way.
To be clear: I want to access a file that is under the maven plugin directory, in one of its Mojos.
Any suggestions will help.
I think the easiest form to read some of the own plugin's resources files is through the getResourceAsStream() API, because sooner or later, your plugin will be delivered as a JAR, and then the src directory will dissapear and there will remain only classpath resources:
try (InputStream input=getClass().getClassLoader().getResourceAsStream("plugins.xml")){
try(Reader reader=new InputStreamReader(input));
{
Xpp3Dom Xpp3DomObject = Xpp3DomBuilder.build(input);
} catch (Exception e) {
...
}
}
Anyway, in this way there is a risk that some other JAR of the classpath should contain a plugins.xml file by chance. To avoid (or at least reduce) this risk, you should package it:
src\
main\
resources\
foo\
bar\
MyMojo.java
plugins.xml
... and in this case, you must read it through getClass().getResourceAsInputStream().
This is straight from Gradle 3.2.1 specifying_multiple_files. Why this does not work ? Why it cannot pick up files from ../shared/java during compilation. Compilation fails for sources in src/main/java depend on ../shared/src
The example is straight from Gradle
task compile(type: JavaCompile)
compile {
// Add some source directories use String paths
source 'src/main/java', 'src/main/groovy'
// Add a source directory using a File object
source file('../shared/java')
// Add some source directories using a closure
source { file('src/test/').listFiles() }
}
Unfortunately that page is written to show how you can work with files, but not necessarily how to configure the Java Compiler.
https://docs.gradle.org/current/userguide/java_plugin.html
That page will give you the details for what you are trying to solve. What you need is to instead just define the sourceSet.
sourceSets {
main {
java {
srcDirs = ['src/main/java', '../shared/java']
}
}
}
I've successfully configured my gradle build script to create a zip distribution of my application with an extra 'config' folder at the root. This folder contains (at least right now) only one properties file in use by the application, and is on the classpath for the application.
What I'm looking for now, however, is a way to do the same with the 'run' task in the application plugin. When I try to run my application this way, (for testing), my program fails to run because of a class trying to access this properties file on the root of the classpath.
A bonus would be if I could get IntelliJ or Eclipse to also add this folder to its classpath just like the other folders (src/main/java, src/main/resources, ...) so I can run and debug my code from within the IDE without invoking a gradle task. I want to try to avoid as much as possible tying this code to any one IDE, so that when anybody needs to work on the project, they just need to import the build.gradle file and have the IDE make the appropriate config files it needs.
Here is my build.gradle file:
apply plugin: 'application'
mainClassName = "MainClass"
startScripts {
// Add config folder to classpath. Using workaround at
// https://discuss.gradle.org/t/classpath-in-application-plugin-is-building-always-relative-to-app-home-lib-directory/2012
classpath += files('src/dist/config')
doLast {
def windowsScriptFile = file getWindowsScript()
def unixScriptFile = file getUnixScript()
windowsScriptFile.text = windowsScriptFile.text.replace('%APP_HOME%\\lib\\config', '%APP_HOME%\\config')
unixScriptFile.text = unixScriptFile.text.replace('$APP_HOME/lib/config', '$APP_HOME/config')
}
}
repositories {
...
}
dependencies {
...
}
Likely what needs to happen is that I need to have the /src/dist/config folder to be copied into the build directory and added to the classpath, or have its contents be copied into a folder that is already on the classpath.
I ended up taking Opal's suggestion as a hint, and came up with the following solution. I added the following to my build.gradle file:
task processConfig(type: Copy) {
from('src/main/config') {
include '**/*'
}
into 'build/config/main'
}
classes {
classes.dependsOn processConfig
}
run {
classpath += files('build/config/main')
}
Alternatively, a simpler approach would be to add a runtime dependency to my project as such:
dependencies {
...
runtime files('src/main/config')
}
I didn't end up doing it this way, however, because my distribution package ended up having .properties files in the lib folder... and I'm just picky that way.
As you can see in the docs run is a task of type JavaExec. So classpath for it can be modified. Try to add config folder to the classpath. See here.
I have a plugin project and I need a method that will find and open a file in the project where the plugin will run.
I have in a variable the name of the file which should be inside the project. How can this be done ?
Thank you !
For a file resource inside a plug-in:
Bundle bundle = Activator.getDefault().getBundle("com.your.plugin.name");
if (bundle != null) {
URL fileURL;
try {
fileURL = FileLocator.toFileURL(bundle.getEntry("/path/to/your/file/from/the/plugin/root"));
String fileSystemPathToYourFile = fileURL.getPath());
} catch (IOException exception) {
// ...
}
}
Of course, you'll have to make sure the corresponding resource is properly included in your plug-in's binary build (bin.include property in build.properties file, or Binary Build section in the Build tab of the plug-in manifest editor).
If your file is a JAR, you may also have to export your plug-in as a directory (not 100% sure, may depend of what you actually do with your JAR resource - like add it to a project's build path or to a launch configuration classpath).