I am working on creating a library(it will be a jar file). I have a running application "A" which has a dependency on this lib I am creating- "B.jar". Inside src/resources folder of A, we have a file eg."test.yaml". B has some code which is to be called from A and the file path passed as string alongwith. Code in B should be able to read the file content and do further processing..
Here is my code in lib "B"
private TestDataVO getTestDataVO(String testFileName) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(this.testFileName);
testData = ymlMapper.readValue(is, TestDataVO.class);
return testData;
}
The caller code in "A":
getTestDataVO("test.yaml")
The problem with my code is the this.getClass.getClassLoader() gives classpath of "B" rather than "A" and thus not able to read test.yaml which is on "A" 's classpath. How can I can modify getTestDataVO to read the file from "A"'s classpath. Sending absolute path is not an option. I 'll be happy to clarify any further.
PS.- I have tried searching for other answers, all of which point to resource file present in jar and reading from calling application. I am trying the other way around-file in calling application, to be read from jar's context.
Your getTestDataVO
code doesn't care how it gets its data, as long as it gets it.
Hence, the correct parameter type of getTestDataVO
is not String
. Instead it should be byte[]
, or InputStream
, or possibly both.
Also note that what you're doing so far is wrong (it's MyClass.class.getResource
, not getClass().getClassLoader()...
. The getClassLoader()
part is pointless typing and breaks in rare circumstances. getClass()
breaks when subclassing. Hence why that's the only correct way for this kind of task).
Let's say you make that inputstream. Then you can do:
private TestDataVO getTestDataVO(InputStream is) {
try {
testData = ymlMapper.readValue(is, TestDataVO.class);
} finally {
is.close();
}
}
// and to call:
getTestDataVO(MyClass.class.getResourceAsStream("test.yaml"));
Or possibly even by using just getResource
:
private TestDataVO getTestDataVO(URL resource) {
try (var is = resource.openStream()) {
testData = ymlMapper.readValue(is, TestDataVO.class);
}
}
// and to call:
getTestDataVO(MyClass.class.getResource("test.yaml"));
In both of those the crucial aspect is that A ends up using its loader to find this resource, and then passes the found resource to B, instead of asking B to find it which is tricky as it's part of A's classpath, not B's.
EDIT:
A second idea, but I strongly recommend against this:
private TestDataVO getTestDataVO(Class<?> context, String resource) {
try (var is = context.getResourceAsStream(resource)) {
testData = ymlMapper.readValue(is, TestDataVO.class);
}
}
// to call:
class Example {
void foo() {
testDataTool.getTestDataVO(Example.class, "/my/resource.txt");
}
}
This is the closest equivalent of 'I just want callers to pass in a string'. You can do that.. if you also pass along the class to use as 'context' - use this class's loader architecture to load this resource. There is no way to get, not even from a stack trace, the java.lang.Class
instance representing 'the class whose code called me' in case that's where you're thinking is heading.