0

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();
}
Prasath
  • 1,233
  • 2
  • 16
  • 38
SocketM
  • 564
  • 1
  • 19
  • 34

1 Answers1

2

The issue is not with the logging framework but with line

shared.putAll(props);

Properties class extends Hashtable and it is possible for the System properties to change at any time. With shared.putAll(props) you are iterating through the object props of Properties(Hashtable) class and if any value is modified during the iteration, we will get this error of ConcurrentModificationException

A solution would be to call clone() on the System.Properties() object before iterating

Ashishkumar Singh
  • 3,580
  • 1
  • 23
  • 41
  • Hello Ashishkummar, before using Collections.synchronizedMap, I have used a HashMap for iterration ( Properties props = System.getProperties(); for (final Entry entry : props.entrySet())). The error mentioned in my question is from when I used a Hashmap not Collections.synchronizedMap. Thank you for your advice with clone call. I will try it. – SocketM Oct 23 '18 at 10:30
  • I don't think HashMap or Collections.synchronizedMap in your code will cause this error. If you see the stack trace, there is HashTable method call – Ashishkumar Singh Oct 23 '18 at 10:45
  • 1
    @SocketM It's not enough if you syncjronize the access to `System.getProperties()` in your code. Still other threads can call `System.getProperties()` without taking you synchronization into account, without being aware of it. – LuCio Oct 23 '18 at 10:51
  • @LuCio so you recommend as Ashishkumar also recommended, to use a clone method before hand; this way you have a copy of the object at a certain point which will not be affected by change. – SocketM Oct 23 '18 at 11:05
  • I don't know where your `ConcurrentModificationException` comes from. Please note [that](https://docs.oracle.com/javase/10/docs/api/index.html?overview-summary.html) `Properties` _is thread-safe: multiple threads can share a single Properties object without the need for external synchronization._ (see also [this answer](https://stackoverflow.com/a/15039996/2838289)). – LuCio Oct 23 '18 at 11:43