Search code examples
javagradleshared-librariesclasspathjava-resources

Java Library: load file from resources


I'm a developing a Java library that is going to be used internally in our company. It provides a wrapper around Aerospike. When a user calls the factory method, the library should read a file from its resources.

public static IAerospikeClient newTypedClient(IAerospikeClient client) {
    LOG.info("Trying to load aerospike schema yml");

    URL resource = AerospikeTypedClientFactory.class.getClassLoader().getResource("schema.yml");
    if (resource == null) {
        throw new IllegalArgumentException("File 'schema.yml' is not found");
    }

    byte[] bytes = Files.readAllBytes(Paths.get(resource.toURI()));
    return new TypedAerospikeClient(client, new String(bytes));
}

Then I'm trying to add this library as a dependency and call newTypedClient method, I get this error.

Caused by: java.nio.file.FileSystemNotFoundException
    at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171)
    at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157)
    at java.nio.file.Paths.get(Paths.java:143)
    at com.example.AerospikeTypedClientFactory.newTypedClient(AerospikeTypedClientFactory.java:30)

Is there any way to overcome this error? I guess I could add the schema.yml file to the resources folder of the library's consumer. But I definitely don't want to go into this. Because the purpose is to get rid of configurations and put them within a single artefact.

EDIT 1

For those who refer to How should I use getResource() in Java? question. I do understand that getResource reads from the classpath. My question is how can I get this work? Perhaps, I can replace getResource/getResourceAsStream usage with something else. The idea is that schema.yml has to be packed within the library .jar archive.

EDIT 2

Here is the problem step by step.

  1. The described code is put within the lib module.
  2. The lib is packed to jar and published to Artifactory.
  3. The service put a dependency on lib.
  4. The service calls the newTypedClient method from the lib.
  5. The FileSystemNotFoundException raises.

So, I need lib to read schema.yml from the jar it is packed to. Is it possible? Here is the diagram that describes the process.

process example


Solution

  • For those who interested, I wrote a custom Gradle task that generates a class with the static String field. This field value equals to the actual file content. The task runs before compileJava one. For the generation itself I use JavaPoet library. Here is the example.

    task generateSchemaClass {
        def className = "Schema_"
        def directoryPath = "src/main/java/com/example/generated"
        def filePath = directoryPath + className + ".java"
        doFirst {
            TypeSpec typeSpec = TypeSpec.classBuilder("Schema_")
                    .addModifiers(Modifier.PUBLIC)
                    .addAnnotation(
                            AnnotationSpec.builder(SuppressWarnings.class)
                                    .addMember("value", '$S', "all")
                                    .build()
                    )
                    .addField(
                            FieldSpec.builder(String.class, "CONTENT", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                                    .initializer('$S', file('src/main/resources/schema.yml').text)
                                    .build()
                    )
                    .build()
            JavaFile javaFile = JavaFile.builder("com.example.generated", typeSpec).build()
            file(directoryPath).mkdirs()
    
            def output = file(filePath)
            output.createNewFile()
            output.text = javaFile.toString()
        }
    }
    
    compileJava.dependsOn(generateSchemaClass)