I'm having trouble with reading JVM configuration properties with System.getProperties()
due to the fact that in some environments I'm getting java.util.ConcurrentModificationException
after JVM restart.
[err] java.util.ConcurrentModificationException [err] at java.util.Hashtable$Enumerator.next(Hashtable.java:1502)
I have to restart multiple times the VM in order to avoid previous mentioned error.
What I have done until now:
- synchronized the method where the properties keys and values are being read and written to a log file;
- because Java's Properties object is a HashMap I have tried to use a synchronized Map;
- I have tried to simulate the concurrency issue by using more threads that share the same Properties object, however I have't been able to reproduce the error in my local envionment;
I'm using StringBuffer due to the fact that is thread safe;
private static final Logger LOG = LoggerFactory.getLogger(PropertyLogger.class); public static synchronized final void logProperties() { Level oldLevel = LOG.getLevel(); LOG.setLevel(Level.INFO); Properties props = System.getProperties(); Map<Object, Object> shared = Collections.synchronizedMap(new HashMap<>()); shared.putAll(props); for (final Entry<Object, Object> entry : shared.entrySet()) { try{ StringBuffer buffer = new StringBuffer(); buffer.append(entry.getKey()); buffer.append(" = "); //$NON-NLS-1$ buffer.append(entry.getValue()); LOG.info(buffer.toString()); } catch (Exception ex){ ex.printStackTrace(); } } LOG.setLevel(oldLevel); }
I could be that the problems I'm dealing with, are triggered also by log4j library, used for writting, which is far I know not thread safe.
I have added the stack trace:
[err] java.util.ConcurrentModificationException [err] at java.util.Hashtable$Enumerator.next(Hashtable.java:1502) [err] at com.myapp.common.PropertyLogger.logProperties(PropertyLogger.java:23) [err] at com.myapp.input.ConfigurationServer.parse(ConfigurationServer.java:710) [err] at com.myapp.input.ConfigurationServer.configure(ConfigurationServer.java:94) [err] at com.myapp.input.Application.run(Application.java:78) [err] at com.myapp.input.servlet.InputStarter$ValidatorThread.run(InputStarter.java:113)
Regards,
PS: Should I add synchronized blocks to call the method? For example: In parse method should call my code with
synchronized(this) {
PropertyLogger.logProperties();
}