Search code examples
javasessionwildflyehcacheshiro

Object stored in session will not match same class after redeploy of project


I have two classes Stream and Lake that extend WaterBody. The Stream or Lake are stored in the session managed by Apache Shiro 1.2.4 to be used later.

WaterBody lake = new Lake();
session.setAttribute("water", lake);`

And retrieve it by using

WaterBody water = (WaterBody) session.getAttribute("water");

On a fresh start of the Wildfly 9.0.2 server I can store the store the objects just fine to the session and retrieve them just fine. It does not matter if I am storing a Lake or Stream to the water session attribute. However after a redeploy of the project without restarting the Wildfly server I can store to the session attribute water the same class type only as before ie: can't store Lake first and redeploy then store Stream. I get this error:

 java.lang.ClassCastException: cannot assign instance of [Lfwp.fish.water.HydroUnit; to field fwp.fish.water.WaterBody.hucs of type [Lfwp.fish.water.HydroUnit; in instance of fwp.fish.water.Stream

The class WaterBody has getters and setters for a private field of type HydroUnit and since this is inheritied by both Stream and Lake the error makes little sense.

If on a fresh start the water session attribute is assigned to both the same class before and after the redeploy it will assign the value to the session variable just fine and then on retrieval it will throw this error:

java.lang.ClassCastException: fwp.fish.water.Lake cannot be cast to fwp.fish.water.WaterBody

Since Lake extends WaterBody it should not have an issue.

With Shiro we are using the ehcache 2.9 for the session cache.

It seems like the serialVersionUID is being ignored by the cache or it's generating a different one itself behind the scenes and it thinks that the classes have changed when they have not between redeployments.

The classes:

public class WaterBody implements Serializable {
    private static final long serialVersionUID = -5481729151007010163L;
    private int id;
    private HydroUnit[] hucs;

    public WaterBody() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public HydroUnit[] getHucs() {
        return hucs;
    }

    public void setHucs(HydroUnit[] hucs) {
        this.hucs = hucs;
    }
}

public class Lake extends WaterBody {
    private static final long serialVersionUID = -925557850476426686L;
    private String centroidLatitude;
    private String centroidLongitude;

    public Lake(){
        super();    
    }

    public String getCentroidLatitude() {
        return centroidLatitude;
    }

    public void setCentroidLatitude(String centroidLatitude) {
        this.centroidLatitude = centroidLatitude;
    }

    public String getCentroidLongitude() {
        return centroidLongitude;
    }

    public void setCentroidLongitude(String centroidLongitude) {
        this.centroidLongitude = centroidLongitude;
    }
}

public class Stream extends WaterBody {
    private static final long serialVersionUID = 2556033593701784053L;
    private String mouthLatitude;
    private String mouthLongitude;

    public Stream(){
         super();       
    }

    public String getMouthLatitude() {
        return mouthLatitude;
    }

    public void setMouthLatitude(String mouthLatitude) {
        this.mouthLatitude = mouthLatitude;
    }

    public String getMouthLongitude() {
        return mouthLongitude;
    }

    public void setMouthLongitude(String mouthLongitude) {
        this.mouthLongitude = mouthLongitude;
    }
}

Solution

  • After researching into the issue this is what we had found. When the project is ran the first time ClassLoader creates the classes when they are used and then stores the objects into the session. After a hot redeployment of that project while the server is running, when it comes to load a Class again the ClassLoader creates a new instance of that Class. However the session still remembers the Class from the last time an object of that Class was stored and will sometimes let the new objects get stored in the session appearing as that same object. But upon retrieving the object it is still defined by the old instance of that Class and thus when trying to cast it to the current instance of that Class it will fail.

    Our solution is for projects that have objects stored in the session is to separate the classes that will be stored as objects in the session into a separate .jar module included with the project that is being redeployed. That way if there are changes to the Class files the server will have to be restarted anyway since there is a change to a module dependency. Now if the project is hot redeployed the classes stay intact since they are not in the .war file that is being refreshed.