Search code examples
javaeclipsegradleconfigurationfile-properties

Eclipse - Gradle sourceSets, how to manage my yaml property files


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);
}

Solution

  • 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.

    1. 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.