[infinispan-commits] Infinispan SVN: r1617 - in trunk: client and 14 other directories.

infinispan-commits at lists.jboss.org infinispan-commits at lists.jboss.org
Wed Mar 24 07:03:03 EDT 2010


Author: mircea.markus
Date: 2010-03-24 07:03:01 -0400 (Wed, 24 Mar 2010)
New Revision: 1617

Added:
   trunk/client/
   trunk/client/hotrod-client/
   trunk/client/hotrod-client/pom.xml
   trunk/client/hotrod-client/src/
   trunk/client/hotrod-client/src/main/
   trunk/client/hotrod-client/src/main/java/
   trunk/client/hotrod-client/src/main/java/hotrod/
   trunk/client/hotrod-client/src/main/java/hotrod/ClientDisconnectedException.java
   trunk/client/hotrod-client/src/main/java/hotrod/ClusterTopologyListener.java
   trunk/client/hotrod-client/src/main/java/hotrod/Flag.java
   trunk/client/hotrod-client/src/main/java/hotrod/HotRodClientException.java
   trunk/client/hotrod-client/src/main/java/hotrod/HotRodException.java
   trunk/client/hotrod-client/src/main/java/hotrod/RemoteCache.java
   trunk/client/hotrod-client/src/main/java/hotrod/RemoteCacheManager.java
   trunk/client/hotrod-client/src/main/java/hotrod/TimeoutException.java
   trunk/client/hotrod-client/src/main/java/hotrod/VersionedEntry.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/
   trunk/client/hotrod-client/src/main/java/hotrod/impl/HotrodConstants.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/InvalidResponseException.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheImpl.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheSpi.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/Transport.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/VersionedEntry.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/
   trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TcpTransport.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportException.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportFactory.java
   trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/VHelper.java
   trunk/client/hotrod-client/src/main/resources/
   trunk/client/hotrod-client/src/main/resources/hotrod-client.properties
   trunk/client/hotrod-client/src/test/
   trunk/client/hotrod-client/src/test/java/
   trunk/client/hotrod-client/src/test/java/org/
   trunk/client/hotrod-client/src/test/java/org/infinispan/
   trunk/client/hotrod-client/src/test/java/org/infinispan/client/
   trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/
   trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodClientIntegrationTest.java
   trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodListenerTest.java
   trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/VHelperTest.java
Modified:
   trunk/pom.xml
Log:
added first hot rod client stub

Added: trunk/client/hotrod-client/pom.xml
===================================================================
--- trunk/client/hotrod-client/pom.xml	                        (rev 0)
+++ trunk/client/hotrod-client/pom.xml	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+   <modelVersion>4.0.0</modelVersion>
+   <parent>
+      <groupId>org.infinispan</groupId>
+      <artifactId>infinispan-parent</artifactId>
+      <version>4.1.0-SNAPSHOT</version>
+      <relativePath>../../parent/pom.xml</relativePath>
+   </parent>
+
+   <artifactId>infinispan-client-hotrod</artifactId>
+   <name>Infinispan Client Hotrod Module</name>
+   <description>Infinispan client hotrod module</description>
+   <packaging>jar</packaging>
+
+   <dependencies>
+      <dependency>
+         <groupId>${project.groupId}</groupId>
+         <artifactId>infinispan-core</artifactId>
+         <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+         <groupId>${project.groupId}</groupId>
+         <artifactId>infinispan-core</artifactId>
+         <version>${project.version}</version>
+         <type>test-jar</type>
+         <scope>test</scope>
+      </dependency>
+   </dependencies>
+</project>

Added: trunk/client/hotrod-client/src/main/java/hotrod/ClientDisconnectedException.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/ClientDisconnectedException.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/ClientDisconnectedException.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,13 @@
+package hotrod;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class ClientDisconnectedException extends HotRodException {
+   public ClientDisconnectedException() {
+      super("Cannot call a method on the remote cache after RemoteCacheFactory.putForExternalRead has been called.");
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/ClusterTopologyListener.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/ClusterTopologyListener.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/ClusterTopologyListener.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,22 @@
+package hotrod;
+
+import java.util.List;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public interface ClusterTopologyListener {
+
+   //todo consider using inet address
+   public class Address {
+      private String host;
+      private int port;
+   }
+
+   public void nodeAdded(List<Address> currentTopology, Address addedNode);
+
+   public void  nodeRemoved(List<Address> currentTopology, Address removedNode);
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/Flag.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/Flag.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/Flag.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,31 @@
+package hotrod;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public enum Flag {
+   ZERO_LOCK_ACQUISITION_TIMEOUT(0x0001),
+   CACHE_MODE_LOCAL(0x0002),
+   SKIP_LOCKING(0x0004),
+   FORCE_WRITE_LOCK(0x0008),
+   SKIP_CACHE_STATUS_CHECK(0x0010),
+   FORCE_ASYNCHRONOUS(0x0020),
+   FORCE_SYNCHRONOUS(0x0040),
+   SKIP_CACHE_STORE(0x0100),
+   FAIL_SILENTLY(0x0200),
+   SKIP_REMOTE_LOOKUP(0x0400),
+   PUT_FOR_EXTERNAL_READ(0x0800);
+
+   private int flagInt;
+
+   Flag(int flagInt) {
+      this.flagInt = flagInt;
+   }
+
+   public int getFlagInt() {
+      return flagInt;
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/HotRodClientException.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/HotRodClientException.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/HotRodClientException.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,35 @@
+package hotrod;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class HotRodClientException extends HotRodException {
+   private long messageId;
+   private int errorStatusCode;
+
+   public HotRodClientException(String message, Throwable cause) {
+      super(message, cause);
+   }
+
+   public HotRodClientException(long messageId, int errorStatusCode) {
+      this.messageId = messageId;
+      this.errorStatusCode = errorStatusCode;
+   }
+
+
+   @Override
+   public String toString() {
+      return "HotRodServerException{" +
+            "messageId=" + messageId +
+            ", errorStatusCode=" + errorStatusCode +
+            "} " + super.toString();
+   }
+
+   @Override
+   public String getMessage() {
+      return toString() + "." + super.getMessage();    
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/HotRodException.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/HotRodException.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/HotRodException.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,24 @@
+package hotrod;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class HotRodException extends RuntimeException {
+   public HotRodException() {
+   }
+
+   public HotRodException(String message) {
+      super(message);
+   }
+
+   public HotRodException(String message, Throwable cause) {
+      super(message, cause);
+   }
+
+   public HotRodException(Throwable cause) {
+      super(cause);
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/RemoteCache.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/RemoteCache.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/RemoteCache.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,314 @@
+package hotrod;
+
+import org.infinispan.AdvancedCache;
+import org.infinispan.Cache;
+import org.infinispan.config.Configuration;
+import org.infinispan.util.concurrent.NotifyingFuture;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides remote reference to a Hot Rod server/cluster. It implements {@link org.infinispan.Cache}, but given its
+ * nature (remote) some operations are not supported. All these unsupported operations are being overridden within this
+ * interface and documented as such.
+ * <p/>
+ * <b>New operations</b>: besides the operations inherited from {@link org.infinispan.Cache}, RemoteCache also adds new
+ * operations to optimize/reduce network traffic: e.g. versioned put operation.
+ * <p/>
+ * <b>Concurrency</b>: implementors of this interface will support multi-threaded access, similar to the way {@link
+ * org.infinispan.Cache} supports it.
+ * <p/>
+ * <b>Return values</b>: previous existing values for certain {@link java.util.Map} operations are not being returned,
+ * but null will always be returned instead. E.g. {@link java.util.Map#put(Object, Object)} returns the previous value
+ * associated to the supplied key. In case of RemoteCache, this will always return null.
+ * <p/>
+ * <b>Synthetic operations</b>: Certain aggregate operations are being implemented based on other Hot Rod operations.
+ * E.g. all the {@link java.util.Map#putAll(java.util.Map)} is implemented through multiple individual puts. This means
+ * that the these operations are not atomic and that they are costly, e.g. as the number of network round-trips is not
+ * one, but the size of the added map. All these synthetic operations are documented as such and should be used
+ *
+ * @author Mircea.Markus at jboss.com
+ * @since 4.1
+ */
+public interface RemoteCache<K, V> extends Cache<K, V> {
+
+
+   /**
+    * Removes the given entry only if its version matches the supplied version. A typical use case looks like this:
+    * <pre>
+    * VersionedEntry ve = remoteCache.getVersionedEntry(key);
+    * //some processing
+    * remoteCache.remove(key, ve.getVersion();
+    * </pre>
+    * Lat call (remove) will make sure that the entry will only be removed if it hasn't been changed in between.
+    *
+    * @return true if the entry has been removed
+    * @see VersionedEntry
+    * @see #getVersionedEntry(Object)
+    */
+   boolean remove(Object key, long version);
+
+   /**
+    * @see #remove(Object, Object)
+    */
+   NotifyingFuture<Boolean> removeAsync(Object key, long version);
+
+   /**
+    * Removes the given value only if its version matches the supplied version. See {@link #remove(Object, long)} for a
+    * sample usage.
+    *
+    * @return true if the method has been replaced
+    * @see #getVersionedEntry(Object)
+    * @see VersionedEntry
+    */
+   boolean replace(K key, V newValue, long version);
+
+   /**
+    * @see #replace(Object, Object, long)
+    */
+   boolean replace(K key, V newValue, long version, long lifespan, TimeUnit unit);
+
+   /**
+    * @see #replace(Object, Object, long)
+    */
+   boolean replace(K key, V newValue, long version, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit);
+
+   /**
+    * @see #replace(Object, Object, long)
+    */
+   NotifyingFuture<Boolean> replaceAsync(K key, V newValue, long version);
+
+   /**
+    * @see #replace(Object, Object, long)
+    */
+   NotifyingFuture<Boolean> replaceAsync(K key, V newValue, long version, long lifespan, TimeUnit unit);
+
+   /**
+    * @see #replace(Object, Object, long)
+    */
+   NotifyingFuture<Boolean> replaceAsync(K key, V newValue, long version, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit);
+
+
+   /**
+    * Returns the {@link VersionedEntry} associated to the supplied key param, or null if it doesn't exist.
+    */
+   VersionedEntry getVersionedEntry(K key);
+
+
+   /**
+    * Operation might be supported for smart clients that will be able to register for topology changes.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   void addListener(Object listener);
+
+   /**
+    * @throws UnsupportedOperationException
+    * @see #addListener(Object)
+    */
+   @Override
+   void removeListener(Object listener);
+
+   /**
+    * @throws UnsupportedOperationException
+    * @see #addListener(Object)
+    */
+   @Override
+   Set<Object> getListeners();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   int size();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean isEmpty();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean containsValue(Object value);
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   Set<K> keySet();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   Collection<V> values();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   Set<Entry<K, V>> entrySet();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   void evict(K key);
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   Configuration getConfiguration();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean startBatch();
+
+   /**
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   void endBatch(boolean successful);
+
+   /**
+    * This operation is not supported. Consider using {@link #remove(Object, long)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean remove(Object key, Object value);
+
+   /**
+    * This operation is not supported. Consider using {@link #removeAsync(Object, long)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   NotifyingFuture<Boolean> removeAsync(Object key, Object value);
+
+   /**
+    * This operation is not supported. Consider using {@link #replace(Object, Object, long)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean replace(K key, V oldValue, V newValue);
+
+   /**
+    * This operation is not supported. Consider using {@link #replace(Object, Object, long, long, TimeUnit)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit unit);
+
+   /**
+    * This operation is not supported. Consider using {@link #replace(Object, Object, long, long, TimeUnit, long,
+    * TimeUnit)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit);
+
+   /**
+    * This operation is not supported. Consider using {@link #replaceAsync(Object, Object, long)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   NotifyingFuture<Boolean> replaceAsync(K key, V oldValue, V newValue);
+
+   /**
+    * This operation is not supported. Consider using {@link #replaceAsync(Object, Object, long, long, TimeUnit)}
+    * instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   NotifyingFuture<Boolean> replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit unit);
+
+   /**
+    * This operation is not supported. Consider using {@link #replaceAsync(Object, Object, long, long, TimeUnit, long,
+    * TimeUnit)} instead.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   NotifyingFuture<Boolean> replaceAsync(K key, V oldValue, V newValue, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit);
+
+   /**
+    * This operation is not supported.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   AdvancedCache<K, V> getAdvancedCache();
+
+   /**
+    * This operation is not supported.
+    *
+    * @throws UnsupportedOperationException
+    */
+   @Override
+   void compact();
+
+
+   /**
+    * Synthetic operation. The client iterates over the set of keys and calls put for each one of them. This results in
+    * operation not being atomic (if a failure happens after few puts it is not rolled back) and costly (for each key in
+    * the parameter map a remote call is performed).
+    */
+   @Override
+   void putAll(Map<? extends K, ? extends V> map, long lifespan, TimeUnit unit);
+
+   /**
+    * Synthetic operation.
+    *
+    * @see #putAll(java.util.Map, long, java.util.concurrent.TimeUnit)
+    */
+   @Override
+   void putAll(Map<? extends K, ? extends V> map, long lifespan, TimeUnit lifespanUnit, long maxIdleTime, TimeUnit maxIdleTimeUnit);
+
+   /**
+    * Synthetic operation.
+    *
+    * @see #putAll(java.util.Map, long, java.util.concurrent.TimeUnit)
+    */
+   @Override
+   NotifyingFuture<Void> putAllAsync(Map<? extends K, ? extends V> data);
+
+   /**
+    * Synthetic operation.
+    *
+    * @see #putAll(java.util.Map, long, java.util.concurrent.TimeUnit)
+    */
+   @Override
+   NotifyingFuture<Void> putAllAsync(Map<? extends K, ? extends V> data, long lifespan, TimeUnit unit);
+
+   /**
+    * Synthetic operation.
+    *
+    * @see #putAll(java.util.Map, long, java.util.concurrent.TimeUnit)
+    */
+   @Override
+   NotifyingFuture<Void> putAllAsync(Map<? extends K, ? extends V> data, long lifespan, TimeUnit lifespanUnit, long maxIdle, TimeUnit maxIdleUnit);
+
+   /**
+    * Synthetic operation.
+    *
+    * @see #putAll(java.util.Map, long, java.util.concurrent.TimeUnit)
+    */
+   @Override
+   void putAll(Map<? extends K, ? extends V> m);
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/RemoteCacheManager.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/RemoteCacheManager.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/RemoteCacheManager.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,79 @@
+package hotrod;
+
+import hotrod.impl.RemoteCacheSpi;
+import org.infinispan.lifecycle.Lifecycle;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class RemoteCacheManager implements Lifecycle {
+
+   private Properties props;
+
+   /**
+    * Build a cache manager based on supplied given properties.
+    * TODO - add a list of all possible configuration parameters here
+    */
+   public RemoteCacheManager(Properties props) {
+      this.props = props;
+   }
+
+   /**
+    * Same as {@link #RemoteCacheManager(java.util.Properties)}, but it will try to lookup the config properties in the
+    * classpath, in a file named <tt>hotrod-client.properties</tt>.
+    * @throws HotRodClientException if such a file cannot be found in the classpath
+    */
+   public RemoteCacheManager() {
+      ClassLoader loader = Thread.currentThread().getContextClassLoader();
+      InputStream stream = loader.getResourceAsStream("hotrod-client.properties");
+      loadFromStream(stream);
+   }
+
+   /**
+    * Same as {@link #RemoteCacheManager(java.util.Properties)}, but it will try to lookup the config properties in
+    * supplied URL.
+    * @throws HotRodClientException if properties could not be loaded
+    */
+   public RemoteCacheManager(URL config) {
+      try {
+         loadFromStream(config.openStream());
+      } catch (IOException e) {
+         throw new HotRodClientException("Could not read URL:" + config, e);
+      }
+   }
+
+   private void loadFromStream(InputStream stream) {
+      props = new Properties();
+      try {
+         props.load(stream);
+      } catch (IOException e) {
+         throw new HotRodClientException("Issues configuring from client hotrod-client.properties",e);
+      }
+   }
+
+   public RemoteCacheSpi getRemoteCache(String remoteCacheName) {
+      return null;
+   }
+
+   public RemoteCacheSpi getDefaultRemoteCache() {
+      return null;
+   }
+
+   @Override
+   public void start() {
+      // TODO: Customise this generated block
+   }
+
+   @Override
+   public void stop() {
+      // TODO: Customise this generated block
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/TimeoutException.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/TimeoutException.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/TimeoutException.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,10 @@
+package hotrod;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class TimeoutException extends HotRodException {
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/VersionedEntry.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/VersionedEntry.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/VersionedEntry.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,13 @@
+package hotrod;
+
+import java.util.Map;
+
+/**
+ * // TODO: Document this
+ *
+ * @author Mircea.Markus at jboss.com
+ * @since 4.1
+ */
+public interface VersionedEntry extends Map.Entry{
+   public long getVersion();
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/HotrodConstants.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/HotrodConstants.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/HotrodConstants.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,61 @@
+package hotrod.impl;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public interface HotrodConstants {
+
+   public static final short REQUEST_MAGIC = 0xA0;
+   public static final short RESPONSE_MAGIC = 0xA1;
+
+   public static final byte HOTROD_VERSION = 0x01;
+
+   //requests
+   public static final byte PUT_REQUEST = 0x01;
+   public static final byte GET_REQUEST = 0x03;
+   public static final byte PUT_IF_ABSENT_REQUEST = 0x05;
+   public static final byte REPLACE_REQUEST = 0x07;
+   public static final byte REPLACE_IF_UNMODIFIED_REQUEST = 0x09;
+   public static final byte REMOVE_REQUEST = 0x0B;
+   public static final byte REMOVE_IF_UNMODIFIED_REQUEST = 0x0D;
+   public static final byte CONTAINS_KEY_REQUEST = 0x0F;
+   public static final byte PUT_FOR_EXTERNAL_READ_REQUEST = 0x11;
+   public static final byte GET_WITH_CAS_REQUEST = 0x13;
+   public static final byte EVICT_REQUEST = 0x15;
+   public static final byte CLEAR_REQUEST = 0x17;
+   public static final byte STATS_REQUEST = 0x19;
+   public static final byte QUIT_REQUEST = 0x1B;
+   public static final byte EVENT_REGISTRATION_REQUEST = 0x1D;
+
+   //responses
+   public static final byte PUT_RESPONSE = 0x02;
+   public static final byte GET_RESPONSE = 0x04;
+   public static final byte PUT_IF_ABSENT_RESPONSE = 0x06;
+   public static final byte REPLACE_RESPONSE = 0x08;
+   public static final byte REPLACE_IF_UNMODIFIED_RESPONSE = 0x0A;
+   public static final byte REMOVE_RESPONSE = 0x0C;
+   public static final byte REMOVE_IF_UNMODIFIED_RESPONSE = 0x0E;
+   public static final byte CONTAINS_KEY_RESPONSE = 0x10;
+   public static final byte PUT_FOR_EXTERNAL_READ_RESPONSE = 0x12;
+   public static final byte GET_WITH_CAS_RESPONSE = 0x14;
+   public static final byte EVICT_RESPONSE = 0x16;
+   public static final byte CLEAR_RESPONSE = 0x18;
+   public static final byte STATS_RESPONSE = 0x1A;
+   public static final byte QUIT_RESPONSE = 0x1C;
+   public static final byte EVENT_REGISTRATION_RESPONSE = 0x1E;
+
+   //response status
+   public static final byte NO_ERROR_STATUS = 0x00;
+   public static final int INVALID_MAGIC_OR_MESSAGE_ID_STATUS = 0x81;
+   public static final int REQUEST_PARSING_ERROR_STATUS = 0x84;
+   public static final byte NOT_PUT_REMOVED_REPLACED_STATUS = 0x01;
+   public static final int UNKNOWN_COMMAND_STATUS = 0x82;
+   public static final int SERVER_ERROR_STATUS = 0x85;
+   public static final int KEY_DOES_NOT_EXIST_STATUS = 0x02;
+   public static final int UNKNOWN_VERSION_STATUS = 0x83;
+   public static final int COMMAND_TIMEOUT_STATUS = 0x86;
+
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/InvalidResponseException.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/InvalidResponseException.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/InvalidResponseException.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,26 @@
+package hotrod.impl;
+
+import hotrod.HotRodException;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class InvalidResponseException extends HotRodException {
+   public InvalidResponseException() {
+   }
+
+   public InvalidResponseException(String message) {
+      super(message);
+   }
+
+   public InvalidResponseException(String message, Throwable cause) {
+      super(message, cause);
+   }
+
+   public InvalidResponseException(Throwable cause) {
+      super(cause);
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheImpl.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheImpl.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheImpl.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,257 @@
+package hotrod.impl;
+
+import hotrod.ClusterTopologyListener;
+import hotrod.Flag;
+import hotrod.HotRodClientException;
+import hotrod.TimeoutException;
+import hotrod.impl.VersionedEntry;
+import hotrod.impl.RemoteCacheSpi;
+import hotrod.RemoteCacheManager;
+import hotrod.impl.transport.TransportFactory;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class RemoteCacheImpl implements RemoteCacheSpi, HotrodConstants {
+
+   private final String cacheName;
+   private final byte[] cacheNameBytes;
+   private static final AtomicLong MSG_ID = new AtomicLong();
+   private TransportFactory transportFactory;
+
+   public RemoteCacheImpl(String cacheName, TransportFactory transportFactory) {
+      this.cacheName = cacheName;
+      cacheNameBytes = cacheName.getBytes(); //todo add charset here
+      this.transportFactory = transportFactory;
+   }
+
+   public byte[] get(byte[] key) {
+      Transport transport = getTransport();
+      try {
+         byte status = sendKeyOperation(key, transport, GET_REQUEST);
+         if (status == KEY_DOES_NOT_EXIST_STATUS) {
+            return null;
+         }
+         if (status == NO_ERROR_STATUS) {
+            int responseLength = transport.readVInt();
+            byte[] result = new byte[responseLength];
+            return transport.readByteArray(result);
+         } 
+      } finally {
+         transport.release();
+      }
+      throw new IllegalStateException("We should not reach here!");
+   }
+
+   public boolean remove(byte[] key) {
+      Transport transport = getTransport();
+      try {
+         byte status = sendKeyOperation(key, transport, REMOVE_REQUEST);
+         if (status == KEY_DOES_NOT_EXIST_STATUS) {
+            return false;
+         } else if (status == NO_ERROR_STATUS) {
+            return true;
+         }
+      } finally {
+         transport.release();
+      }
+      throw new IllegalStateException("We should not reach here!");
+   }
+
+   public boolean contains(byte[] key) {
+      Transport transport = getTransport();
+      try {
+         byte status = sendKeyOperation(key, transport, CONTAINS_KEY_REQUEST);
+         if (status == KEY_DOES_NOT_EXIST_STATUS) {
+            return false;
+         } else if (status == NO_ERROR_STATUS) {
+            return true;
+         }
+      } finally {
+         transport.release();
+      }
+      throw new IllegalStateException("We should not reach here!");
+   }
+
+   public VersionedEntry getVersionedCacheEntry(byte[] key) {
+      Transport transport = getTransport();
+      try {
+         byte status = sendKeyOperation(key, transport, GET_WITH_CAS_REQUEST);
+         if (status == KEY_DOES_NOT_EXIST_STATUS) {
+            return null;
+         }
+         if (status == NO_ERROR_STATUS) {
+            long cas = transport.readVLong();
+            int responseLength = transport.readVInt();
+            byte[] value = new byte[responseLength];
+            return new VersionedEntry(cas, key, value);
+         }
+      } finally {
+         transport.release();
+      }
+      throw new IllegalStateException("We should not reach here!");
+   }
+
+   //[header][key length][key][lifespan][max idle][value length][value]
+   public void put(byte[] key, byte[] value) {
+      Transport transport = getTransport();
+      try {
+         byte status = sendPutOperation(key, value, transport, PUT_REQUEST);
+         if (status != NO_ERROR_STATUS) {
+            throw new InvalidResponseException("Unexpected response status: " + Integer.toHexString(status));
+         }
+      } finally {
+         transport.release();
+      }
+   }
+
+   public boolean putIfAbsent(byte[] key, byte[] value) {
+      Transport transport = getTransport();
+      try {
+         byte status = sendPutOperation(key, value, transport, PUT_IF_ABSENT_REQUEST);
+         if (status == NO_ERROR_STATUS) {
+            return true;
+         } else if (status == NOT_PUT_REMOVED_REPLACED_STATUS) {
+            return false;
+         }
+      } finally {
+         transport.release();
+      }
+      throw new IllegalStateException("We should not reach here!");
+   }
+
+   public boolean replace(byte[] key, byte[] value) {
+      return false;
+   }
+
+   public VersionedOperationResponse replaceIfUnmodified(byte[] key, byte[] value, long version) {
+      return null;  // TODO: Customise this generated block
+   }
+
+   public VersionedOperationResponse removeIfUnmodified(byte[] key, long version) {
+      return null;  // TODO: Customise this generated block
+   }
+
+   public void putForExternalRead(byte[] key, byte[] value) {
+      // TODO: Customise this generated block
+   }
+
+   public boolean evict(byte[] key) {
+      return false;  // TODO: Customise this generated block
+   }
+
+   public void clear() {
+      // TODO: Customise this generated block
+   }
+
+   public String stats() {
+      return null;  // TODO: Customise this generated block
+   }
+
+   public String stats(String paramName) {
+      return null;  // TODO: Customise this generated block
+   }
+
+   public void addClusterTopologyListener(ClusterTopologyListener listener) {
+      // TODO: Customise this generated block
+   }
+
+   public boolean removeClusterTopologyListener(ClusterTopologyListener listener) {
+      return false;  // TODO: Customise this generated block
+   }
+
+   public RemoteCacheManager getRemoteCacheFactory() {
+      return null;  // TODO: Customise this generated block
+   }
+
+   public Transport getTransport() {
+      return transportFactory.getTransport();
+   }
+
+
+   private byte sendKeyOperation(byte[] key, Transport transport, byte code) {
+      long messageId = writeHeader(transport, code);
+      transport.writeByteArray(key);
+      transport.flush();
+      byte status = readHeaderAndValidate(transport, messageId, code);
+
+      //this will make sure that we don't have an error
+      processResponseStatus(status, messageId);
+      return status;
+   }
+
+   private byte sendPutOperation(byte[] key, byte[] value, Transport transport, byte code) {
+      long messageId = writeHeader(transport, code);
+      transport.writeByteArray(key);
+      //todo - lifespan and max_idle
+      transport.writeVInt(0); //lifespan
+      transport.writeVInt(0); //max_idle
+      transport.writeByteArray(value);
+      transport.flush();
+      byte status = readHeaderAndValidate(transport, messageId, code);
+      processResponseStatus(status, messageId);
+      return status;
+   }
+
+   /*
+    * Magic	| MessageId	| Version | Opcode | CacheNameLength | CacheName | Flags
+    */
+   private long writeHeader(Transport transport, byte operationCode, Flag... flags) {
+      transport.appendUnsignedByte(REQUEST_MAGIC);
+      long messageId = MSG_ID.incrementAndGet();
+      transport.writeVLong(messageId);
+      transport.writeByteArray(HOTROD_VERSION, operationCode);
+      transport.writeVInt(cacheNameBytes.length);
+      transport.writeByteArray(cacheNameBytes);
+      int flagInt = 0;
+      for (Flag flag: flags) {
+         flagInt = flag.getFlagInt() | flagInt;
+      }
+      transport.writeVInt(flagInt);
+      return messageId;
+   }
+
+   private byte readHeaderAndValidate(Transport transport, long messageId, byte opCode) {
+      byte magic = transport.readByte();
+      if (magic != RESPONSE_MAGIC) {
+         throw new InvalidResponseException("Invalid magic number. Expected " + RESPONSE_MAGIC + " and received " + Integer.toHexString(magic));
+      }
+      long receivedMessageId = transport.readVLong();
+      if (receivedMessageId != messageId) {
+         throw new InvalidResponseException("Invalid message id. Expected " + messageId + " and received " + Long.toHexString(receivedMessageId));
+      }
+      byte receivedOpCode = transport.readByte();
+      if (receivedOpCode != opCode) {
+         throw new InvalidResponseException("Invalid response operation. Expected " + opCode + " and received " + Integer.toHexString(receivedOpCode));
+      }
+      return transport.readByte();
+   }
+
+   private void processResponseStatus(byte status, long messageId) {
+      switch ((int)status) {
+         case INVALID_MAGIC_OR_MESSAGE_ID_STATUS:
+         case REQUEST_PARSING_ERROR_STATUS:
+         case UNKNOWN_COMMAND_STATUS:
+         case SERVER_ERROR_STATUS:
+         case UNKNOWN_VERSION_STATUS: {
+            throw new HotRodClientException(messageId, status);
+         }
+         case COMMAND_TIMEOUT_STATUS: {
+            throw new TimeoutException();
+         }
+         case NO_ERROR_STATUS:
+         case KEY_DOES_NOT_EXIST_STATUS: {
+            //don't do anything, these are correct responses
+            break;
+         }
+         default: {
+            throw new IllegalStateException("Unknown status: " + Integer.toHexString(status));
+         }
+      }
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheSpi.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheSpi.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/RemoteCacheSpi.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,72 @@
+package hotrod.impl;
+
+import hotrod.ClusterTopologyListener;
+import hotrod.RemoteCacheManager;
+import hotrod.impl.VersionedEntry;
+
+/**
+ * // TODO: Document this
+ *
+ * - TODO - add timeout support
+ * - TODO - add flags support
+ * - TODO - enforce encoding and add such tests
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public interface RemoteCacheSpi {
+
+   public enum VersionedOperationResponse {
+      SUCCESS(true), NO_SUCH_KEY(false), MODIFIED_KEY(false);
+      private boolean isModified;
+
+      VersionedOperationResponse(boolean modified) {
+         isModified = modified;
+      }
+
+      public boolean isUpdated() {
+         return isModified;
+      }
+   }
+
+   public byte[] get(byte[] key);
+
+   public boolean remove(byte[] key);
+
+   public boolean contains(byte[] key);
+
+   public VersionedEntry getVersionedCacheEntry(byte[] key);
+
+   /**
+    * @return true if this there is an entry for that key in the cache(which is overwritten now), false otherwise.
+    */
+   public void put(byte[] key, byte[] value);
+
+   public boolean putIfAbsent(byte[] key, byte[] value);
+
+   public boolean replace(byte[] key, byte[] value);
+
+   public VersionedOperationResponse replaceIfUnmodified(byte[] key, byte[] value, long version);
+
+   public VersionedOperationResponse removeIfUnmodified(byte[] key, long version);
+
+   public void putForExternalRead(byte[] key, byte[] value);
+
+   /**
+    * @param key the key to be evicted.
+    * @return true if the key was evicted, false if it does not exist or could not be evicted.
+    */
+   public boolean evict(byte[] key);
+
+   public void clear();
+
+   public String stats();
+
+   public String stats(String paramName);
+
+   public void addClusterTopologyListener(ClusterTopologyListener listener);
+
+   public boolean removeClusterTopologyListener(ClusterTopologyListener listener);
+
+   public RemoteCacheManager getRemoteCacheFactory();
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/Transport.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/Transport.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/Transport.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,37 @@
+package hotrod.impl;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public interface Transport {
+
+   public void writeByteArray(byte... toAppend);
+
+   /**
+    * Treats the tailing byte as an unsigned byte.
+    */
+   public void appendUnsignedByte(short requestMagic);
+
+   public void writeVInt(int length);
+
+   public void writeVLong(long l);
+
+   public long readVLong();
+
+   public int readVInt();
+
+   public void flush();
+
+   public byte readByte();
+
+   public void release();
+
+   /**
+    * reads an vint which is size; then an array having that size.
+    * @param bufferToFill
+    */
+   public byte[] readByteArray(byte[] bufferToFill);
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/VersionedEntry.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/VersionedEntry.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/VersionedEntry.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,55 @@
+package hotrod.impl;
+
+import java.util.Arrays;
+
+/**
+* // TODO: Document this
+*
+* @author mmarkus
+* @since 4.1
+*/
+public class VersionedEntry {
+   private final long version;
+   private final byte[] key;
+   private final byte[] value;
+
+   public VersionedEntry(long version, byte[] key, byte[] value) {
+      this.version = version;
+      this.key = key;
+      this.value = value;
+   }
+
+   public long getVersion() {
+      return version;
+   }
+
+   public byte[] getKey() {
+      return key;
+   }
+
+   public byte[] getValue() {
+      return value;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      VersionedEntry that = (VersionedEntry) o;
+
+      if (version != that.version) return false;
+      if (!Arrays.equals(key, that.key)) return false;
+      if (!Arrays.equals(value, that.value)) return false;
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      int result = (int) (version ^ (version >>> 32));
+      result = 31 * result + (key != null ? Arrays.hashCode(key) : 0);
+      result = 31 * result + (value != null ? Arrays.hashCode(value) : 0);
+      return result;
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TcpTransport.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TcpTransport.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TcpTransport.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,128 @@
+package hotrod.impl.transport;
+
+import hotrod.impl.Transport;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.logging.Logger;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class TcpTransport implements Transport {
+
+   public static final Logger log = Logger.getLogger(TcpTransport.class.getName());
+
+   private String host;
+   private int port;
+   private Socket socket;
+
+   public void appendUnsignedByte(short requestMagic) {
+      try {
+         socket.getOutputStream().write(requestMagic);
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public void writeVInt(int length) {
+      try {
+         VHelper.writeVInt(length, socket.getOutputStream());
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public void writeVLong(long l) {
+      try {
+         VHelper.writeVLong(l, socket.getOutputStream());
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public long readVLong() {
+      try {
+         return VHelper.readVLong(socket.getInputStream());
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public int readVInt() {
+      try {
+         return VHelper.readVInt(socket.getInputStream());
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public TcpTransport(String host, int port) {
+      this.host = host;
+      this.port = port;
+   }
+
+   public void connect() {
+      try {
+         socket = new Socket(host, port);
+      } catch (IOException e) {
+         throw new TransportException("Problems establishing initial connection", e);
+      }
+   }
+
+   public void writeByteArray(byte... toAppend) {
+      try {
+         socket.getOutputStream().write(toAppend);
+      } catch (IOException e) {
+         throw new TransportException("Problems writing data to stream", e);
+      }
+   }
+
+   public void flush() {
+      try {
+         socket.getOutputStream().flush();
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public byte readByte() {
+      int resultInt;
+      try {
+         resultInt = socket.getInputStream().read();
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+      if (resultInt == -1) {
+         throw new TransportException("End of stream reached!");
+      }
+      return (byte) resultInt;
+   }
+
+   public void release() {
+      try {
+         socket.close();
+      } catch (IOException e) {
+         log.warning("Issues closing socket:" + e.getMessage());
+      }
+   }
+
+   public byte[] readByteArray(byte[] bufferToFill) {
+      int size;
+      try {
+         size = socket.getInputStream().read(bufferToFill);
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+      if (size == -1) {
+         throw new RuntimeException("End of stream reached!");
+      }
+      if (size != bufferToFill.length) {
+         throw new TransportException("Expected " + bufferToFill.length + " bytes but only could read " + size + " bytes!");
+      }
+      return bufferToFill;
+   }
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportException.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportException.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportException.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,26 @@
+package hotrod.impl.transport;
+
+import hotrod.HotRodException;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class TransportException extends HotRodException {
+   public TransportException() {
+   }
+
+   public TransportException(String message) {
+      super(message);
+   }
+
+   public TransportException(String message, Throwable cause) {
+      super(message, cause);
+   }
+
+   public TransportException(Throwable cause) {
+      super(cause);
+   }
+}
\ No newline at end of file

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportFactory.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportFactory.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/TransportFactory.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,18 @@
+package hotrod.impl.transport;
+
+import hotrod.impl.Transport;
+import hotrod.impl.transport.TcpTransport;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class TransportFactory {
+
+   public Transport getTransport() {
+      return new TcpTransport("a",1);
+   }
+
+}

Added: trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/VHelper.java
===================================================================
--- trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/VHelper.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/java/hotrod/impl/transport/VHelper.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,102 @@
+package hotrod.impl.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+public class VHelper {
+   private static final int MAX_VINT_BYTES = 5;
+   private static final int MAX_VLONG_BYTES = 9;
+
+   public static int readVInt(InputStream is) {
+      int result = 0;
+      for (int i = 0; i < MAX_VINT_BYTES; i++) {
+         int aByte = nextByte(is);
+         boolean hasMore = (aByte & 128) > 0;
+         aByte &= 127; //remove leading byte
+         result = result | (aByte << (i*7));
+         if (!hasMore) break;
+      }
+      if (result < 0)
+         throw new TransportException("negative number read: " + result);
+      return result;
+   }
+
+   public static void writeVInt(int toWrite, OutputStream os) {
+      boolean hasMore;
+      do {
+         int currentByte = toWrite & 0x0000007F;
+         toWrite = toWrite >> 7;
+         hasMore = toWrite > 0;
+         if (hasMore) {
+            currentByte |= 128;
+         }
+         writeByte(os, currentByte);
+      } while (hasMore);
+   }
+
+   private static void writeByte(OutputStream os, int currentByte) {
+      try {
+         os.write(currentByte);
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   private static int nextByte(InputStream is) {
+      try {
+         int result = is.read();
+         if (result < 0) {
+            throw new TransportException("Unexpected end of stream " + result);
+         }
+         return result;
+      } catch (IOException e) {
+         throw new TransportException(e);
+      }
+   }
+
+   public static long readVLong(InputStream is) {
+      long result = 0;
+      for (int i = 0; i < MAX_VLONG_BYTES; i++) {
+         long aByte = nextByte(is);
+         boolean hasMore = (aByte & 128) > 0;
+         aByte &= 127; //remove leading byte
+         result = result | (aByte << (i*7));
+         if (!hasMore) break;
+      }
+      if (result < 0)
+         throw new TransportException("negative number read: " + result);
+      return result;
+   }
+
+
+   public static void writeVLong(long toWrite, OutputStream os) {
+      boolean hasMore;
+      do {
+         long currentByte = toWrite & 0x000000000000007F;
+         toWrite = toWrite >> 7;
+         hasMore = toWrite > 0;
+         if (hasMore) {
+            currentByte |= 128;
+         }
+         writeByte(os, (int)currentByte);
+      } while (hasMore);
+   }
+
+   public static void main(String[] args) {
+      long zero = 0;
+      long aByte = 1;
+      long shift = 35;
+      System.out.println((zero | (aByte << 35)));
+      long a = 1l<<18;
+      long b = 1l<<17;
+      long ab = a * b;
+      System.out.println(ab);
+   }
+}

Added: trunk/client/hotrod-client/src/main/resources/hotrod-client.properties
===================================================================
--- trunk/client/hotrod-client/src/main/resources/hotrod-client.properties	                        (rev 0)
+++ trunk/client/hotrod-client/src/main/resources/hotrod-client.properties	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1 @@
+hotrod-servers=127.0.0.1:8998:127.0.0.2:6723
\ No newline at end of file

Added: trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodClientIntegrationTest.java
===================================================================
--- trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodClientIntegrationTest.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodClientIntegrationTest.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,274 @@
+package org.infinispan.client.hotrod;
+
+import hotrod.ClientDisconnectedException;
+import hotrod.ClusterTopologyListener;
+import hotrod.impl.VersionedEntry;
+import hotrod.impl.RemoteCacheSpi;
+import hotrod.RemoteCacheManager;
+import org.infinispan.Cache;
+import org.infinispan.config.Configuration;
+import org.infinispan.test.MultipleCacheManagersTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+ at Test (testName = "client.hotrod.HotRodClientIntegrationTest", groups = "functional", enabled = false) 
+public class HotRodClientIntegrationTest extends MultipleCacheManagersTest {
+
+   private static final String CACHE_NAME = "replSync";
+   private Cache cache;
+   private Cache defaultCache;
+
+   RemoteCacheSpi defaultRemoteCacheSpi;
+   RemoteCacheSpi remoteCacheSpi;
+
+   @Override
+   protected void createCacheManagers() throws Throwable {
+      Configuration replSync = getDefaultClusteredConfig(Configuration.CacheMode.REPL_SYNC);
+      createClusteredCaches(2, CACHE_NAME, replSync);
+
+      //pass the config file to the cache
+      RemoteCacheManager cacheManager = new RemoteCacheManager();
+      defaultRemoteCacheSpi = cacheManager.getDefaultRemoteCache();
+      remoteCacheSpi = cacheManager.getRemoteCache(CACHE_NAME);
+   }
+
+   @AfterClass
+   public void testDestroyRemoteCacheFactory() {
+      RemoteCacheManager cacheManager = remoteCacheSpi.getRemoteCacheFactory();
+      cacheManager.stop();
+      try {
+         remoteCacheSpi.get("aKey".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.clear();
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.evict("aKey".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.addClusterTopologyListener(null);
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.removeClusterTopologyListener(null);
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.putForExternalRead("aKey".getBytes(), "aValue".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.putIfAbsent("aKey".getBytes(), "aValue".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.remove("aKey".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.removeIfUnmodified("aKey".getBytes(), 12321L);
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.replaceIfUnmodified("aKey".getBytes(), "aNewValue".getBytes(), 12321L);
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+      try {
+         remoteCacheSpi.replace("aKey".getBytes(), "aNewValue".getBytes());
+         assert false;
+      } catch (ClientDisconnectedException e) {}
+   }
+
+   @AfterClass (alwaysRun = true)
+   @Override
+   protected void destroy() {
+      TestTopologyListener listener = new TestTopologyListener();
+      remoteCacheSpi.addClusterTopologyListener(listener);
+      super.destroy();
+      assert listener.invocationCount == 2; 
+   }
+
+   public void testPut() {
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      assertEquals("aValue", get(cache, "aKey"));
+      defaultRemoteCacheSpi.put("otherKey".getBytes(), "otherValue".getBytes());
+      assertEquals("otherValue", get(defaultCache, "otherKey"));
+
+      assert Arrays.equals("aKey".getBytes(), remoteCacheSpi.get("aValue".getBytes()));
+      assert Arrays.equals("otherKey".getBytes(), remoteCacheSpi.get("otherKey".getBytes()));
+   }
+
+   public void testRemove() {
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      assert Arrays.equals("aKey".getBytes(), remoteCacheSpi.get("aValue".getBytes()));
+
+      assert get(cache, "aKey").equals("aValue");
+      assertEquals(true, remoteCacheSpi.remove("aKey".getBytes()));
+      assert remoteCacheSpi.remove("aKey".getBytes());
+      assert get(cache,"aKey") == null;
+      assert !remoteCacheSpi.remove("aKey".getBytes());
+   }
+
+   public void testContains() {
+      assert !remoteCacheSpi.contains("aKey".getBytes());
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      assert !remoteCacheSpi.contains("aKey".getBytes());
+   }
+
+   public void testGetVersionedCacheEntry() {
+      assert null == remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      VersionedEntry entry = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      assert entry != null;
+      assert Arrays.equals(entry.getKey(), "aKey".getBytes());
+      assert Arrays.equals(entry.getValue(), "aValue".getBytes());
+
+      //now put the same value
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      VersionedEntry entry2 = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      assert Arrays.equals(entry2.getKey(), "aKey".getBytes());
+      assert Arrays.equals(entry2.getValue(), "aValue".getBytes());
+
+      assert entry2.getVersion() != entry.getVersion();
+      assert !entry.equals(entry2);
+
+      //now put a different value
+      remoteCacheSpi.put("aKey".getBytes(), "anotherValue".getBytes());
+      VersionedEntry entry3 = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      assert Arrays.equals(entry3.getKey(), "aKey".getBytes());
+      assert Arrays.equals(entry3.getValue(), "anotherValue".getBytes());
+      assert entry3.getVersion() != entry2.getVersion();
+      assert !entry3.equals(entry2);
+   }
+
+   public void testReplace() {
+      assert !remoteCacheSpi.replace("aKey".getBytes(), "anotherValue".getBytes());
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      assert remoteCacheSpi.replace("aKey".getBytes(), "anotherValue".getBytes());
+      assert get(cache, "aKey").equals("anotherValue");
+   }
+
+   public void testReplaceIfUnmodified() {
+      RemoteCacheSpi.VersionedOperationResponse response = remoteCacheSpi.replaceIfUnmodified("aKey".getBytes(), "aValue".getBytes(), 12321212l);
+      assert response == RemoteCacheSpi.VersionedOperationResponse.NO_SUCH_KEY;
+      assert !response.isUpdated();
+
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      VersionedEntry entry = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      response = remoteCacheSpi.replaceIfUnmodified("aKey".getBytes(), "aNewValue".getBytes(), entry.getVersion());
+      assert response == RemoteCacheSpi.VersionedOperationResponse.SUCCESS;
+      assert response.isUpdated();
+
+      VersionedEntry entry2 = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      assert entry2.getVersion() != entry.getVersion();
+      assert Arrays.equals(entry2.getKey(), "aKey".getBytes());
+      assert Arrays.equals(entry2.getValue(), "aNewValue".getBytes());
+
+      response = remoteCacheSpi.replaceIfUnmodified("aKey".getBytes(), "aNewValue".getBytes(), entry.getVersion());
+      assert response == RemoteCacheSpi.VersionedOperationResponse.MODIFIED_KEY;
+      assert !response.isUpdated();
+   }
+
+   public void testRemoveIfUnmodified() {
+      RemoteCacheSpi.VersionedOperationResponse response = remoteCacheSpi.removeIfUnmodified("aKey".getBytes(), 12321212l);
+      assert response == RemoteCacheSpi.VersionedOperationResponse.NO_SUCH_KEY;
+      assert !response.isUpdated();
+
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      VersionedEntry entry = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      response = remoteCacheSpi.removeIfUnmodified("aKey".getBytes(), entry.getVersion());
+      assert response == RemoteCacheSpi.VersionedOperationResponse.SUCCESS;
+      assert response.isUpdated();
+      assert !cache.containsKey("aKey".getBytes());
+
+      remoteCacheSpi.put("aKey".getBytes(), "aValueNew".getBytes());
+
+      VersionedEntry entry2 = remoteCacheSpi.getVersionedCacheEntry("aKey".getBytes());
+      assert entry2.getVersion() != entry.getVersion();
+      assert Arrays.equals(entry2.getKey(), "aKey".getBytes());
+      assert Arrays.equals(entry2.getValue(), "aNewValue".getBytes());
+
+      response = remoteCacheSpi.removeIfUnmodified("aKey".getBytes(), entry.getVersion());
+      assert response == RemoteCacheSpi.VersionedOperationResponse.MODIFIED_KEY;
+      assert !response.isUpdated();
+   }
+
+   public void testPutIfAbsent() {
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      assert !remoteCacheSpi.putIfAbsent("aKey".getBytes(), "anotherValue".getBytes());
+      assert get(cache, "aKey").equals("aValue");
+
+      assert cache.remove("aKey".getBytes()).equals("aValue".getBytes());
+      assert !remoteCacheSpi.contains("aKey".getBytes());
+
+      assert true : remoteCacheSpi.replace("aKey".getBytes(), "anotherValue".getBytes());
+   }
+
+   public void testPutForExternalRead() {
+      remoteCacheSpi.putForExternalRead("aKey".getBytes(), "aValue".getBytes());
+      remoteCacheSpi.putForExternalRead("aKey".getBytes(), "anotherValue".getBytes());
+      assert get(cache, "aKey").equals("aValue");
+
+      assert cache.remove("aKey".getBytes()).equals("aValue".getBytes());
+      assert !remoteCacheSpi.contains("aKey".getBytes());
+   }
+
+   public void testClear() {
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      remoteCacheSpi.put("aKey2".getBytes(), "aValue".getBytes());
+      remoteCacheSpi.clear();
+      assert cache.isEmpty();
+      assert !remoteCacheSpi.contains("aKey".getBytes());
+      assert !remoteCacheSpi.contains("aKey2".getBytes());
+   }
+
+   public void testEvict() {
+      assert  !remoteCacheSpi.evict("aKey".getBytes());
+      remoteCacheSpi.put("aKey".getBytes(), "aValue".getBytes());
+      assert remoteCacheSpi.evict("aKey".getBytes());
+   }
+
+   public void testStats() {
+      //todo implement
+   }
+
+   private Object get(Cache cache, String s) {
+      return new String((byte[])cache.get(s.getBytes()));
+   }
+
+   private static class TestTopologyListener implements ClusterTopologyListener {
+
+      private int invocationCount;
+
+      public void nodeAdded(List<Address> currentTopology, Address addedNode) {
+         // TODO: Customise this generated block
+      }
+
+      public void nodeRemoved(List<Address> currentTopology, Address removedNode) {
+         invocationCount++;
+      }
+   }
+}

Added: trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodListenerTest.java
===================================================================
--- trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodListenerTest.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/HotRodListenerTest.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,13 @@
+package org.infinispan.client.hotrod;
+
+import org.testng.annotations.Test;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+ at Test (testName = "client.hotrod.HotRodListenerTest", groups = "functional")
+public class HotRodListenerTest {
+}

Added: trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/VHelperTest.java
===================================================================
--- trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/VHelperTest.java	                        (rev 0)
+++ trunk/client/hotrod-client/src/test/java/org/infinispan/client/hotrod/VHelperTest.java	2010-03-24 11:03:01 UTC (rev 1617)
@@ -0,0 +1,264 @@
+package org.infinispan.client.hotrod;
+
+import hotrod.impl.transport.VHelper;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * // TODO: Document this
+ *
+ * @author mmarkus
+ * @since 4.1
+ */
+ at Test(testName = "client.hotrod.VHelperTest", groups = "unit, functional")
+public class VHelperTest {
+
+   public void testString2Byte() {
+      str2byte("00000001", (byte) 1);
+      str2byte("00001001", (byte) 9);
+      str2byte("01111111", (byte) 127);
+      str2byte("11111111", (byte) -1);
+      str2byte("11111110", (byte) -2);
+      str2byte("10000001", (byte) -127);
+      str2byte("10000000", (byte) -128);
+      str2byte("11111011", (byte) -5);
+   }
+
+   public void testByte2String() {
+      byte2str((byte) 9, "00001001");
+      byte2str((byte) 1, "00000001");
+      byte2str((byte) 127, "01111111");
+      byte2str((byte) -1, "11111111");
+      byte2str((byte) -2, "11111110");
+      byte2str((byte) -127, "10000001");
+      byte2str((byte) -128, "10000000");
+      byte2str((byte) -5, "11111011");
+   }
+
+   public void testReadVInt1() {
+      assert 0 == VHelper.readVInt(getInputStream((byte) 0));
+      assert 1 == VHelper.readVInt(getInputStream((byte) 1));
+      assert 2 == VHelper.readVInt(getInputStream((byte) 2));
+
+      assert 127 == VHelper.readVInt(getInputStream((byte) 127));
+
+
+      byte[] nr128 = new byte[]{str2byte("10000000"), str2byte("00000001")};
+      assert 128 == VHelper.readVInt(getInputStream(nr128));
+
+
+      byte[] nr129 = new byte[]{str2byte("10000001"), str2byte("00000001")};
+      assert 129 == VHelper.readVInt(getInputStream(nr129));
+
+      byte[] nr130 = new byte[]{str2byte("10000010"), str2byte("00000001")};
+      assert 130 == VHelper.readVInt(getInputStream(nr130));
+
+      byte[] nr16383 = new byte[]{str2byte("11111111"), str2byte("01111111")};
+      assert 16383 == VHelper.readVInt(getInputStream(nr16383));
+
+      byte[] nr16384 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      assert 16384 == VHelper.readVInt(getInputStream(nr16384));
+
+      byte[] nr16385 = new byte[]{str2byte("10000001"), str2byte("10000000"), str2byte("00000001")};
+      assert 16385 == VHelper.readVInt(getInputStream(nr16385));
+
+      byte[] nr16393 = new byte[]{str2byte("10001001"), str2byte("10000000"), str2byte("00000001")};
+      assert 16393 == VHelper.readVInt(getInputStream(nr16393));
+
+      byte[] nr2pow28 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      assert (1 << 28) == VHelper.readVInt(getInputStream(nr2pow28));
+
+      byte[] nr2pow30 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000100")};
+      assert (1 << 30) == VHelper.readVInt(getInputStream(nr2pow30));
+
+      byte[] nr2pow30plus2 = new byte[]{str2byte("10000010"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000100")};
+      assert (1 << 30) + 2 == VHelper.readVInt(getInputStream(nr2pow30plus2));
+   }
+
+   public void testWriteVint() {
+      String[] written = writeVInt(0);
+      assert Arrays.equals(written, new String[]{"00000000"});
+
+      written = writeVInt(1);
+      assert Arrays.equals(written, new String[]{"00000001"});
+
+      written = writeVInt(127);
+      assert Arrays.equals(written, new String[]{"01111111"});
+
+      written = writeVInt(129);
+      assert Arrays.equals(written, new String[]{"10000001", "00000001"});
+
+      written = writeVInt(130);
+      assert Arrays.equals(written, new String[]{"10000010", "00000001"});
+
+      written = writeVInt(16383);
+      assert Arrays.equals(written, new String[]{"11111111", "01111111"});
+
+      written = writeVInt(16384);
+      assert Arrays.equals(written, new String[]{"10000000", "10000000", "00000001"});
+
+      written = writeVInt(16385);
+      assert Arrays.equals(written, new String[]{"10000001", "10000000", "00000001"});
+
+      written = writeVInt(1 << 28);
+      assert Arrays.equals(written, new String[]{"10000000", "10000000", "10000000", "10000000", "00000001"});
+
+      written = writeVInt(1 << 30);
+      assert Arrays.equals(written, new String[]{"10000000", "10000000", "10000000", "10000000", "00000100"});
+
+      written = writeVInt((1 << 30) + 2);
+      assert Arrays.equals(written, new String[]{"10000010", "10000000", "10000000", "10000000", "00000100"});
+   }
+
+   public void testReadVLong() {
+      assert 0 == VHelper.readVLong(getInputStream((byte) 0));
+      assert 1 == VHelper.readVLong(getInputStream((byte) 1));
+      assert 2 == VHelper.readVLong(getInputStream((byte) 2));
+
+      assert 127 == VHelper.readVLong(getInputStream((byte) 127));
+
+
+      byte[] nr128 = new byte[]{str2byte("10000000"), str2byte("00000001")};
+      assert 128 == VHelper.readVLong(getInputStream(nr128));
+
+
+      byte[] nr129 = new byte[]{str2byte("10000001"), str2byte("00000001")};
+      assert 129 == VHelper.readVLong(getInputStream(nr129));
+
+      byte[] nr130 = new byte[]{str2byte("10000010"), str2byte("00000001")};
+      assert 130 == VHelper.readVLong(getInputStream(nr130));
+
+      byte[] nr16383 = new byte[]{str2byte("11111111"), str2byte("01111111")};
+      assert 16383 == VHelper.readVLong(getInputStream(nr16383));
+
+      byte[] nr16384 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      assert 16384 == VHelper.readVLong(getInputStream(nr16384));
+
+      byte[] nr16385 = new byte[]{str2byte("10000001"), str2byte("10000000"), str2byte("00000001")};
+      assert 16385 == VHelper.readVLong(getInputStream(nr16385));
+
+      byte[] nr2pow28 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      assert (1 << 28) == VHelper.readVLong(getInputStream(nr2pow28));
+
+      byte[] nr2pow30 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000100")};
+      assert (1 << 30) == VHelper.readVLong(getInputStream(nr2pow30));
+
+      byte[] nr2pow30plus2 = new byte[]{str2byte("10000010"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000100")};
+      assert (1 << 30) + 2 == VHelper.readVLong(getInputStream(nr2pow30plus2));
+
+      byte[] nr2pow35 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      long expected = 1l << 35;
+      long obtained = VHelper.readVLong(getInputStream(nr2pow35));
+      assert expected == obtained : "expected " + expected + " but received " + obtained;
+
+
+      byte[] nr2pow42 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      expected = (long) (1 << 22) * (long) (1 << 20);
+      obtained = VHelper.readVLong(getInputStream(nr2pow42));
+      assert expected == obtained : "Expected " + expected + " but obtained " + obtained;
+
+      byte[] nr2pow56 = new byte[]{str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("10000000"), str2byte("00000001")};
+      expected = 1l << 56;
+      obtained = VHelper.readVLong(getInputStream(nr2pow56));
+      assert expected == obtained : "Expected " + expected + " but obtained " + obtained;
+   }
+
+   public void testWriteVLong() {
+      String[] written = writeVLong(0);
+      assert Arrays.equals(written, new String[]{"00000000"});
+
+      written = writeVLong(1);
+      assert Arrays.equals(written, new String[]{"00000001"});
+
+      written = writeVLong(127);
+      assert Arrays.equals(written, new String[]{"01111111"});
+
+      written = writeVLong(129);
+      assert Arrays.equals(written, new String[]{"10000001", "00000001"});
+
+      written = writeVLong(130);
+      assert Arrays.equals(written, new String[]{"10000010", "00000001"});
+
+      written = writeVLong(16383);
+      assert Arrays.equals(written, new String[]{"11111111", "01111111"});
+
+      written = writeVLong(16384);
+      assert Arrays.equals(written, new String[]{"10000000", "10000000", "00000001"});
+
+      written = writeVLong(16385);
+      assert Arrays.equals(written, new String[]{"10000001", "10000000", "00000001"});
+
+      written = writeVLong(1 << 28);
+      assert Arrays.equals(written, new String[]{"10000000", "10000000", "10000000", "10000000", "00000001"});
+
+      written = writeVLong(1 << 30);
+      assert Arrays.equals(written, new String[]{"10000000", "10000000", "10000000", "10000000", "00000100"});
+
+      written = writeVLong((1 << 30) + 2);
+      assert Arrays.equals(written, new String[]{"10000010", "10000000", "10000000", "10000000", "00000100"});
+
+      written = writeVLong((1l << 56) + 2);
+      assert Arrays.equals(written, new String[]{"10000010", "10000000", "10000000", "10000000", "10000000", "10000000", "10000000", "10000000", "00000001"});
+   }
+
+
+   private String[] writeVInt(int toWrite) {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream(5);
+      VHelper.writeVInt(toWrite, baos);
+      byte[] result = baos.toByteArray();
+      return toStringArray(result);
+   }
+
+   private String[] writeVLong(long toWrite) {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream(9);
+      VHelper.writeVLong(toWrite, baos);
+      byte[] result = baos.toByteArray();
+      return toStringArray(result);
+   }
+
+   private String[] toStringArray(byte[] result) {
+      String[] resultStr = new String[result.length];
+      for (int i = 0; i < resultStr.length; i++) {
+         resultStr[i] = byte2str(result[i]);
+      }
+      return resultStr;
+   }
+
+
+   private InputStream getInputStream(byte... bytes) {
+      return new ByteArrayInputStream(bytes);
+   }
+
+
+   private byte str2byte(String str, byte... check) {
+      assert str.length() == 8;
+      byte result = 0;
+      for (int i = 7; i >= 0; i--) {
+         assert str.charAt(i) == '0' || str.charAt(i) == '1';
+         boolean isOne = str.charAt(i) == '1';
+         byte mask = isOne ? ((byte) (1 << (7 - i))) : (byte) 0;
+         result |= mask;
+      }
+      if (check != null && check.length > 0) {
+         assert result == check[0] : "Expected " + check[0] + " but received " + result;
+      }
+      return result;
+   }
+
+   private String byte2str(byte aByte, String... expectedValue) {
+      String result = "";
+      for (int i = 0; i <= 7; i++) {
+         byte mask = (byte) (1 << (7 - i));
+         boolean isOne = (mask & aByte) != 0;
+         result += isOne ? "1" : "0";
+      }
+      if (expectedValue != null && expectedValue.length > 0) {
+         assert result.equals(expectedValue[0]) : "Expected " + expectedValue[0] + " but received " + result;
+      }
+      return result;
+   }
+}

Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml	2010-03-23 18:05:33 UTC (rev 1616)
+++ trunk/pom.xml	2010-03-24 11:03:01 UTC (rev 1617)
@@ -33,6 +33,7 @@
       <module>server/core</module>
       <module>server/memcached</module>
       <!-- module>server/hotrod</module -->
+      <module>client/hotrod-client</module>
       <module>jopr-plugin</module>
       <module>demos/gui</module>
       <module>demos/ec2</module>



More information about the infinispan-commits mailing list