Serialization Issues

Current Mifos Session Behavior

Mifos stores objects in the session (HttpSession) provided by the servlet API. The Servlet API says that objects stored in the session are supposed to be serializable. This is so that your web server (jetty/tomcat) can save all the sessions on shutdown and re-read them on startup. Thus, restarting the web server need not require all the users to log back in again.

Java Serialization has mechanisms to write out an object with one version of your program, and read it with a new version (for example, see the serialver command and the serialVersionUID field of serializable classes). If we implemented this, it would mean that someone could upgrade Mifos and not require the users to log out and back in. However, this is extra work and complexity, and our current decision is that it isn't worth it, since the benefit seems small.

Serialization Issues Summary

While examining output from running findbugs on the Mifos codebase, it was found that a number of classes that implement the Serializable interface are not serializable because they contain non-serializable members.

The intent of implementing the Serializable interface was to support httpSession serialization. Http``Session serialization occurs to:

  • allow active sessions to be preserved during a shutdown and restart sequence
  • allow active, but idle, sessions to be swapped out to disk (to free up memory)
  • support clusters (for example having 2 servers running Mifos so that if one goes down, the other can continue without losing the active sessions).

It has been decided that support of httpSession serialization is not currently a high priority and so addressing the issue of Serialization will be deferred.

Developer Notes on Serialization

Business object classes and entity classes (xxxBO and xxxEntity classes) implement the Serializable interface (primarily by inheriting it from PersistentObject) in order to support httpSession serialization. A number of these classes have members which are not Serializable which breaks serialization for them.

Classes are added to the httpSession through the setAttribute methods of the SessionUtils class. The following classes are currently added directly using setAttribute:

AccountActionEntity
AccountBO
AccountCheckListBO
AccountNotesEntity
AccountPaymentEntity
AccountStateEntity
AccountStatusChangeHistoryEntity
ActivityEntity
BulkEntryBO
CenterBO
CheckListBO
ClientBO
CustomerBO
CustomerCheckListBO
CustomerHistoricalDataEntity
CustomerStatusEntity
CustomFieldDefinitionEntity
FeeBO
FundBO
GLCodeEntity
GroupBO
LoanBO
LoanOfferingBO
MasterDataEntity
MeetingBO
OfficeBO
PaymentTypeEntity
PersonnelBO
PrdStatusEntity
ProductCategoryBO
RecurrenceTypeEntity
RoleBO
SavingsBO
SavingsOfferingBO
SupportedLocalesEntity
WeekDaysEntity

There are many other classes that are members of the classes listed above and so they too must support Serialization.

To enforce adding only Serializable objects to the httpSession, the setAttribute method was made to require a parameter which is Serializable or is a Collection of Serializable objects. The only exception to this is the QueryResult object which is not serializble but is saved in the httpSession. A separate setAttribute method signature was created for this case (but would be removed once QueryResult were made Serializable or refactored away).

In order to support Serialization, the plan would be to add unit tests to each Serializable class to verify that it can be serialzed and then make the class and all its members (recursively) Serializable. A template for a test to verify that a class can serialize follows:

import org.mifos.framework.TestUtils;

public void testIsSerializable() throws Exception {
  // create a Serializable object with sufficient internal state to 
  // exercise Serializing all member varialbles
  SomeClass someClass = new SomeClass(...);
  TestUtils.assertCanSerialize(someClass);

It is important to note that a class must be initialized such that all its members contain data in order to completely test Serialization, so in most cases the no argument constructor is not sufficient.

Example error message

May 22, 2008 3:13:14 PM org.apache.catalina.session.StandardManager start
SEVERE: Exception loading sessions from persistent storage
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: org.mifos.framework.business.util.Address
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1333)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1947)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1871)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
        at org.apache.catalina.session.StandardSession.readObject(StandardSession.java:1439)
        at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:942)
        at org.apache.catalina.session.StandardManager.doLoad(StandardManager.java:394)
        at org.apache.catalina.session.StandardManager.load(StandardManager.java:321)
        at org.apache.catalina.session.StandardManager.start(StandardManager.java:637)
        at org.apache.catalina.core.ContainerBase.setManager(ContainerBase.java:438)
        at org.apache.catalina.core.StandardContext.start(StandardContext.java:4271)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:829)
        at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:718)
        at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:490)
        at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1147)
        at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
        at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
        at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
        at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
        at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
        at org.apache.catalina.core.StandardService.start(StandardService.java:516)
        at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)


Workaround

For the time being Serialization can be disabled for containers such as Tomcat to insure that httpSessions are not serialized.

In Tomcat 5.5.x, you can turn off httpsession serialization by setting the Manager pathname to null in the context.xml file:

<Manager pathname=""/>