Java class loader bug: Caused by: java.io.IOException: Stream closed - java

I'm getting a strange bug regarding I believe class loader issues when I deploy my webapp to Tomcat. The bug doesn't appear when I run my webapp locally using Jetty. It seems like my input streams for my .yml resource files are being closed for some reason when they shouldn't be. This bug first appeared when I tried to convert my single module project into a multi module project. Before that, it was working fine on Tomcat using the exact same code:
Caused by: org.yaml.snakeyaml.error.YAMLException: java.io.IOException: Stream closed
at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:200)
at org.yaml.snakeyaml.reader.StreamReader.<init>(StreamReader.java:60)
at org.yaml.snakeyaml.Yaml.load(Yaml.java:412)
at com.config.ConfigProvider.<init>(ConfigProvider.java:20)
... 49 more
Caused by: java.io.IOException: Stream closed
at java.io.PushbackInputStream.ensureOpen(PushbackInputStream.java:57)
at java.io.PushbackInputStream.read(PushbackInputStream.java:149)
at org.yaml.snakeyaml.reader.UnicodeReader.init(UnicodeReader.java:90)
at org.yaml.snakeyaml.reader.UnicodeReader.read(UnicodeReader.java:122)
at java.io.Reader.read(Reader.java:123)
at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:184)
... 55 more
Here's the line that causes the bug:
String s = ConfigProvider.getConfig().getString("test");
Here's the ConfigProvider class. It basically scans for all resource files of regex ^.*\\.config\\.yml$, converts it into a Map<String, Object>, and combines all the obtained Map<String, Object> into a single Map<String, Object>:
1 public class ConfigProvider {
2 protected static final String CONFIG_PACKAGE = ConfigProvider.class.getPackage().getName();
3 protected static final Pattern CONFIG_PATH_REGEX = Pattern.compile("^.*\\.config\\.yml$");
4
5 private static final ConfigProvider INSTANCE = new ConfigProvider();
6 private Map<String, Object> configMap;
7
8 protected ConfigProvider() {
9 configMap = new HashMap<String, Object>();
10
11 Set<String> configPaths = new Reflections(CONFIG_PACKAGE,
12 new ResourcesScanner()).getResources(CONFIG_PATH_REGEX);
13
14 if (configPaths.isEmpty()) {
15 throw new RuntimeException("no config paths found");
16 }
17
18 for (String path : configPaths) {
19 InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
20 Map<String, Object> fullConfig = new Map<String, Object>((Map) new Yaml().load(inputStream));
21
22 try {
23 inputStream.close();
24 } catch (IOException e) {
25 throw new RuntimeException("error closing stream");
26 }
27
28 MapUtils.merge(configMap, fullConfig);
29 }
30 }
31
32 public static ConfigMap getConfig() {
33 return INSTANCE.configMap;
34 }
35 }
Here's my project structure, titled Foo:
- Foo (this is a module)
- .idea
- application (this is a module)
- src
- main
- java
- resources
- application.config.yml
- webapp
- test
- pom.xml
- client (this is a module)
- src
- main
- java
- resources
- client.config.yml
- webapp
- test
- pom.xml
- pom.xml
ConfigProvider is a class I get from my parent pom file (Foo/pom.xml). I package a WAR file from the application module (Foo/application/target/application.war), and deploy it with Tomcat. Previously, my project was a single module project with just a Foo module being identical to application module. Then I added a client module and converted the project into a multi module project, and the problem has showed up. I think it's because my class loader is getting messed up due to the multiple modules. I've spent a lot of time trying to debug this and still haven't gotten anywhere. Anyone know what could be the cause, or can think of possible things to try?
Please let me know if you need more info.

According to this post, that exception could mean that the .yml file is simply not found. Since you changed your project structure, it is possible that the logic used to build the configPaths needs to be modified for the new structure. Did you try to log the content of configPaths to see if the paths are correct for the new structure?
Also make sure that the .yml files are included in the .war file. Some build systems handle resources differently than java class files.

Bean class member variable should match with .yml file keys and second option is there might be chances that you might be giving wrong file path.

Related

Java 8 dealing unicode characters in file names unexpectedly

I am frustrated to find a solution to the issue as explained in the question title. To help you understand clearly my problem, I am presenting below a little detail.
I have some files having an accent in their names which are storing in a directory. Running ls -lht, it cannot show the files correctly. The accents are decoded incorrectly. But if I press the tab key to apply autocompletion from the terminal, the file name can be shown as expected. See the snippet below.
tc_pst03#login-01: 6] ls -lht
total 704K
-rw-r----- 1 tc_pst03 pst_pub 86K Oct 9 00:27 Li??2012_Modelling osteomyelitis_Control Model.cps
-rw-r----- 1 tc_pst03 pst_pub 46K Oct 9 00:27 Li??2012_Modelling osteomyelitis_Control Model.xml
-rw-r----- 1 tc_pst03 pst_pub 11K Oct 9 00:27 Li??2012_Modelling osteomyelitis_Control Model.sedml
tc_pst03#login-01: 6] mv Liò2012_Modelling\ osteomyelitis_Control\ Model.
When using a Java snippet to get all those files, I get the results which are Li??2012... instead of Liò. I have looked for shared solutions in our communities but no solution works for my problem. Below is the Java snippet I have tried to get the list of those files.
List<File> get(String modelId, int revisionNumber) throws ModelException {
File modelDirectory = new File(modelCacheDir, modelId)
File revisionDirectory
List returnedFiles = new LinkedList<File>()
try {
revisionDirectory = new File(modelDirectory, revisionNumber.toString())
if (!revisionDirectory.exists()) {
throw new FileNotFoundException()
} else {
returnedFiles = Files.list(revisionDirectory.toPath())*.toFile() //revisionDirectory.listFiles().toList()
}
if (returnedFiles?.isEmpty()) {
String message = """The cache directory of this model ${modelId} revision ${revisionNumber} is empty. \
The model cache builder will be launched again."""
throwModelException(modelId, message)
}
} catch (FileNotFoundException me) {
String message = """The files associated with this model ${modelId}, \
revision ${revisionNumber} hasn't been cached yet"""
throwModelException(modelId, message)
}
return returnedFiles
}
I suspected JVM does use the default charset so I manually enable UTF-8 by defining it in JAVA_TOOLS_OPTIONS: export JAVA_TOOLS_OPTIONS=-Dfile.encoding="UTF-8".
Some results are printed below:
[
._Li?2012_Modelling osteomyelitis_Control Model.xml4483388255187556135.tmp,
._Li?2012_Modelling osteomyelitis_Control Model.xml8578169841449575225.tmp,
Li��2012_Modelling osteomyelitis_Control Model.sedml,
._Li?2012_Modelling osteomyelitis_Control Model.xml1056906750418910165.tmp,
Li��2012_Modelling osteomyelitis_Control Model.xml,
Li��2012_Modelling osteomyelitis_Control Model.cps]
I need to get those files' names to compare with the same files names persisted in the database. However, the file names getting from the file system are decoded improperly so it is never equal to the ones already saved in the database.
Do anyone know why the issue is happening. Any ideas? Thanks!

Gradle removing comments and reformatting properties file

When I am trying to edit a property within Gradle it re-formats my entire properties file and removes the comments. I am assuming this is because of the way Gradle is reading and writing to the properties file. I would like to just change a property and leave the rest of the properties file untouched including leaving the current comments in place and order of the values. Is this possible to do using Gradle 5.2.1?
I have tried to just use setProperty (which does not write to the file), used a different writer: (versionPropsFile.withWriter { versionProps.store(it, null) } )
and tried a different way to read in the properties file: versionProps.load(versionPropsFile.newDataInputStream())
Here is my current Gradle code:
File versionPropsFile = file("default.properties");
def versionProps = new Properties()
versionProps.load(versionPropsFile.newDataInputStream())
int version_minor = versionProps.getProperty("VERSION_MINOR")
int version_build = versionProps.getProperty("VERSION_BUILD")
versionProps.setProperty("VERSION_MINOR", 1)
versionProps.setProperty("VERSION_BUILD", 2)
versionPropsFile.withWriter { versionProps.store(it, null) }
Here is a piece of what the properties file looks like before gradle touches it:
# Show splash screen at startup (yes* | no)
SHOW_SPLASH = yes
# Start in minimized mode (yes | no*)
START_MINIMIZED = no
# First day of week (mon | sun*)
# FIRST_DAY_OF_WEEK = sun
# Version number
# Format: MAJOR.MINOR.BUILD
VERSION_MAJOR = 1
VERSION_MINOR = 0
VERSION_BUILD = 0
# Build value is the date
BUILD = 4-3-2019
Here is what Gradle does to it:
#Wed Apr 03 11:49:09 CDT 2019
DISABLE_L10N=no
LOOK_AND_FEEL=default
ON_MINIMIZE=normal
CHECK_IF_ALREADY_STARTED=YES
VERSION_BUILD=0
ASK_ON_EXIT=yes
SHOW_SPLASH=yes
VERSION_MAJOR=1
VERSION_MINOR=0
VERSION_BUILD=0
BUILD=04-03-2019
START_MINIMIZED=no
ON_CLOSE=minimize
PORT_NUMBER=19432
DISABLE_SYSTRAY=no
This is not a Gradle issue per se. The default Properties object of Java does not preserve any layout/comment information of properties files. You can use Apache Commons Configuration, for example, to get layout-preserving properties files.
Here’s a self-contained sample build.gradle file that loads, changes and saves a properties file, preserving comments and layout information (at least to the degree that is required by your example file):
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.apache.commons:commons-configuration2:2.4'
}
}
import org.apache.commons.configuration2.io.FileHandler
import org.apache.commons.configuration2.PropertiesConfiguration
import org.apache.commons.configuration2.PropertiesConfigurationLayout
task propUpdater {
doLast {
def versionPropsFile = file('default.properties')
def config = new PropertiesConfiguration()
def fileHandler = new FileHandler(config)
fileHandler.file = versionPropsFile
fileHandler.load()
// TODO change the properties in whatever way you like; as an example,
// we’re simply incrementing the major version here:
config.setProperty('VERSION_MAJOR',
(config.getProperty('VERSION_MAJOR') as Integer) + 1)
fileHandler.save()
}
}

How to integrate part of Java JDK7 as a standalone library in JDK 8 (JDBC-ODBC Bridge)

I am using the JDBC-ODBC bridge to connect to a QuickBooks database. (Intuit has offered sparse support, and does not have their own direct java driver to my knowledge). This was great in JRE/JDK 7. Now in JRE/JDK 8 this feature was removed. That's fine. I am attempting to use the solution mentioned by "frhack" in THIS (Removal of JDBC ODBC bridge in java 8) post. However, what frhack is suggesting is to essentially move the bridge directly from Java 7 to Java 8, a process which i would have to do each time if i wish to update my JDK.
What i want to do instead, is make the jdbc.jar file, with the DLL file included, so that i can simply use it as a standalone library. I created a jar as instructed, but also placed the DLL in the root of the archive.
This seems to work...... partly. I did not need to change any of my code, and the class loads! However, when i try to connect to the data source, the new library throws an NPE. Since this is compiled code (Class files), i can not see the exact problem, but i presume one possibility is it doesn't know that the location of the dll file has changed. Following is a section of my code, alongside some output of a stack trace.
Output of stack trace:
Debug - Exception caught!
java.lang.NullPointerException
at sun.jdbc.odbc.JdbcOdbcDriver.initialize(JdbcOdbcDriver.java:453)
at sun.jdbc.odbc.JdbcOdbcDriver.connect(JdbcOdbcDriver.java:153)
at java.sql.DriverManager.getConnection(Unknown Source)
at java.sql.DriverManager.getConnection(Unknown Source)
at Dashboard.Modules.Uploader.Data.DatabaseManager.connect(DatabaseManager.java:83)
at Dashboard.Modules.Uploader.Data.DatabaseManager.outputData(DatabaseManager.java:57)
at Dashboard.Modules.Uploader.Uploader.exportData(Uploader.java:182)
at Dashboard.Modules.Uploader.Uploader.run(Uploader.java:112)
Code snippet of DatabaseManager.java:
25 private final String url = "jdbc:odbc:quickbooks";
...
28 private final String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
...
77 private boolean connect()
78 {
79 boolean result = true;
80 try
81 {
82 Class.forName(driver);
83 con = DriverManager.getConnection(url);
84 }
85 catch (ClassNotFoundException | SQLException e)
86 {
87 result = false;
88 e.printStackTrace();
89 System.out.println(e.getMessage());
90 }
91 catch (Exception x)
92 {
93 System.out.println("Debug - Exception caught!");
94 x.printStackTrace();
95 }
96
97 return result;
98 }
Is my assumption correct? Is there a way to structure the jar file to include the DLL file so that it is usable, or another place within my project i can place this? I would prefer to embed it into the jar itself, because of the ease of use once this is set up.
Thanks in advance for any help and advice on this!

Maven. Nested properties filtering doesn't work

I have two property files:
#environment.properties
env = production
and second file is:
#commons.properties
production.port = 123
test.port = 567
Also,I have resource file which need be filtered by environment.properties file and commons.properties file and copied.
The resource-file contains:
${${env}.port}
So,I want to filter my resource file with first file and get:
${production.port}
and then I want to filter it with second filter file and get:
123
I use maven 3.2.5 and the resource-file isn't filtered at all.
I know that there's issue related with this problem:
https://jira.codehaus.org/browse/MRESOURCES-70 But it still unresolved.
So,my question is - is there any solution to resolve this problem? (actually,I think that resource-plugin should be modified for work with nested property filtering).
And second question - does exist any way to avoid this problem by refactoring,I mean any other architecture solution. Or, what would you do if you had same problem?

MIME-type checking with JMimeMagic - MagicMatchNotFoundException

I need check currentFile of MIME-type. If result is success and file have MIME-type return true. If wasn't checking succed return false.
With this goal I use JMimeMagic.
I try do this according this post
Output from this code is - net.sf.jmimemagic.MagicMatchNotFoundException
You need have JDK 7 - for changing File to byte[] at this way(Files.readAllBytes(path)).
Code:
class ProbeContentTypeCheker implements Checker {
#Override
public boolean check(File currentFile) {
String mimeType = null;
try {
Path path = Paths.get(currentFile.getAbsolutePath());
byte[] data = Files.readAllBytes(path);
MagicMatch match = Magic.getMagicMatch(data);
mimeType = match.getMimeType();
} catch (MagicParseException | MagicMatchNotFoundException
| MagicException | IOException e) {
e.printStackTrace();
}
if (null != mimeType) {
return true;
}
return false;
}
}
Output (only if it's "wrong" type):
net.sf.jmimemagic.MagicMatchNotFoundException
at net.sf.jmimemagic.Magic.getMagicMatch(Magic.java:222)
at net.sf.jmimemagic.Magic.getMagicMatch(Magic.java:170)
at task.ProbeContentTypeCheker.check(FileScan.java:357)
at task.FolderScan.findFiles(FileScan.java:223)
at task.FolderScan.findFiles(FileScan.java:215)
at task.FolderScan.run(FileScan.java:202)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
If file is "ok" type => output to console normal. But after some time arise another exception:
Exception in thread "pool-1-thread-1" java.lang.OutOfMemoryError: Java heap space
at java.lang.String.toCharArray(String.java:2753)
at org.apache.oro.text.perl.Perl5Util.match(Unknown Source)
at net.sf.jmimemagic.MagicMatcher.testRegex(MagicMatcher.java:663)
at net.sf.jmimemagic.MagicMatcher.testInternal(MagicMatcher.java:433)
at net.sf.jmimemagic.MagicMatcher.test(MagicMatcher.java:341)
at net.sf.jmimemagic.Magic.getMagicMatch(Magic.java:208)
at net.sf.jmimemagic.Magic.getMagicMatch(Magic.java:170)
at task.ProbeContentTypeCheking.check(FileScan.java:384)
at task.FolderScan.findFiles(FileScan.java:228)
at task.FolderScan.findFiles(FileScan.java:225)
at task.FolderScan.findFiles(FileScan.java:225)
at task.FolderScan.run(FileScan.java:209)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Question:
How do solve this arises of exception?
JMimeMagic 0.1.2 depends on Commons Logging 1.0.4
A NoClassDefFoundError means that the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.
The solution would be to add the commons-logging-1.0.4.jar to your classpath.
Note that JMimeMagic has other 3rd party dependencies:
Jakarta ORO 2.0.8
Log4j 1.2.8
Xerces 2.4.0 (optional)
xml-apis 2.0.2
xmlParserAPIs 2.0.2
Update - MagicMatchNotFoundException
The MagicMatchNotFoundException is thrown if no mime type match is found for the provided data.
You can set the log level of net.sf.jmimemagic to DEBUG to get more information about what is going on
Update 2 - OutOfMemoryError
The OOM looks related to the behavior of JmimeMagic. In some cases it will try to run a regular expression against the entire byte array input to find the magic number match. See this reported issue for the Nuxeo Enterprise Platform.
I think you can solve this issue by limiting the size of the byte array you pass to getMagicMatch

Categories

Resources