blob: 4c824448f6b4446cdaaad7b56db0f059044bcc4a [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;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.Cache;
import org.apache.geode.internal.cache.DistributedPutAllOperation.EntryVersionsList;
import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
import org.apache.geode.test.junit.categories.SerializationTest;
/**
* Test the DSFID serialization framework added for rolling upgrades in 7.1
*
*
*
*/
@Category({SerializationTest.class})
public class BackwardCompatibilitySerializationDUnitTest extends JUnit4CacheTestCase {
private transient ByteArrayOutputStream baos;
private transient ByteArrayInputStream bais;
public static boolean toDataCalled = false;
public static boolean toDataPre66Called = false;
public static boolean toDataPre70called = false;
public static boolean fromDataCalled = false;
public static boolean fromDataPre66Called = false;
public static boolean fromDataPre70Called = false;
public TestMessage msg = new TestMessage();
public BackwardCompatibilitySerializationDUnitTest() {
super();
}
@Override
public final void postSetUp() {
baos = new ByteArrayOutputStream();
// register TestMessage using an existing dsfid
DSFIDFactory.registerDSFID(DataSerializableFixedID.PUTALL_VERSIONS_LIST, TestMessage.class);
}
@Override
public final void preTearDownCacheTestCase() {
resetFlags();
// reset the class mapped to the dsfid
DSFIDFactory.registerDSFID(DataSerializableFixedID.PUTALL_VERSIONS_LIST,
EntryVersionsList.class);
this.baos = null;
this.bais = null;
}
/**
* Test if correct toData/toDataPreXXX is called when changes are made to the TestMessage in 66
* and 70 and version of peer is 56
*
*/
@Test
public void testToDataFromHigherVersionToLower() throws Exception {
DataOutputStream dos =
new VersionedDataOutputStream(new DataOutputStream(baos), Version.GFE_56);
InternalDataSerializer.writeDSFID(msg, dos);
assertTrue(toDataPre66Called);
assertFalse(toDataCalled);
}
/**
* Test if correct toData/toDataXXX is called when changes are made to the TestMessage in 66 and
* 70 and version of peer is 70
*
*/
@Test
public void testToDataFromLowerVersionToHigher() throws Exception {
DataOutputStream dos =
new VersionedDataOutputStream(new DataOutputStream(baos), Version.GFE_701);
InternalDataSerializer.writeDSFID(msg, dos);
assertTrue(toDataCalled);
}
/**
* Test if correct fromData/fromDataXXX is called when changes are made to the TestMessage in 66
* and 70 and version of peer is 70
*
*/
@Test
public void testFromDataFromHigherVersionToLower() throws Exception {
InternalDataSerializer.writeDSFID(msg, new DataOutputStream(baos));
this.bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new VersionedDataInputStream(new DataInputStream(bais), Version.GFE_701);
Object o = InternalDataSerializer.basicReadObject(dis);
assertTrue(o instanceof TestMessage);
assertTrue(fromDataCalled);
}
/**
* Test if correct fromData/fromDataXXX is called when changes are made to the TestMessage in 66
* and 70 and version of peer is 56
*
*/
@Test
public void testFromDataFromLowerVersionToHigher() throws Exception {
InternalDataSerializer.writeDSFID(msg, new DataOutputStream(baos));
this.bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new VersionedDataInputStream(new DataInputStream(bais), Version.GFE_56);
Object o = InternalDataSerializer.basicReadObject(dis);
assertTrue(o instanceof TestMessage);
assertTrue(fromDataPre66Called);
}
/**
* Test if all messages implement toDataPreXXX and fromDataPreXXX if the message has been upgraded
* in any of the versions
*
*/
@Test
public void testAllMessages() throws Exception {
// list of msgs not created using reflection
// taken from DSFIDFactory.create()
ArrayList<Integer> constdsfids = new ArrayList<Integer>();
constdsfids.add(new Byte(DataSerializableFixedID.REGION).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.END_OF_STREAM_TOKEN).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.DLOCK_REMOTE_TOKEN).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.TRANSACTION_ID).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.INTEREST_RESULT_POLICY).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.UNDEFINED).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.RESULTS_BAG).intValue());
constdsfids.add(new Byte(DataSerializableFixedID.GATEWAY_EVENT_IMPL_66).intValue());
constdsfids.add(new Short(DataSerializableFixedID.TOKEN_INVALID).intValue());
constdsfids.add(new Short(DataSerializableFixedID.TOKEN_LOCAL_INVALID).intValue());
constdsfids.add(new Short(DataSerializableFixedID.TOKEN_DESTROYED).intValue());
constdsfids.add(new Short(DataSerializableFixedID.TOKEN_REMOVED).intValue());
constdsfids.add(new Short(DataSerializableFixedID.TOKEN_REMOVED2).intValue());
constdsfids.add(new Short(DataSerializableFixedID.TOKEN_TOMBSTONE).intValue());
for (int i = 0; i < 256; i++) {
Constructor<?> cons = DSFIDFactory.getDsfidmap()[i];
if (!constdsfids.contains(i - Byte.MAX_VALUE - 1) && cons != null) {
Object ds = cons.newInstance((Object[]) null);
checkSupportForRollingUpgrade(ds);
}
}
// some msgs require distributed system
Cache c = getCache();
for (Object o : DSFIDFactory.getDsfidmap2().values()) {
Constructor<?> cons = (Constructor<?>) o;
if (cons != null) {
DataSerializableFixedID ds = (DataSerializableFixedID) cons.newInstance((Object[]) null);
checkSupportForRollingUpgrade(ds);
}
}
c.close();
}
private void checkSupportForRollingUpgrade(Object ds) {
Version[] versions = null;
if (ds instanceof SerializationVersions) {
versions = ((SerializationVersions) ds).getSerializationVersions();
}
if (versions != null && versions.length > 0) {
for (int i = 0; i < versions.length; i++) {
try {
ds.getClass().getMethod("toDataPre_" + versions[i].getMethodSuffix(),
new Class[] {DataOutput.class});
ds.getClass().getMethod("fromDataPre_" + versions[i].getMethodSuffix(),
new Class[] {DataInput.class});
} catch (NoSuchMethodException e) {
fail("toDataPreXXX or fromDataPreXXX for previous versions not found " + e.getMessage());
}
}
} else {
for (Method method : ds.getClass().getMethods()) {
if (method.getName().startsWith("toDataPre")) {
fail(
"Found backwards compatible toData, but class does not implement getSerializationVersions()"
+ method);
} else if (method.getName().startsWith("fromDataPre")) {
fail(
"Found backwards compatible fromData, but class does not implement getSerializationVersions()"
+ method);
}
}
}
}
private void resetFlags() {
toDataCalled = false;
toDataPre66Called = false;
toDataPre70called = false;
fromDataCalled = false;
fromDataPre66Called = false;
fromDataPre70Called = false;
}
public static class TestMessage implements DataSerializableFixedID {
/** The versions in which this message was modified */
private static final Version[] dsfidVersions = new Version[] {Version.GFE_66, Version.GFE_70};
public TestMessage() {}
@Override
public Version[] getSerializationVersions() {
return dsfidVersions;
}
@Override
public void toData(DataOutput out) throws IOException {
toDataCalled = true;
}
public void toDataPre_GFE_6_6_0_0(DataOutput out) throws IOException {
toDataPre66Called = true;
}
public void toDataPre_GFE_7_0_0_0(DataOutput out) throws IOException {
toDataPre70called = true;
}
@Override
public void fromData(DataInput in) throws IOException {
fromDataCalled = true;
}
public void fromDataPre_GFE_6_6_0_0(DataInput out) throws IOException {
fromDataPre66Called = true;
}
public void fromDataPre_GFE_7_0_0_0(DataInput out) throws IOException {
fromDataPre70Called = true;
}
@Override
public int getDSFID() {
return DataSerializableFixedID.PUTALL_VERSIONS_LIST;
}
}
}