All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.org.jgroups.protocols.JGroupsVersioningJUnitTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */

package com.gemstone.org.jgroups.protocols;

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.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import junit.framework.TestCase;

import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.VersionedDataInputStream;
import com.gemstone.gemfire.internal.VersionedDataOutputStream;
import com.gemstone.gemfire.internal.VersionedObjectInput;
import com.gemstone.gemfire.internal.VersionedObjectOutput;
import com.gemstone.gemfire.internal.shared.Version;
import com.gemstone.org.jgroups.Address;
import com.gemstone.org.jgroups.Event;
import com.gemstone.org.jgroups.Header;
import com.gemstone.org.jgroups.Message;
import com.gemstone.org.jgroups.View;
import com.gemstone.org.jgroups.stack.IpAddress;
import com.gemstone.org.jgroups.stack.Protocol;

public class JGroupsVersioningJUnitTest extends TestCase {

  private static final String HEADER_NAME = "vHeader";

  /* 
   * For the tests in this class we create a functioning InternalDistributedSystem
   * with tcp disabled and test with its jgroups channel.
   */
  @Override
  public void setUp() throws IOException {
  }

  @Override
  public void tearDown() {
  }
  
  /** basic marshaling test for a Message */
  public void testMessageVersioning() throws Exception {
    Version v = Version.fromOrdinal((short)(Version.CURRENT.ordinal()-1), false);
    VersionedIpAddress src = new VersionedIpAddress("localhost", 12345, v);
    Message m = new Message(true);
    m.setSrc(src);
    m.setVersion(v.ordinal()); // set the serialization version
    VersionedHeader header = new VersionedHeader();
    m.putHeader(HEADER_NAME, header);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream out = new DataOutputStream(bos);
    m.writeTo(out);
    Assert.assertTrue(header.serializedVersion == v, "expected header to serialize with older version");
    Assert.assertTrue(src.serializedVersion == v, "expected src address to serialize with older version");
    out.close();
    byte[] result = bos.toByteArray();
    
    ByteArrayInputStream bis = new ByteArrayInputStream(result);
    DataInputStream in = new DataInputStream(bis);
    m = new Message(true);
    m.setVersion(v.ordinal());
    m.readFrom(in);
    header = (VersionedHeader)m.getHeader(HEADER_NAME);
    Assert.assertTrue(header != null, "expected to find a VersionedHeader");
    Assert.assertTrue(header.serializedVersion == v, "expected header to be deserialized with an older version");
    // Deserialization of Messages create IpAddress instances directly, so we can't expect the
    // message to contain a VersionedIpAddress.
  }
  
  /**
   * Test that the transport protocol handles versioning properly
   * @throws Exception
   */
  public void testUDP() throws Exception {
    System.out.println("starting testUDP");
    TestUDP udp = new TestUDP();

    Version v = Version.fromOrdinal((short)(Version.CURRENT.ordinal()-1), false);
    VersionedIpAddress src = new VersionedIpAddress("localhost", 12345, v);
    VersionedIpAddress dest = new VersionedIpAddress("localhost", 12347, v);
    udp.down(new Event(Event.SET_LOCAL_ADDRESS, src));
    
    Vector mbrs = new Vector();
    mbrs.add(src); mbrs.add(dest);
    View view = new View(src, 1, mbrs);
    udp.down(new Event(Event.VIEW_CHANGE, view));

    // send a multicast message first
    Message m = new Message(true);
    m.setSrc(src);
    VersionedHeader header = new VersionedHeader();
    m.putHeader(HEADER_NAME, header);
    udp.down(new Event(Event.MSG, m));
    Assert.assertTrue(header.serializedVersion == v, "expected header to serialize with older version");
    VersionedIpAddress msgSrc = (VersionedIpAddress)m.getSrc();
    Assert.assertTrue(msgSrc.serializedVersion == v, "expected src address to serialize with older version: " + msgSrc);

    // simulate receiving the message
    List buffers = udp.collectedBuffers;
    Assert.assertTrue(buffers.size() == 1); // should have serialized one message and sent it out
    Buffer buffer = buffers.get(0);
    udp.receive(src, dest, buffer.data, buffer.offset, buffer.length);
    Message msg = udp.lastReceivedMessage;
    Assert.assertTrue(msg.getVersion() == v.ordinal());
    header = (VersionedHeader)msg.getHeader(HEADER_NAME);
    Assert.assertTrue(header != null); // header should have been included
    Assert.assertTrue(header.serializedVersion == v);

    // now do the same with a point-to-point message
    udp.collectedBuffers.clear();
    udp.lastReceivedMessage = null;
    
    m = new Message(true);
    m.setSrc(src);
    m.setDest(dest);
    header = new VersionedHeader();
    m.putHeader(HEADER_NAME, header);
    udp.down(new Event(Event.MSG, m));
    Assert.assertTrue(header.serializedVersion == v, "expected header to serialize with older version");
    msgSrc = (VersionedIpAddress)m.getSrc();
    Assert.assertTrue(msgSrc.serializedVersion == v, "expected src address to serialize with older version: " + msgSrc);

    // simulate receiving the message
    buffers = udp.collectedBuffers;
    Assert.assertTrue(buffers.size() == 1); // should have serialized one message and sent it out
    buffer = buffers.get(0);
    udp.receive(src, dest, buffer.data, buffer.offset, buffer.length);
    msg = udp.lastReceivedMessage;
    Assert.assertTrue(msg.getVersion() == v.ordinal());
    header = (VersionedHeader)msg.getHeader(HEADER_NAME);
    Assert.assertTrue(header != null); // header should have been included
    Assert.assertTrue(header.serializedVersion == v);
  }
  
  
  public static class Buffer {
    Address dest;
    byte[] data;
    int offset;
    int length;
    
    public Buffer(byte[] data, int offset, int length) {
      this.data = data;
      this.offset = offset;
      this.length = length;
    }
    public Buffer(Address dest, byte[] data, int offset, int length) {
      this.data = data;
      this.offset = offset;
      this.length = length;
      this.dest = dest;
    }
  }
  
  
  /**
   * TestUDP extends UDP and overrides some methods for unit testing
   */
  public static class TestUDP extends UDP {
    List collectedBuffers = new LinkedList();
    Message lastReceivedMessage;
    
    @Override
    public void sendToAllMembers(byte[] data, int offset, int length)
        throws Exception {
      collectedBuffers.add(new Buffer(data, offset, length));
    }

    @Override
    protected Message bufferToMessage(DataInputStream instream, Address dest,
        Address sender, boolean multicast) throws Exception {
      this.lastReceivedMessage = super.bufferToMessage(instream, dest, sender, multicast);
      return this.lastReceivedMessage;
    }

    @Override
    public void sendToSingleMember(Address dest, boolean isJoinResponse,
        byte[] data, int offset, int length) throws Exception {
      collectedBuffers.add(new Buffer(dest, data, offset, length));
    }
    
  }
  

  /**
   * CollectingProtocol gathers the messages it receives in up() and down()
   * for later inspection.
   */
  public static class CollectingProtocol extends Protocol {
    List collectedMessages = new LinkedList();
    
    @Override
    public String getName() {
      return "CollectingProtocol";
    }
    
    @Override
    public void down(Event ev) {
      if (ev.getType() == Event.MSG) {
        collectedMessages.add((Message)ev.getArg());
      }
    }
    
    @Override
    public void up(Event ev) {
      if (ev.getType() == Event.MSG) {
        collectedMessages.add((Message)ev.getArg());
      }
    }
  }
  
  /**
   * VersionedIpAddress is an IpAddress that tracks what version was
   * used for serialization/deserialization
   */
  public static class VersionedIpAddress extends IpAddress {
    Version serializedVersion;
    
    public VersionedIpAddress(String host, int port, Version v) {
      super(host, port);
      setVersionOrdinal(v.ordinal());
    }
    
    @Override
    public String getName() {
      return "";
    }
    
    @Override
    public String toString() {
      String sup = super.toString();
      if (this.serializedVersion != null) {
        sup += " serialized with " + serializedVersion;
      }
      return sup + "@" + Integer.toHexString(System.identityHashCode(this));
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
      throw new UnsupportedOperationException();
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
        ClassNotFoundException {
      throw new UnsupportedOperationException();
    }

    @Override
    public void toData(DataOutput out) throws IOException {
//      System.out.println("serializing " + this + " with " + out);
//      Thread.dumpStack();
      if (out instanceof VersionedDataOutputStream) {
        this.serializedVersion = ((VersionedDataOutputStream)out).getVersion();
      } else {
        this.serializedVersion = Version.CURRENT;
      }
      super.toData(out);
    }

    @Override
    public void fromData(DataInput in) throws IOException {
      if (in instanceof VersionedDataInputStream) {
        this.serializedVersion = ((VersionedDataInputStream)in).getVersion();
      } else {
        this.serializedVersion = Version.CURRENT;
      }
      super.fromData(in);
    }

    
  }
  
  
  /**
   * VersionedHeader is a JGroups message header that keeps track of
   * what version was used to serialize/deserialize it. 
   */
  public static class VersionedHeader extends Header {
    Version serializedVersion;
    
    public VersionedHeader() {
      super();
    }
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
//      System.out.println("VersionedHeader.writeExternal invoked with " + out);
      if (out instanceof VersionedObjectOutput) {
        this.serializedVersion = ((VersionedObjectOutput)out).getVersion();
      } else {
        this.serializedVersion = Version.CURRENT;
      }
      out.writeBoolean(true);
      out.writeUTF("a header written with " + this.serializedVersion);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
        ClassNotFoundException {
      System.out.println("VersionedHeader.readExternal invoked with " + in);
      if (in instanceof VersionedObjectInput) {
        this.serializedVersion = ((VersionedObjectInput)in).getVersion();
      } else {
        this.serializedVersion = Version.CURRENT;
      }
      in.readBoolean();
      in.readUTF();
    }
    
    @Override
    public String toString() {
      return "VersionedHeader(v="+this.serializedVersion+")";
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy