99soft / rocoto

expanded properties file parsing for Google Guice
http://99soft.github.com/rocoto/
Apache License 2.0
22 stars 12 forks source link

ConcurrentModificationException when running tests in parallel #4

Open hbf opened 11 years ago

hbf commented 11 years ago

Hi there,

I have just encountered a little bug that manifests itself when you use rocoto's bindSystemProperties() in unit tests and run these concurrently in the same VM. The method internally creates a new iterator from System.getProperties() and the latter may be changed externally, thus resulting in:

INFO: An exception was caught and reported. Message: java.util.ConcurrentModificationException
java.util.ConcurrentModificationException
    at java.util.Hashtable$Enumerator.next(Hashtable.java:1031)
    at org.nnsoft.guice.rocoto.configuration.PropertiesIterator.next(PropertiesIterator.java:93)
    at org.nnsoft.guice.rocoto.configuration.PropertiesIterator.next(PropertiesIterator.java:27)
    at org.nnsoft.guice.rocoto.configuration.ConfigurationModule.bindProperties(ConfigurationModule.java:151)
    at org.nnsoft.guice.rocoto.configuration.ConfigurationModule.bindProperties(ConfigurationModule.java:127)
    at org.nnsoft.guice.rocoto.configuration.ConfigurationModule.bindSystemProperties(ConfigurationModule.java:161)
    ...
    at com.google.inject.AbstractModule.configure(AbstractModule.java:59)
    at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:223)
    at com.google.inject.AbstractModule.install(AbstractModule.java:118)
        ...
    at com.google.inject.AbstractModule.configure(AbstractModule.java:59)
    at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:223)
    at com.google.inject.spi.Elements.getElements(Elements.java:101)
    at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:133)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:103)
    at com.google.inject.Guice.createInjector(Guice.java:95)
    at com.google.inject.Guice.createInjector(Guice.java:72)
    at com.google.inject.Guice.createInjector(Guice.java:62)
    ....
    at org.scalatest.WordSpec$$anonfun$runTests$1.apply(WordSpec.scala:2325)
    at org.scalatest.WordSpec$$anonfun$runTests$1.apply(WordSpec.scala:2325)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:323)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:311)
    at scala.collection.immutable.List.foreach(List.scala:309)
    at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:311)
    at org.scalatest.SuperEngine.org$scalatest$SuperEngine$$runTestsInBranch(Engine.scala:302)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:331)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:311)
    at scala.collection.immutable.List.foreach(List.scala:309)
    at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:311)
    at org.scalatest.SuperEngine.org$scalatest$SuperEngine$$runTestsInBranch(Engine.scala:306)
    at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:384)
    at org.scalatest.WordSpec$class.runTests(WordSpec.scala:2325)
    ...
    at org.scalatest.tools.ScalaTestFramework$ScalaTestRunner.run(ScalaTestFramework.scala:217)
    at org.scalatools.testing.Runner2.run(Runner2.java:16)
    at sbt.TestRunner.delegateRun(TestFramework.scala:57)
    at sbt.TestRunner.run(TestFramework.scala:51)
    at sbt.TestRunner.runTest$1(TestFramework.scala:71)
    at sbt.TestRunner.run(TestFramework.scala:80)
    at sbt.TestFramework$$anonfun$6$$anonfun$apply$8$$anonfun$7$$anonfun$apply$9.apply(TestFramework.scala:178)
    at sbt.TestFramework$$anonfun$6$$anonfun$apply$8$$anonfun$7$$anonfun$apply$9.apply(TestFramework.scala:178)
    at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:190)
    at sbt.TestFramework$$anonfun$6$$anonfun$apply$8$$anonfun$7.apply(TestFramework.scala:178)
    at sbt.TestFramework$$anonfun$6$$anonfun$apply$8$$anonfun$7.apply(TestFramework.scala:178)
    at sbt.Tests$$anonfun$makeParallel$1$$anonfun$apply$7.apply(Tests.scala:119)
    at sbt.Tests$$anonfun$makeParallel$1$$anonfun$apply$7.apply(Tests.scala:119)
    at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:47)
    at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:47)
    at sbt.std.Transform$$anon$5.work(System.scala:71)
    at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:232)
    at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:232)
    at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
    at sbt.Execute.work(Execute.scala:238)
    at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:232)
    at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:232)
    at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
    at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)

Googling for this shows that others have run into the same issue. It's probably sufficient to create a copy before iterating or so.

I am using rococo-6.1.

Thanks, Kaspar