StreamCorruptedException only in a Spring Boot application - java

On a Spring Boot application (2.3.3), I have a dependency to a module developed by my company. From a service I'm calling a method from this dependency which needs a file loaded from resources (src/main/resources/META-INF/spring-main-cfg.xml), so I've copied and pasted this file to my Spring Boot application resources.
Here's the code executed in that dependency:
InputStream in = RSAEncrypter.class.getClassLoader().getResourceAsStream(keyFileName);
// StreamCorruptedException here
ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(in));
Stack:
java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:936) ~[na:1.8.0_281]
at java.io.ObjectInputStream.(ObjectInputStream.java:394) ~[na:1.8.0_281]
The thing is, I've created a new dummy Maven project with only this dependency and a main to execute that code and it works.
I don't understand what could be the reasons I've got this exception executed from my Spring Boot application. I've got the same Java version, the file read is the same.
Any ideas?

I've finally found the issue, it was the file it self since I had a global:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
The Maven doc on this say:
Warning: Do not filter files with binary content like images! This will most likely result in corrupt output.
https://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html
So I just added an exception for my file :
<resources>
<!-- Only properties files have to be filtered.
META-INF/spring-main-cfg.xml must not be or StreamCorruptedException occure by reading it -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>

Related

Wrong content of binary test resources when using ClassLoader InputStream

I have a binary file in test resources src/test/resources/file.bin:
$ ls -la src/test/resources
-rw-r--r-- 1 g4s8 g4s8 5125 Apr 30 19:53 file.bin
I'm using it for tests to verify some class.
Before testing, I need to copy the content to the file-system, I'm using Thread.currentThread().getContextClassLoader() to read the data:
#Test
public void readsContent(#TempDir final Path tmp) throws Exception {
final ClassLoader clo = Thread.currentThread().getContextClassLoader();
final Path file = Files.createFile(tmp.resolve("file.bin"));
try (
final InputStream res = new BufferedInputStream(clo.getResourceAsStream("file.bin"));
final OutputStream out = new BufferedOutputStream(Files.newOutputStream(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE))
) {
byte[] buf = new byte[8192];
for (int read = res.read(buf); read >= 0; read = res.read(buf)) {
out.write(buf, 0, read);
}
}
// test code
}
But the content of this file is bigger than expected and it differ from what was in resource file:
$ ls -la /tmp/junit18423574017636618834/
-rw-r--r-- 1 g4s8 g4s8 9350 May 1 12:22 file.bin
The result file is 9350 bytes of size, but source file was 5125.
With hex editor I investigated that only first two bytes of these files are the same, all other data is different:
What's wrong with my code? Why this file can't be read correctly using standard way via ClassLoader?
It was caused by enabled filtering Maven option inherited from parent pom.xml, it seems Maven managed to find and replace some patterns in binary file, and IDE uses Maven to prepare test resources:
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
</build>
I've overridden this option in project's pom.xml, and it's working now:
<build>
<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
<filtering>false</filtering>
</testResource>
</testResources>
</build>

Maven resource filtering of json file

In my current project I have a submodule which is using the maven exec plugin to run a test service which pulls configuration files from a location outside of the resources/testResources folders.
I need to use filtering to inject an environment variable into a few of the configuration files. This is working for one of the files, a .properties file, but not for another file which is a .json. In the latter case it simply leaves the variable in the json file. The two files are right next to each other in the filtered directory.
Here is the filtering snippet from the submodule:
<build>
<finalName>${artifactId}</finalName>
<testResources>
<testResource>
<filtering>true</filtering>
<directory>../etc</directory>
</testResource>
</testResources>
json file:
{ "customMappings" :
{ "tag:example.com:/vagrant/" : "file:${VAGRANT_CWD}" }
}
Abbreviated project structure:
project
etc
config.properties
config.json
submodule
pom.xml
The submodule is definitely loading both files, but only filtering the .properties file.
Is there something special about it being a json file that would prevent filtering from happening to it? Anything that can be done about this?
For what its worth, I did eventually get this to work. I found that I had to directly list the file for inclusion in order to get it to be processed (its been a long time so hopefully this is the correct solution):
<build>
<finalName>${artifactId}</finalName>
<testResources>
<testResource>
<filtering>true</filtering>
<directory>../etc</directory>
<includes>
<include>config.json</include>
<include>config.properties</include>
</includes>
</testResource>
</testResources>
...

Resources directory not found on classpath

I'm using Intellij 13, and I've marked a directory as a resources directory (thinking that this would make all sub-files/folders available on the classpath), however when I run the project, an exception is thrown saying that a file called database.properties (inside the resources folder) can't be found at runtime.
Any ideas?
'database' folder is the folder marked as a resources folder.
I do not know intellij-idea, but I know that maven can deal with multiple resources directory provided they are declared in pom.xml. From Maven doc on Mave Resources plugin - Specifying resources directories :
...
<resources>
<resource>
<directory>resource1</directory>
</resource>
<resource>
<directory>resource2</directory>
</resource>
<resource>
<directory>resource3</directory>
</resource>
</resources>
...
If IntelliJ-IDEA has not declared your database directory such way in the pom, maven has no chance to find it.

How do I load truststore from classpath with Apache CXF?

I'm using Apache CXF (v2.7.3) to call a SOAP service over HTTPS. I can load the truststore from a file but not from the classpath - I get "Invalid keystore format" error.
I have this config in my cfx.xml file:
<http:conduit name="*.http-conduit">
<http:tlsClientParameters>
<sec:trustManagers>
<!-- For some reason, when I use the resource field, I get a "Invalid keystore format" exception -->
<sec:keyStore type="JKS" password="MYPASSWORD"
resource="truststore.jks" />
<!-- THIS WORKS FINE: <sec:keyStore type="JKS" password="MYPASSWORD"
file="/fullPathToMyTrustStore/truststore.jks" /> -->
</sec:trustManagers>
</http:tlsClientParameters>
</http:conduit>
I can load the trust store from file, but not from the classpath. I can tell from the exception that truststore.jks file is being found, but it is invalid. This is the stacktrace of exception thrown.
Caused by: java.io.IOException: Invalid keystore format
at sun.security.provider.JavaKeyStore.engineLoad(JavaKeyStore.java:633)
at sun.security.provider.JavaKeyStore$JKS.engineLoad(JavaKeyStore.java:38)
at java.security.KeyStore.load(KeyStore.java:1185)
at org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils.getKeyStore(TLSParameterJaxBUtils.java:142)
at org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils.getTrustManagers(TLSParameterJaxBUtils.java:292)
at org.apache.cxf.configuration.jsse.TLSClientParametersConfig.createTLSClientParametersFromType(TLSClientParametersConfig.java:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:149)
I had exactly the same problem and blamed CXF at the beginning, but actually certificates were invalid on the classpath. The first thing to check is that the file in the jar is the same as in the project structure (before packing in jar).
Here are possible suspects to blame and possible solutions:
1) If you are using Maven, then filtering process may corrupt binaries (my case)
Solution: exclude certificates from Maven filtering process e.g:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.jks</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.jks</include>
</includes>
</resource>
</resources>
2) If you are using maven-assembly-plugin for building your distribution, it may corrupt binaries:
Solution: http://jira.codehaus.org/browse/MASSEMBLY-412

Programmatically getting the Maven version of your project

How do I get the Maven version of my project programatically?
In other words:
static public String getVersion()
{
...what goes here?...
}
For example, if my project would generate the jar CalculatorApp-1.2.3.jar, I want getVersion() to return 1.2.3.
Create file version.prop in src/main/resources with the following contents:
version=${project.version}
Add the following to your project's pom:
<build>
...
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/version.prop</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/version.prop</exclude>
</excludes>
</resource>
</resources>
...
</build>
Add the following method:
public String getVersion()
{
String path = "/version.prop";
InputStream stream = getClass().class.getResourceAsStream(path);
if (stream == null)
return "UNKNOWN";
Properties props = new Properties();
try {
props.load(stream);
stream.close();
return (String) props.get("version");
} catch (IOException e) {
return "UNKNOWN";
}
}
p.s. Found most of this solution here: http://blog.nigelsim.org/2011/08/31/programmatically-getting-the-maven-version-of-your-project/#comment-124
For jar files, you have the MANIFEST.MF as the default place to put the Implementation-Version there. Maven supports building the jar file like this.
See also
How do I add an Implementation-Version value to a jar manifest using Maven?

Categories