RequestFactoryJarExtractor intermittently throws exceptions when running
on a multi-core build machine.  The build infrastructure did not catch
the error and kept going, leaving a corrupt request factory jar file.

This change updates the Map and Sets used to be concurrent
datastructures and caps the number of threads at 4.

Review at http://gwt-code-reviews.appspot.com/1425805

Review by: robertvawter@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10044 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/requestfactory/build.xml b/requestfactory/build.xml
index 5139e8c..dae489d 100755
--- a/requestfactory/build.xml
+++ b/requestfactory/build.xml
@@ -32,6 +32,7 @@
           <pathelement path="${gwt.build.out}/dev/bin-test" />
           <pathelement path="${gwt.build.out}/user/bin-test" />
         </classpath>
+        <jvmarg value="-ea"/>
         <arg value="@{target}"/>
         <arg file="${gwt.build.lib}/requestfactory-@{target}.jar"/>
       </java>
diff --git a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java
index f1e4718..1395ba8 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java
@@ -72,8 +72,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -81,6 +79,8 @@
 import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -624,6 +624,11 @@
       WriteOperation.class, RequestFactorySource.class, SimpleEventBus.class
   };
 
+  /**
+   * Maximum number of threads to use to run the Extractor.
+   */
+  private static final int MAX_THREADS = 4;
+  
   static {
     List<Class<?>> sharedClasses = Arrays.<Class<?>> asList(SHARED_CLASSES);
 
@@ -687,7 +692,7 @@
     RequestFactoryJarExtractor extractor = new RequestFactoryJarExtractor(
         errorContext, classLoader, jarEmitter, seeds, mode);
     extractor.run();
-    System.exit(0);
+    System.exit(extractor.isExecutionFailed() ? 1 : 0);
   }
 
   /**
@@ -732,6 +737,7 @@
     return false;
   }
 
+  private boolean executionFailed = false;
   private final Emitter emitter;
   private final ExecutorService ex;
   private final BlockingQueue<Future<?>> inProcess = new LinkedBlockingQueue<Future<?>>();
@@ -739,8 +745,8 @@
   private final Loader loader;
   private final Mode mode;
   private final List<Class<?>> seeds;
-  private final Map<Type, Type> seen = new HashMap<Type, Type>();
-  private final Set<String> sources = new HashSet<String>();
+  private final Map<Type, Type> seen = new ConcurrentHashMap<Type, Type>();
+  private final Set<String> sources = new ConcurrentSkipListSet<String>();
   private final ExecutorService writerService;
 
   public RequestFactoryJarExtractor(Logger logger, Loader loader,
@@ -751,7 +757,8 @@
     this.seeds = seeds;
     this.mode = mode;
 
-    ex = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+    int numThreads = Math.min(MAX_THREADS, Runtime.getRuntime().availableProcessors());
+    ex = Executors.newFixedThreadPool(numThreads);
     writerService = Executors.newSingleThreadExecutor();
   }
 
@@ -770,6 +777,7 @@
       } catch (InterruptedException retry) {
       } catch (ExecutionException e) {
         e.getCause().printStackTrace();
+        executionFailed  = true;
       }
     }
     emitter.close();
@@ -782,6 +790,10 @@
     inProcess.add(writerService.submit(new EmitOneType(state)));
   }
 
+  private boolean isExecutionFailed() {
+    return executionFailed;
+  }
+
   /**
    * Look at constant values from the bytecode, processing referenced types.
    */