[SOLVED] ConcurrentModificationException

Hey Guys,

I am getting a concurrentModificationException in my code. I have narrowed it down to when I add in portal Controls. I am getting the error from the phsyics space. I have tried everything to deal with it but everything seems not to work. I don’t know what to do now. I am attaching all the code as needed. Help Please guys, I need this as part of my Java Final project at school.



GravityWell.java

[java]

import com.jme3.app.SimpleApplication;

import com.jme3.asset.AssetManager;

import com.jme3.bullet.BulletAppState;

import com.jme3.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector3f;

import com.jme3.scene.Geometry;

import com.jme3.scene.Node;

import com.jme3.scene.Spatial;

import com.jme3.scene.shape.Sphere;

import com.jme3.texture.Image;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture2D;

import com.jme3.util.SkyFactory;

import com.jme3.util.TangentBinormalGenerator;

import java.util.ConcurrentModificationException;

import java.util.Random;

import mygame.Game;



/**

*

  • @author Gogol

    */

    public class GravityWell

    {

    Random rand;



    byte X;

    byte Y;

    byte Z;



    byte WellType = -1;



    public SolarSystem Parent;



    public Node Well;



    int radius = 150;



    Game App;



    long LocalSeed;



    public BulletAppState bullet;



    private Portal[] Portals = new Portal[6];



    AssetManager assetManager;



    FeatureGenerator genny;



    public GravityWell(byte x, byte y, byte z, SolarSystem Parent)

    {

    this.X = x;

    this.Y = y;

    this.Z = z;

    this.Parent = Parent;

    this.App = Parent.Parent.Parent.app;

    this.bullet = App.bulletAppState;

    this.assetManager = Parent.Parent.Parent.app.getAssetManager();

    this.genny = new FeatureGenerator(this);



    LocalSeed = Parent.SystemSeed + this.X + this.Y + this.Z;



    rand = new Random(LocalSeed);



    Well = new Node("" + LocalSeed);

    //this is the test!

    this.bullet.initialize(App.getStateManager(), App);

    generateBackGround();

    //ERROR GENERATES around the Portal Construction.

    try

    {

    //generatePortals();

    }

    catch(Exception e)

    {

    System.out.println("Error with portal Generation");

    e.printStackTrace();

    }







    if(this.X == 0 && this.Y == 0 && this.Z == 0)

    {

    this.generateStar();

    //Comment this one in to test surrounding wells so i can do planet stuff.

    try

    {

    generatePortals();

    }

    catch(Exception e)

    {

    System.out.println("Error with portal Generation");

    e.printStackTrace();

    }

    }

    else

    {

    this.generateFeatures();

    this.calcSunLighting(X,Y,Z);

    try

    {

    generatePortals();

    }

    catch(Exception e)

    {

    System.out.println("Error with portal Generation");

    e.printStackTrace();

    }

    }





    }





    public synchronized void generateFeatures()

    {

    byte temp = WellType <= 0 ? (byte) rand.nextInt(3): WellType;

    switch(temp)

    {

    case 0:

    {

    System.out.println("This is an Empty Gravity Well");

    break;

    }

    case 1:

    {

    this.attachChild(genny.getFeature(FeatureGenerator.PLANETARY_FEATURE));

    break;

    }

    case 2:

    {

    System.out.println("This is a Gravity Well with an Anomoly in it!");

    break;

    }

    }

    }



    public synchronized void generateStar()

    {

    System.out.println("This is a Gravity Well with a Star in it!");

    }



    public void generateBackGround()

    {

    AssetManager assetManager = App.getAssetManager();

    Spatial sky;

    Texture up = assetManager.loadTexture("Textures/Spacescape/PurpleNebulaComplex/purpleNebulaComplex_top3.jpg");

    Texture down = assetManager.loadTexture("Textures/Spacescape/PurpleNebulaComplex/purpleNebulaComplex_bottom4.jpg");

    Texture north = assetManager.loadTexture("Textures/Spacescape/PurpleNebulaComplex/purpleNebulaComplex_front5.jpg");

    Texture south = assetManager.loadTexture("Textures/Spacescape/PurpleNebulaComplex/purpleNebulaComplex_back6.jpg");

    Texture east = assetManager.loadTexture("Textures/Spacescape/PurpleNebulaComplex/purpleNebulaComplex_left2.jpg");

    Texture west = assetManager.loadTexture("Textures/Spacescape/PurpleNebulaComplex/purpleNebulaComplex_right1.jpg");

    sky = SkyFactory.createSky(assetManager, east, west, north, south, up, down);

    sky.setLocalTranslation(0, 0, 0);

    Well.attachChild(sky);

    }



    public void calcSunLighting(byte x, byte y, byte z)

    {

    DirectionalLight Sun = new DirectionalLight();

    Sun.setDirection(new Vector3f(x,y,z));

    Sun.setColor(ColorRGBA.Pink);

    Well.addLight(Sun);

    }



    public boolean hasNeighbor(byte x, byte y, byte z)

    {

    if(Parent.checkCoords(x, y, z))

    {

    return true;

    }

    return false;

    }



    public synchronized void generatePortals() throws Exception

    {

    System.out.println("Creating Portals!");

    System.out.println("

");
if(hasNeighbor( (byte) (this.X + 1), Y, Z))
{
Portal East = new Portal((byte) (this.X + 1), Y, Z, this);
East.Mesh.setLocalTranslation(radius, 0, 0);
Well.attachChild(East.Mesh);
Portals[0] = East;
}
if(hasNeighbor( (byte) (this.X - 1), Y, Z))
{
Portal West = new Portal((byte) (this.X - 1), Y, Z, this);
West.Mesh.setLocalTranslation(-radius, 0, 0);
Well.attachChild(West.Mesh);
Portals[1] = West;
}
if(hasNeighbor( X, (byte) (this.Y + 1), Z))
{
Portal Top = new Portal(X, (byte) (this.Y + 1), Z, this);
Top.Mesh.setLocalTranslation(0, radius, 0);
Well.attachChild(Top.Mesh);
Portals[2] = Top;
}
if(hasNeighbor( X, (byte) (this.Y - 1), Z))
{
Portal Bottom = new Portal(X, (byte) (this.Y - 1), Z, this);
Bottom.Mesh.setLocalTranslation(0, -radius, 0);
Well.attachChild(Bottom.Mesh);
Portals[3] = Bottom;
}
if(hasNeighbor( X, Y, (byte) (this.Z + 1)))
{
Portal North = new Portal( X, Y, (byte) (this.Z + 1), this);
North.Mesh.setLocalTranslation(0, 0, radius);
Well.attachChild(North.Mesh);
Portals[4] = North;
}
if(hasNeighbor( X, Y, (byte) (this.Z - 1)))
{
Portal South = new Portal( X, Y, (byte) (this.Z - 1), this);
South.Mesh.setLocalTranslation(0, 0, -radius);
Well.attachChild(South.Mesh);
Portals[5] = South;
}
}

public byte[] getCords()
{
byte[] Cords = new byte[3];
Cords[0] = X;
Cords[1] = Y;
Cords[2] = Z;
return Cords;
}

public synchronized void doWellCleanUp()
{
for(int i = 0; i < Portals.length; i++)
{
if(Portals != null)
{
Portals.destory();
}
}
bullet.getPhysicsSpace().removeAll(Well);
Well.removeFromParent();
}

public void attachChild(SpaceObject Object)
{
Object.Parent = this;
Well.attachChild(Object.Mesh);
}
}
[/java]

Portal.java
[java]

byte x, y, z;

String IDENT;
public GravityWell Parent;
Node Mesh;

public Portal(byte x, byte y, byte z, GravityWell Parent) throws Exception
{
this.x = x;
this.y = y;
this.z = z;
this.Parent = Parent;
IDENT = "Portal " + Parent.LocalSeed + " -- "+ "[" +Parent.X + "," + Parent.Y + "," + Parent.Z + "]" + "->[" + x + "," + y + "," + z + "]";
Mesh = new Node(IDENT);

Box b = new Box( 1, 1, 1); // create cube shape at the origin
Geometry geom = new Geometry("Box", b); // create cube geometry from the shape
Material mat = new Material(Parent.App.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material
mat.setColor("Color", ColorRGBA.Green); // set color of material to blue
geom.setMaterial(mat);

Mesh.attachChild(geom);

try
{
PortalControl pControl = new PortalControl(this);
Mesh.addControl(pControl);
}
catch (Exception e)
{
System.out.println("Error with Portal Control");
throw e;
}


}

public String getID()
{
return IDENT;
}

public byte[] getJumpCords()
{
byte[] Cords = new byte[3];
Cords[0] = x;
Cords[1] = y;
Cords[2] = z;
return Cords;
}

public synchronized void destory()
{
Parent.bullet.getPhysicsSpace().removeCollisionListener(Mesh.getControl(PortalControl.class));
Parent.bullet.getPhysicsSpace().removeAll(Mesh);
Mesh.removeFromParent();
System.out.println(IDENT + " was Destroied!");
}

[/java]

PortalControl.java
[java]
import actor.Ship;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.control.GhostControl;
import com.jme3.math.Vector3f;
import java.lang.Exception;
import space.Portal;

/**
*
* @author Gogol
*/
public class PortalControl extends GhostControl implements PhysicsCollisionListener
{
BulletAppState bullet;

Portal Parent;

public PortalControl(Portal portal) throws Exception
{
Parent = portal;
this.bullet = Parent.Parent.bullet;
addToPhysicsEngine();
System.out.println(Parent.getID());
}


public void collision(PhysicsCollisionEvent event)
{
System.out.println(event);
if(event.getNodeA() == null || event.getNodeB() == null)
{
System.out.println("**************************");
System.out.println(" !!NULL!! ");
System.out.println("**************************");
return;
}
String A = event.getNodeA().getName();
String B = event.getNodeB().getName();
if(A.startsWith("Portal"))
{
System.out.println(B);
Ship S = (Ship) event.getNodeB();
this.doJump(S);

}
else if(B.startsWith("Portal"))
{
System.out.println(A);
Ship S = (Ship) event.getNodeA();
this.doJump(S);
}

}

public synchronized void doJump(Ship Ship)
{
Parent.Parent.doWellCleanUp();
Ship.jump(Parent.getJumpCords());
System.out.println("JUMP!!");
}

private synchronized void addToPhysicsEngine() throws Exception
{
BoxCollisionShape B = new BoxCollisionShape(new Vector3f(.5f, .5f, .5f));
super.setCollisionShape(B);

this.bullet.getPhysicsSpace().add(this);
this.bullet.getPhysicsSpace().addCollisionListener(this);
}


}
[/java]

make it synchronize with JME update loop thread, by executing it in simpleUpdate() method (via interface or using AbstractAppState(and making your modify in update()))

What would I do to synchronize it?

For example you can enqueue it in the application loop



[java]

GameState.enqueue(new Callable<String>() {

@Override

public String call() throws Exception {

// Do your stuff here

return "somedebug that can be printed out by overriding applications enqueue method";

}

});

[/java]



This is how I do it in my GameState (the main appstate attached to the application)

This way I can debug how long each process took.

[java]

private static HashMap<Long, Future<?>> futureMap = new HashMap<Long, Future<?>>();



public static synchronized Future<?> enqueue(Callable<String> callable) {

Future<?> future = app.enqueue(callable);

futureMap.put(System.currentTimeMillis(), future);

HashMap<Long, Future<String>> temp = (HashMap<Long, Future<String>>) futureMap.clone();

Iterator<Entry<Long, Future<String>>> iter = temp.entrySet().iterator();

Entry<Long, Future<String>> entry = null;

for (; iter.hasNext(); entry = iter.next()) {

if (entry != null && entry.getValue().isDone()) {

try {

logger.log(Level.FINEST, "" + (System.currentTimeMillis() - entry.getKey()) + "ms > " + entry.getValue().get() + " | MapSize:" + futureMap.size());

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

e.printStackTrace();

}

futureMap.remove(entry.getKey());

}

}

return future;

}

[/java]

perfecticus:


This way I can debug how long each process took.

oh man, you don't use Profiler? (Profiler Netbins/SDK plugin)

i really recommend you to use it ;) it tell about all choosed classes methods executing count and time. (just need to learn how to use it)

btw: there are more "clean" ways to do synchronized methods.
@oxplay2 said:
perfecticus:


oh man, you don't use Profiler? (Profiler Netbins/SDK plugin)

i really recommend you to use it ;) it tell about all choosed classes methods executing count and time. (just need to learn how to use it)

btw: there are more "clean" ways to do synchronized methods.


At times I use profiler. But sometimes I want to find per instance future time, which is hard to tell using profiler.
Additionally I dont need all the info the profiler gives me, why I have a simpler method for debugging processing time.

But yes, profiler is useful for alot of things.

Wait, I am still confused. What was wrong with the way I did it? And would each gravity well be its own App State?



And what do I need to do? A few Steps would be nice,

Sorry guys

What you need to do is encapsulate the adding of objects in the application thread, so that it will not happen during rendering.

The easiest way of doing this is to enqueue the action in the SimpleApplication thread.



In your case, I believe you should wrap wherever you are adding the objects to the rootNode/physicsSpace.



So for example you will do something like this in somewhat pseudo code

[java]

final Object o = createObject() // Do not add it to the root node in the constructors, nor to physics space



// Then enqueue your actions, this will thread safe your actions

simpleApplication.enqueue(new Callable<Void>() {

@Override

public void run() {

rootNode.attachChild(o);

physicsSpace.addAll(o);

}

});

[/java]

Wait then, what about this Thread pool Executor, do I need to put on in the simple App? Or is the Callable enough? Also does PhysicSpace’s AddAll method also add in Listeners?

The simpleApplication already has a thread pool.



All you need to do is get your application instance and call enqueue, as seen in the code above.



No addAll will not add listeners, only spatials and controls.

Thanks That did it! It works now. You guys are awesome!