javarmi

RMI Test Passes in Isolation but Fails with UnmarshalException When Run with Other Tests


I am trying to understand why a specific test method that utilizes RMI passes when run in isolation but fails when run along with other tests that also use RMI. The test structure is the same for both cases.

...

  @BeforeClass
  public static void beforeTest() throws Exception {
    try {
      System.setProperty(Constants.JAVA_RMI_TEST_PORT_ENVIRONMENT, TestEnvironment.getPort());
      dataFactory.register();
      await().atMost(5, TimeUnit.SECONDS);
      client = DataFactoryClient.getInstance(TestEnvironment.getDomain(), TestEnvironment.getPort());
    } catch (RemoteException e) {
      throw new RuntimeException(e);
    } catch (NotBoundException e) {
      throw new RuntimeException(e);
    }
  }

  @AfterClass
  public static void afterAll() {
    dataFactory.release();
  }


  @Test
  public void testSimpleSave() throws Exception {
    EntityStore entityStore = client.getEntityStore();
    Assert.assertNotNull(entityStore);

    DataFactoryEntity entity = new DataFactoryEntityBuilder()
            .environment(TestEnvironment.getEnvironment())
            .entityType("Foo")
            .putPropertyMap("foo", "bar")
            .build();

    DataFactoryEntity dataFactoryEntity = entityStore.saveEntity(entity).get();
    Assert.assertNotNull(dataFactoryEntity);
    Assert.assertNotNull(dataFactoryEntity.entityId());
    Assert.assertEquals("bar", dataFactoryEntity.propertyMap().get("foo"));
  }
public class EntityStoreClientImpl implements EntityStore {
  @Override public Option<DataFactoryEntity> saveEntity(@NotNull DataFactoryEntity entity)
      throws DataFactoryException, NotBoundException, RemoteException {
    return getEntityStore().saveEntity(entity);
  }
}

The test method testSimpleSave fails if not run in isolation. When run alongside the other tests it throws:

java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
    java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: ....datafactory.database.impl.DatabaseManagerImpl

    at sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:130)
    at ....datafactory.repositories.impl.EntityStoreClientImpl.getEntityStore(EntityStoreClientImpl.java:108)
    at ....datafactory.repositories.impl.EntityStoreClientImpl.saveEntity(EntityStoreClientImpl.java:56)
    at ....datafactory.DataFactoryClientTest.testSimpleSave(DataFactoryClientTest.java:75

It is not supposed to serialize DatabaseManagerImpl (which is not meant to be anyway) like the other unit test. What's puzzling is why this particular test is attempting to serialize it.

The whole test code can be checked here for verbosity.

I could use some help and would greatly appreciate it!

Update:

Here is a quick reference to a similar test that can pass:

  @Test
  public void testNearbyConditionWithGeoHash() throws Exception {
    String environment = TestEnvironment.getEnvironment();
    EntityStore entityStore = DataFactoryClient.getInstance(TestEnvironment.getDomain(), TestEnvironment.getPort()).getEntityStore();

    DataFactoryEntity firstLocation = new DataFactoryEntityBuilder()
        .environment(environment)
        .entityType("Room")
        .putPropertyMap("address", "Room 123, 456 Street, 789 Avenue")
        .putPropertyMap("geoLocation", new GeoHash(120.976171, 14.580919).toString())
        .build();
    firstLocation = entityStore.saveEntity(firstLocation).get();
    assertNotNull(firstLocation.entityId());
...

Solution

  • I could be wrong here as it has been a while since written RMI server, and it is hard to see what you code does, so will delete if this doesn't help.

    RMI depends on serialisation, so any non-transient member fields are candidates for transfer between client and server. I see that EntityStoreImpl contains non-transient reference to DatabaseManager. When binding to registry with dataFactory.register(), you use the implementation class:

      entityStore = new EntityStoreImpl(DatabaseManagerImpl.getInstance(), LuceneIndexerImpl.getInstance());
      registry.rebind(EntityStore.class.getName(), entityStore);
    

    Normally I'd expect to see a generated stub so the implementation class is not referenced directly, something like this:

      EntityStore stub = (EntityStore )UnicastRemoteObject.exportObject(entityStore, 0);
      registry.rebind(EntityStore.class.getName(), stub);