One to store the final list of jar dependencies and the other to store the visited Jar names. This way I could keep track of my recursive loop and wouldn't loose my way if there were any circular dependencies. A little background, the jar in question had about 500 dependency jars and many had cyclic dependencies. Here is my initial code which threw the ConcurrentModificationException.
import java.util.HashMap; .... public class FindJarDependencies { String pathToInitialJar; // Mapping from "name of jar" -> "path of jar" MapWhen I ran this program all hell broke loose on line number 54. On further debugging I figured out the problem.dependencies = new HashMap (); // Mapping from "name of jar" -> "True|false" ( True, if jar has been visited otherwise false) Map visitedJars = new HashMap (); public List getDependenciesForJar(String pathToInitialJar) { List result = ArrayList (); Iterator iter = dependencies.valueSet().iterator(); while (iter.hasNext()) { result.add(iter.next()); } return result; } /** * In view of having a short blog, * lets assume this method adds the manifest-classpath * for a given jar into the dependency Map. */ private void addJarDependencies(String pathOfJar) { // Open the jar file and add manifest-classpath to dependencies Map. } public void walkDependencyJar(String pathOfJarDependency) { // Strip name from path of jar String jarName = getNameFromPath(pathOfJarDependency); // add jar if not present in dependency Map. if (!dependency.get(jarName)) addJarDependencies(pathOfJarDependency); // Add dependency Map values to visitedJars. // By default the value would be false. for (String s : dependencies.keySet()) { if (!visitedJar.containsKey(s)) { visitedJar.put(s,false); } } // Iterate through the visitedJars and add them to the dependency as and when // you find new jars. Iterator iter = visitedJars.keySet().iterator(); while(iter.hasNext()) { String jarClasspath = iter.next(); if (!visitedJar.get(jarClasspath)) { visitedJar.put(jarClasspath, true); // Oops ConcurrentModificationException walkDependencyJar(); } } } }
In my code I was iterating over "visitedJar" at line 51 but inside the same loop at line number 54 I am trying to change the Map. This was the root cause. Java Iterator doesn't allow for changes to iterating data structure. For better understanding I wrote a stripped down version which throws the same error.
package com.example.ConcurrentModificationExample; import java.util.*; public class ConcurrentModificationExample { static MapWhen I ran this code. I was able to reproduce the problem. Wala I knew exactly what was going on. After some googling I found couple of good resources and the way out. There are different solutions to this problem. They are:map = new HashMap (); public static void main(String[] args) { map.put(1, false); map.put(2, false); map.put(3, false); map.put(4, false); Iterator iter = map.keySet().iterator(); while(iter.hasNext()) { int value = iter.next(); System.out.println("Value is: '" + value +"'"); if (value == 3) map.put(5, false); // place where ConcurrentModificationException occurs } } }
However in my code I went ahead with ConcurrentHashMap and it worked fine. If time permits I would like to make this a multi threaded program. May become a topic for my later posts.
No comments:
Post a Comment