3

I'm using a generic entity converter in my Project. I've noticed that sometimes the converter throws a java.util.ConcurrentModificationException There is also a 4 years old Post which has no answer.

I tried to use Iterator instead of a plain for-loop but it didn't helped.

Is there a way to create a thread-safe converter?

@FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {

    private static Map<Object, String> entities = new WeakHashMap<Object, String>();

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object entity) {
        synchronized (entities) {
            if (!entities.containsKey(entity)) {
                String uuid = UUID.randomUUID().toString();
                entities.put(entity, uuid);
                return uuid;
            } else {
                return entities.get(entity);
            }
        }
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String uuid) {

        Iterator<Map.Entry<Object, String>> tempIt = entities.entrySet().iterator();
        while (tempIt.hasNext()) {
            Map.Entry<Object, String> nextObj = tempIt.next();
            if (nextObj.getValue().equals(uuid)) {
                return nextObj.getKey();
            }
        }

        return null;
    }

}
Mihawk
  • 815
  • 3
  • 14
  • 31

2 Answers2

2

Wrap the body of getAsObject in a block synchronized on entities, as in the other method.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
2

Another way to achieve it is to use Java's ConcurrentMap in order to guarantee thread-safety. In a one-liner:

private static ConcurrentMap<Object, String> entities = new ConcurrentHashMap<>();

@Override
public String getAsString(FacesContext context, UIComponent component, Object entity) {
    return entities.putIfAbsent(entity, UUID.randomUUID().toString());
}

Starting from Java 8, you could also take advantage from computeIfAbsent, which will only calculate the UUID if the key is absent:

return entities.putIfAbsent(entity, 
    entities.computeIfAbsent(k -> UUID.randomUUID().toString()));

Or even enhance it with Guava's caching utilities, if you want to keep weak references.

Aritz
  • 30,971
  • 16
  • 136
  • 217