blob: 5d0326bc75da560ac4c7c1357afa0d27740bac5d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.cache.tier.sockets;
import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.apache.geode.internal.AvailablePortHelper.getRandomAvailableTCPPort;
import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import java.util.Properties;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.DataSerializer;
import org.apache.geode.TestDataSerializer;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.MirrorType;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.client.PoolManager;
import org.apache.geode.cache.client.internal.PoolImpl;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.internal.InternalDataSerializer;
import org.apache.geode.internal.cache.CacheServerImpl;
import org.apache.geode.internal.cache.ClientServerObserverAdapter;
import org.apache.geode.internal.cache.ClientServerObserverHolder;
import org.apache.geode.internal.cache.EventID;
import org.apache.geode.test.dunit.DistributedTestUtils;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.NetworkUtils;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.junit.categories.ClientServerTest;
import org.apache.geode.test.junit.categories.SerializationTest;
@Category({ClientServerTest.class, SerializationTest.class})
public final class DataSerializerPropagationDUnitTest extends JUnit4DistributedTestCase {
private static final int instanceCountWithAllPuts = 3;
private static final int instanceCountWithOnePut = 1;
private static final String REGION_NAME = "ClientServerDataSerializersRegistrationDUnitTest";
private static Cache cache = null;
private static VM client1 = null;
private static VM client2 = null;
private VM server1 = null;
private VM server2 = null;
// The port numbers for the two servers, once they've been started.
private int PORT1 = -1;
private int PORT2 = -1;
protected static EventID eventId;
private static volatile EventID observedEventID;
private static EventID getObservedEventID() {
return observedEventID;
}
@Override
public final void postSetUp() throws Exception {
final Host host = Host.getHost(0);
client1 = host.getVM(0);
client2 = host.getVM(1);
server1 = host.getVM(2);
server2 = host.getVM(3);
PORT1 = -1;
PORT2 = -1;
}
private void createCache(Properties props) throws Exception {
DistributedSystem ds = getSystem(props);
ds.disconnect();
ds = getSystem(props);
assertNotNull(ds);
cache = CacheFactory.create(ds);
assertNotNull(cache);
}
public static void createClientCache(String host, Integer port1) throws Exception {
Properties props = new Properties();
props.setProperty(MCAST_PORT, "0");
props.setProperty(LOCATORS, "");
new DataSerializerPropagationDUnitTest().createCache(props);
Pool p = PoolManager.createFactory().addServer(host, port1.intValue()).setMinConnections(1)
.setSubscriptionEnabled(true).setPingInterval(200)
.create("ClientServerDataSerializersRegistrationDUnitTestPool");
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.DISTRIBUTED_ACK);
factory.setPoolName(p.getName());
Region r = cache.createRegion(REGION_NAME, factory.create());
r.registerInterest("ALL_KEYS");
}
private int initServerCache(VM server) {
return server.invoke(DataSerializerPropagationDUnitTest::createServerCache);
}
private void tearDownForVM() {
eventId = null;
observedEventID = null;
PoolImpl.IS_INSTANTIATOR_CALLBACK = false;
DataSerializerPropagationDUnitTest.closeCache();
}
@Override
public final void preTearDown() throws Exception {
try {
// close the clients first
client1.invoke(this::tearDownForVM);
client2.invoke(this::tearDownForVM);
tearDownForVM();
server1.invoke(this::tearDownForVM);
server2.invoke(this::tearDownForVM);
client1 = null;
client2 = null;
server1 = null;
server2 = null;
} finally {
TestDataSerializer.successfullyInstantiated = false;
DistributedTestUtils.unregisterAllDataSerializersFromAllVms();
}
}
public static void closeCache() {
if (cache != null && !cache.isClosed()) {
cache.close();
}
}
private static void verifyDataSerializers(final int numOfDataSerializers) {
await()
.untilAsserted(() -> assertEquals(
"serializers: " + Arrays.toString(InternalDataSerializer.getSerializers()),
InternalDataSerializer.getSerializers().length, numOfDataSerializers));
}
private static void registerDataSerializer(Class klass) {
InternalDataSerializer.register(klass, true);
}
private static void registerDSObjectLocalOnly() throws Exception {
InternalDataSerializer._register(new DSObjectLocalOnly(79), true);
}
public static void stopServer() {
assertEquals("Expected exactly one CacheServer", 1, cache.getCacheServers().size());
CacheServerImpl bs = (CacheServerImpl) cache.getCacheServers().iterator().next();
assertNotNull(bs);
bs.stop();
}
public static void startServer() throws Exception {
Cache c = CacheFactory.getAnyInstance();
assertEquals("Expected exactly one CacheServer", 1, c.getCacheServers().size());
CacheServerImpl bs = (CacheServerImpl) c.getCacheServers().iterator().next();
assertNotNull(bs);
bs.start();
}
/**
* Register two DataSerializers on the server and verify that they get propagated to client.
*/
@Test
public void testServerUpFirstClientLater() throws Exception {
PORT1 = initServerCache(server1);
server1.invoke(() -> registerDataSerializer(DSObject1.class));
server1.invoke(() -> registerDataSerializer(DSObject2.class));
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(2));
final String hostName = NetworkUtils.getServerHostName();
client1.invoke(() -> DataSerializerPropagationDUnitTest.createClientCache(hostName, PORT1));
client1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(2));
// Put some entries from the client
client1.invoke(() -> {
Region region = cache.getRegion(REGION_NAME);
for (int i = 1; i <= 10; i++) {
region.put(i, i);
}
});
client1.invoke(() -> {
Region region = cache.getRegion(REGION_NAME);
assertEquals(1, region.get(1));
});
server1.invoke(() -> {
Region region = cache.getRegion(REGION_NAME);
for (int i = 1; i <= 10; i++) {
assertEquals(i, region.get(i));
}
});
}
@Test
public void testDataSerializersWith2ClientsN2Servers() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
client1.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server2.getHost()), new Integer(PORT2)));
client1.invoke(() -> registerDataSerializer(DSObject1.class));
client1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
}
// this test is for bug 44112
@Test
public void testLocalOnlyDataSerializable() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
client1.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server2.getHost()), new Integer(PORT2)));
server1.invoke(() -> DataSerializerPropagationDUnitTest.registerDSObjectLocalOnly());
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(0)));
client1.invoke(() -> {
DSObjectLocalOnly.allowDefaultInstantiation = true;
try {
DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1));
} finally {
DSObjectLocalOnly.allowDefaultInstantiation = false;
}
});
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(0)));
}
@Test
public void testDataSerializersWithServerKill() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
client1.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server2.getHost()), new Integer(PORT2)));
client1.invoke(() -> registerDataSerializer(DSObject1.class));
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(1));
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(1));
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(1));
// can get server connectivity exception
final IgnoredException expectedEx =
IgnoredException.addIgnoredException("Server unreachable", client1);
// stop the cache server
server1.invoke(() -> DataSerializerPropagationDUnitTest.stopServer());
server1.invoke(() -> registerDataSerializer(DSObject2.class));
server1.invoke(() -> registerDataSerializer(DSObject3.class));
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(3)));
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(3)));
client1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithOnePut)));
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(3)));
expectedEx.remove();
}
@Test
public void testDataSerializers() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
client1.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server2.getHost()), new Integer(PORT2)));
client1.invoke(() -> registerDataSerializer(DSObject1.class));
server1.invoke(() -> registerDataSerializer(DSObject2.class));
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(2)));
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(2)));
client1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(2)));
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(2)));
}
@Test
public void testDataSerializersWithServerKillAndReInvoked() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
client1.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server2.getHost()), new Integer(PORT2)));
client1.invoke(() -> registerDataSerializer(DSObject1.class));
client1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithOnePut)));
server1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithOnePut)));
server2.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithOnePut)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithOnePut)));
// can get server connectivity exception
try (IgnoredException removedLater =
IgnoredException.addIgnoredException("Server unreachable", client1)) {
server1.invoke(() -> DataSerializerPropagationDUnitTest.stopServer());
try {
client1.invoke(() -> registerDataSerializer(DSObject2.class));
} catch (Exception e) {// we are putting in a client whose server is dead
}
try {
client1.invoke(() -> registerDataSerializer(DSObject3.class));
} catch (Exception e) {// we are putting in a client whose server is
// dead
}
server1.invoke(() -> DataSerializerPropagationDUnitTest.startServer());
client1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithAllPuts)));
server1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithAllPuts)));
server2.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithAllPuts)));
client1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializers(new Integer(instanceCountWithAllPuts)));
}
}
@Test
public void registerDataSerializerGetsPropagatedToAnotherClient() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server2.getHost()), new Integer(PORT2)));
setClientServerObserver1();
client2.invoke(DataSerializerPropagationDUnitTest::setClientServerObserver2);
registerDataSerializer(DSObject1.class);
assertNotNull(eventId);
await("event propagates to client 2").untilAsserted(() -> assertEquals(eventId,
client2.invoke(DataSerializerPropagationDUnitTest::getObservedEventID)));
}
@Test
public void testLazyLoadingOfDataSerializersWith2ClientsN2Servers() throws Exception {
PORT1 = initServerCache(server1);
PORT2 = initServerCache(server2);
client2.bounce();
client1.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.createClientCache(NetworkUtils.getServerHostName(server1.getHost()), new Integer(PORT2)));
client1.invoke(() -> registerDataSerializer(TestDataSerializer.class));
client1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
// Verify through InternalDataSerializer's map entries
client1.invoke(
() -> DataSerializerPropagationDUnitTest.verifyLoadedDataSerializers(new Integer(1)));
client1.invoke(
() -> DataSerializerPropagationDUnitTest.verifyDataSerializerClassNamesMap(new Integer(0)));
client1
.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializerIDMap(new Integer(0)));
client1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializerSupportedClassNamesMap(new Integer(0)));
server1.invoke(
() -> DataSerializerPropagationDUnitTest.verifyLoadedDataSerializers(new Integer(1)));
server1.invoke(
() -> DataSerializerPropagationDUnitTest.verifyDataSerializerClassNamesMap(new Integer(0)));
server1
.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializerIDMap(new Integer(0)));
server1.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializerSupportedClassNamesMap(new Integer(0)));
server2.invoke(
() -> DataSerializerPropagationDUnitTest.verifyLoadedDataSerializers(new Integer(1)));
server2.invoke(
() -> DataSerializerPropagationDUnitTest.verifyDataSerializerClassNamesMap(new Integer(0)));
server2
.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializerIDMap(new Integer(0)));
server2.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializerSupportedClassNamesMap(new Integer(0)));
// Verify by boolean flag in static initializer.
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyTestDataSerializerLoaded());
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyTestDataSerializerLoaded());
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyTestDataSerializerNotLoaded());
// Verify through InternalDataSerializer.getSerializers()
server1.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
server2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
// Verify through InternalDataSerializer's map entries
client2.invoke(
() -> DataSerializerPropagationDUnitTest.verifyLoadedDataSerializers(new Integer(0)));
client2.invoke(
() -> DataSerializerPropagationDUnitTest.verifyDataSerializerClassNamesMap(new Integer(1)));
client2
.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializerIDMap(new Integer(1)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializerSupportedClassNamesMap(new Integer(3)));
// Force TestDataSerializer to be loaded into vm by invoking
// InternalDataSerialier.getSerializers()
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializers(new Integer(1)));
// Verify by boolean flag in static initializer.
client2.invoke(() -> DataSerializerPropagationDUnitTest.verifyTestDataSerializerLoaded());
// Verify through InternalDataSerializer's map entries
client2.invoke(
() -> DataSerializerPropagationDUnitTest.verifyLoadedDataSerializers(new Integer(1)));
client2.invoke(
() -> DataSerializerPropagationDUnitTest.verifyDataSerializerClassNamesMap(new Integer(0)));
client2
.invoke(() -> DataSerializerPropagationDUnitTest.verifyDataSerializerIDMap(new Integer(0)));
client2.invoke(() -> DataSerializerPropagationDUnitTest
.verifyDataSerializerSupportedClassNamesMap(new Integer(0)));
}
private static void verifyTestDataSerializerNotLoaded() {
assertFalse("TestDataSerializer should not have been loaded in this vm.",
TestDataSerializer.successfullyInstantiated);
}
private static void verifyTestDataSerializerLoaded() {
assertTrue("TestDataSerializer should have been loaded in this vm.",
TestDataSerializer.successfullyInstantiated);
}
private static void verifyLoadedDataSerializers(int expected) {
int actual = InternalDataSerializer.getLoadedDataSerializers();
assertEquals(expected, actual);
}
private static void verifyDataSerializerClassNamesMap(int expected) {
int actual = InternalDataSerializer.getDsClassesToHoldersMap().size();
assertEquals(expected, actual);
}
private static void verifyDataSerializerIDMap(int expected) {
int actual = InternalDataSerializer.getIdsToHoldersMap().size();
assertEquals(expected, actual);
}
private static void verifyDataSerializerSupportedClassNamesMap(int expected) {
int actual = InternalDataSerializer.getSupportedClassesToHoldersMap().size();
assertEquals(expected, actual);
}
private static Integer createServerCache() throws Exception {
new DataSerializerPropagationDUnitTest().createCache(new Properties());
AttributesFactory factory = new AttributesFactory();
factory.setScope(Scope.DISTRIBUTED_ACK);
factory.setMirrorType(MirrorType.KEYS_VALUES);
RegionAttributes attrs = factory.create();
cache.createRegion(REGION_NAME, attrs);
int port = getRandomAvailableTCPPort();
CacheServer server1 = cache.addCacheServer();
server1.setPort(port);
server1.setMaxThreads(0);
server1.setNotifyBySubscription(true);
server1.start();
return new Integer(port);
}
private static void setClientServerObserver1() {
PoolImpl.IS_INSTANTIATOR_CALLBACK = true;
ClientServerObserverHolder.setInstance(new ClientServerObserverAdapter() {
@Override
public void beforeSendingToServer(EventID eventID) {
eventId = eventID;
}
});
}
private static void setClientServerObserver2() {
PoolImpl.IS_INSTANTIATOR_CALLBACK = true;
ClientServerObserverHolder.setInstance(new ClientServerObserverAdapter() {
@Override
public void afterReceivingFromServer(EventID eventID) {
observedEventID = eventID;
}
});
}
}
class DSObject1 extends DataSerializer {
public DSObject1() {
// Do nothing.
}
@Override
public int getId() {
return 1;
}
@Override
public Class[] getSupportedClasses() {
return new Class[] {this.getClass()};
}
@Override
public boolean toData(Object o, DataOutput out) throws IOException {
out.write(5);
return false;
}
@Override
public Object fromData(DataInput in) throws IOException, ClassNotFoundException {
readInteger(in);
return null;
}
}
class DSObject2 extends DataSerializer {
public DSObject2() {
// Do nothing.
}
@Override
public int getId() {
return 2;
}
@Override
public Class[] getSupportedClasses() {
return new Class[] {this.getClass()};
}
@Override
public boolean toData(Object o, DataOutput out) throws IOException {
out.write(15);
return false;
}
@Override
public Object fromData(DataInput in) throws IOException, ClassNotFoundException {
readInteger(in);
return null;
}
}
class DSObject3 extends DataSerializer {
public DSObject3() {
// Do nothing.
}
@Override
public int getId() {
return 3;
}
@Override
public Class[] getSupportedClasses() {
return new Class[] {this.getClass()};
}
@Override
public boolean toData(Object o, DataOutput out) throws IOException {
out.write(25);
return false;
}
@Override
public Object fromData(DataInput in) throws IOException, ClassNotFoundException {
readInteger(in);
return null;
}
}
class Bogus {
// Intentionally left blank.
}
/**
* This data serializer can be created locally but remote members who call the default constructor
* will
* fail.
*/
class DSObjectLocalOnly extends DataSerializer {
static boolean allowDefaultInstantiation = false;
DSObjectLocalOnly(int v) {
// this allows us to make one locally without setting the static.
}
// used for serialization
public DSObjectLocalOnly() {
if (!allowDefaultInstantiation) {
throw new UnsupportedOperationException("DSObjectLocalOnly can not be deserialized");
}
}
@Override
public int getId() {
return 20;
}
@Override
public Class[] getSupportedClasses() {
return new Class[] {Bogus.class};
}
@Override
public boolean toData(Object o, DataOutput out) throws IOException {
throw new RuntimeException("Did not expect toData to be called");
}
@Override
public Object fromData(DataInput in) throws IOException, ClassNotFoundException {
throw new RuntimeException("Did not expect fromData to be called");
}
}