| /* |
| * 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; |
| } |
| |
| } |
| } |