This is an automated email from the ASF dual-hosted git repository.

upthewaterspout pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git

commit 2f596638c68472ccacb0085d8982b574ca645cc4
Author: Sarge <[email protected]>
AuthorDate: Wed Dec 6 16:19:37 2017 -0800

    GEODE-4054: Create module for Protobuf message-based client
---
 geode-experimental-driver/build.gradle             |  28 +++
 .../apache/geode/experimental/driver/Driver.java   |  50 +++++
 .../geode/experimental/driver/DriverFactory.java   |  60 ++++++
 .../geode/experimental/driver/ProtobufDriver.java  | 178 ++++++++++++++++++
 .../geode/experimental/driver/ProtobufRegion.java  | 206 +++++++++++++++++++++
 .../apache/geode/experimental/driver/Region.java   |  86 +++++++++
 .../experimental/driver/RegionAttributes.java      |  79 ++++++++
 .../geode/experimental/driver/ValueEncoder.java    | 112 +++++++++++
 .../experimental/driver/RegionIntegrationTest.java | 102 ++++++++++
 .../experimental/driver/ValueEncoderTest.java      |  50 +++++
 settings.gradle                                    |   1 +
 11 files changed, 952 insertions(+)

diff --git a/geode-experimental-driver/build.gradle 
b/geode-experimental-driver/build.gradle
new file mode 100644
index 0000000..ce3eb46
--- /dev/null
+++ b/geode-experimental-driver/build.gradle
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+dependencies {
+    compile project(':geode-common')
+    compile project(':geode-protobuf-messages')
+
+    compile 'com.google.protobuf:protobuf-java:' + 
project.'protobuf-java.version'
+    testCompile project(':geode-core')
+    testCompile project(':geode-junit')
+    testCompile project(':geode-protobuf')
+    testCompile project(':geode-client-protocol')
+    testCompile files(project(':geode-core').sourceSets.test.output)
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/Driver.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/Driver.java
new file mode 100644
index 0000000..59e9f82
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/Driver.java
@@ -0,0 +1,50 @@
+/*
+ * 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.experimental.driver;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.apache.geode.annotations.Experimental;
+
+/**
+ * Defines the behaviors of a driver for communicating with a GemFire server 
by way of the new
+ * protocol.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ */
+@Experimental
+public interface Driver {
+  /**
+   * Retrieves a set of unique names of regions in the GemFire server to which 
this driver is
+   * connected.
+   *
+   * @return Set of strings of names that uniquely identify regions.
+   * @throws IOException
+   */
+  Set<String> getRegionNames() throws IOException;
+
+  /**
+   * Creates an implementation of the region interface for the region with the 
unique name of
+   * <code>regionName</code>.
+   *
+   * @param regionName String that uniquely identifies the region.
+   * @param <K> Type of region keys.
+   * @param <V> Type of region values.
+   * @return
+   */
+  <K, V> Region<K, V> getRegion(String regionName);
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java
new file mode 100644
index 0000000..267bb3d
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java
@@ -0,0 +1,60 @@
+/*
+ * 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.experimental.driver;
+
+import java.net.InetSocketAddress;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.geode.annotations.Experimental;
+
+/**
+ * This is an experimental driver for connecting a client to a geode cluster. 
This driver is still
+ * under development. For a working, full featured client, use ClientCache in 
geode-core. This
+ * driver factory supports the builder style of chaining together mutators.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ */
+@Experimental
+public class DriverFactory {
+  /**
+   * Set of Internet-address-or-host-name/port pairs of the locators to use to 
find GemFire servers
+   * that have Protobuf enabled.
+   */
+  private Set<InetSocketAddress> locators = new HashSet<InetSocketAddress>();
+
+  /**
+   * Adds a locator at <code>host</code> and <code>port</code> to the set of 
locators to use.
+   *
+   * @param host Internet address or host name.
+   * @param port Port number.
+   * @return This driver factory.
+   */
+  public DriverFactory addLocator(String host, int port) {
+    this.locators.add(new InetSocketAddress(host, port));
+    return this;
+  }
+
+  /**
+   * Creates a driver configured to use all the locators about which this 
driver factory knows.
+   *
+   * @return New driver.
+   * @throws Exception
+   */
+  public Driver create() throws Exception {
+    return new ProtobufDriver(locators);
+  }
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java
new file mode 100644
index 0000000..1dac79f
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java
@@ -0,0 +1,178 @@
+/*
+ * 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.experimental.driver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.geode.annotations.Experimental;
+import org.apache.geode.internal.protocol.protobuf.ProtocolVersion;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+import org.apache.geode.internal.protocol.protobuf.v1.ClientProtocol;
+import org.apache.geode.internal.protocol.protobuf.v1.LocatorAPI;
+import org.apache.geode.internal.protocol.protobuf.v1.RegionAPI;
+
+/**
+ * Implements the behaviors of a driver for communicating with a GemFire 
server by way of the new
+ * protocol.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ */
+@Experimental
+public class ProtobufDriver implements Driver {
+  /**
+   * Set of Internet-address-or-host-name/port pairs of the locators to use to 
find GemFire servers
+   * that have Protobuf enabled.
+   */
+  private final Set<InetSocketAddress> locators;
+
+  /**
+   * Socket to a GemFire locator that has Protobuf enabled.
+   */
+  private final Socket socket;
+
+  /**
+   * Creates a driver implementation that communicates via <code>socket</code> 
to a GemFire locator.
+   *
+   * @param locators Set of Internet-address-or-host-name/port pairs of the 
locators to use to find
+   *        GemFire servers that have Protobuf enabled.
+   * @throws IOException
+   */
+  ProtobufDriver(Set<InetSocketAddress> locators) throws IOException {
+    this.locators = locators;
+    Collection<InetSocketAddress> servers = getAvailableServers();
+    InetSocketAddress anyServer = servers.iterator().next();
+    socket = new Socket(anyServer.getAddress(), anyServer.getPort());
+
+    final OutputStream outputStream = socket.getOutputStream();
+    ProtocolVersion.NewConnectionClientVersion.newBuilder()
+        
.setMajorVersion(ProtocolVersion.MajorVersions.CURRENT_MAJOR_VERSION_VALUE)
+        
.setMinorVersion(ProtocolVersion.MinorVersions.CURRENT_MINOR_VERSION_VALUE).build()
+        .writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    if (!ProtocolVersion.VersionAcknowledgement.parseDelimitedFrom(inputStream)
+        .getVersionAccepted()) {
+      throw new IOException("Failed protocol version verification.");
+    }
+  }
+
+  /**
+   * Retrieves a set of unique names of regions in the GemFire server to which 
this driver is
+   * connected.
+   *
+   * @return Set of strings of names that uniquely identify regions.
+   * @throws IOException
+   */
+  @Override
+  public Set<String> getRegionNames() throws IOException {
+    Set<String> regionNames = new HashSet<>();
+
+    final OutputStream outputStream = socket.getOutputStream();
+    ClientProtocol.Message.newBuilder()
+        .setRequest(ClientProtocol.Request.newBuilder()
+            
.setGetRegionNamesRequest(RegionAPI.GetRegionNamesRequest.newBuilder()))
+        .build().writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    final RegionAPI.GetRegionNamesResponse getRegionNamesResponse = 
ClientProtocol.Message
+        
.parseDelimitedFrom(inputStream).getResponse().getGetRegionNamesResponse();
+    for (int i = 0; i < getRegionNamesResponse.getRegionsCount(); ++i) {
+      regionNames.add(getRegionNamesResponse.getRegions(i));
+    }
+
+    return regionNames;
+  }
+
+  /**
+   * Creates an implementation of the region interface for the region with the 
unique name of
+   * <code>regionName</code>.
+   *
+   * @param regionName String that uniquely identifies the region.
+   * @param <K> Type of region keys.
+   * @param <V> Type of region values.
+   * @return
+   */
+  @Override
+  public <K, V> Region<K, V> getRegion(String regionName) {
+    return new ProtobufRegion(regionName, socket);
+  }
+
+  /**
+   * Queries a locator for the GemFire servers that have Protobuf enabled.
+   *
+   * @return Set of Internet-address-or-host-name/port pairs of the GemFire 
servers that have
+   *         Protobuf enabled.
+   * @throws IOException
+   */
+  private Collection<InetSocketAddress> getAvailableServers() throws 
IOException {
+    IOException lastException = null;
+
+    for (InetSocketAddress locator : locators) {
+      try {
+        final Socket locatorSocket = new Socket(locator.getAddress(), 
locator.getPort());
+
+        final OutputStream outputStream = locatorSocket.getOutputStream();
+        ProtocolVersion.NewConnectionClientVersion.newBuilder()
+            
.setMajorVersion(ProtocolVersion.MajorVersions.CURRENT_MAJOR_VERSION_VALUE)
+            
.setMinorVersion(ProtocolVersion.MinorVersions.CURRENT_MINOR_VERSION_VALUE).build()
+            .writeDelimitedTo(outputStream);
+
+        // The locator does not currently send a reply to the 
ProtocolVersion...
+        // if
+        // 
(!ProtocolVersion.HandshakeAcknowledgement.parseDelimitedFrom(inputStream).getHandshakePassed())
+        // {
+        // throw new IOException("Failed ProtocolVersion.");
+        // }
+
+        ClientProtocol.Message.newBuilder()
+            .setRequest(ClientProtocol.Request.newBuilder()
+                
.setGetAvailableServersRequest(LocatorAPI.GetAvailableServersRequest.newBuilder()))
+            .build().writeDelimitedTo(outputStream);
+
+        final InputStream inputStream = locatorSocket.getInputStream();
+        LocatorAPI.GetAvailableServersResponse getAvailableServersResponse = 
ClientProtocol.Message
+            
.parseDelimitedFrom(inputStream).getResponse().getGetAvailableServersResponse();
+        if (getAvailableServersResponse.getServersCount() < 1) {
+          continue;
+        }
+
+        ArrayList<InetSocketAddress> availableServers =
+            new ArrayList<>(getAvailableServersResponse.getServersCount());
+        for (int i = 0; i < getAvailableServersResponse.getServersCount(); 
++i) {
+          final BasicTypes.Server server = 
getAvailableServersResponse.getServers(i);
+          availableServers.add(new InetSocketAddress(server.getHostname(), 
server.getPort()));
+        }
+        return availableServers;
+      } catch (IOException e) {
+        lastException = e;
+      }
+    }
+
+    if (lastException != null) {
+      throw lastException;
+    } else {
+      throw new IllegalStateException("No locators");
+    }
+  }
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufRegion.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufRegion.java
new file mode 100644
index 0000000..5ff4f1a
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufRegion.java
@@ -0,0 +1,206 @@
+/*
+ * 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.experimental.driver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.geode.annotations.Experimental;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+import org.apache.geode.internal.protocol.protobuf.v1.ClientProtocol;
+import org.apache.geode.internal.protocol.protobuf.v1.RegionAPI;
+
+/**
+ * Implements the behaviors of a GemFire region. Send and receives Protobuf 
messages on the provided
+ * socket to communicate with a GemFire server that has Protobuf enabled.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ *
+ * @param <K> Type of region keys.
+ * @param <V> Type of region values.
+ */
+@Experimental
+public class ProtobufRegion<K, V> implements Region<K, V> {
+  /**
+   * String that uniquely identifies the region.
+   */
+  final String name;
+
+  /**
+   * Socket to a GemFire server that has Protobuf enabled.
+   */
+  final Socket socket;
+
+  /**
+   * Creates a region implementation for the region <code>name</code> that 
communicates via
+   * <code>socket</code> to a GemFire server.
+   *
+   * @param name String that uniquely identifies the region.
+   * @param socket Socket to a GemFire server that has Protobuf enabled.
+   */
+  ProtobufRegion(String name, Socket socket) {
+    this.name = name;
+    this.socket = socket;
+  }
+
+  /**
+   * Captures a snapshot of the attributes (e.g., size) of this region.
+   *
+   * @return Attributes associated with this region.
+   * @throws IOException
+   */
+  @Override
+  public RegionAttributes getRegionAttributes() throws IOException {
+    final OutputStream outputStream = socket.getOutputStream();
+    ClientProtocol.Message.newBuilder()
+        .setRequest(ClientProtocol.Request.newBuilder()
+            
.setGetRegionRequest(RegionAPI.GetRegionRequest.newBuilder().setRegionName(name)))
+        .build().writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    return new 
RegionAttributes(ClientProtocol.Message.parseDelimitedFrom(inputStream).getResponse()
+        .getGetRegionResponse().getRegion());
+  }
+
+  /**
+   * Gets the value, if any, contained in this region for the <code>key</code>.
+   *
+   * @param key Unique key associated with a value.
+   * @return Value, if any, associated with <code>key</code>.
+   * @throws IOException
+   */
+  @Override
+  public V get(K key) throws IOException {
+    final OutputStream outputStream = socket.getOutputStream();
+    ClientProtocol.Message.newBuilder()
+        
.setRequest(ClientProtocol.Request.newBuilder().setGetRequest(RegionAPI.GetRequest
+            
.newBuilder().setRegionName(name).setKey(ValueEncoder.encodeValue(key))))
+        .build().writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    return (V) 
ValueEncoder.decodeValue(ClientProtocol.Message.parseDelimitedFrom(inputStream)
+        .getResponse().getGetResponse().getResult());
+  }
+
+  /**
+   * Gets the values, if any, contained in this region for the collection of 
<code>keys</code>.
+   *
+   * @param keys Collection of unique keys associated with values.
+   * @return Map from <code>keys</code> to their associated values.
+   * @throws IOException
+   */
+  @Override
+  public Map<K, V> getAll(Collection<K> keys) throws IOException {
+    Map<K, V> values = new HashMap<>();
+
+    final OutputStream outputStream = socket.getOutputStream();
+    RegionAPI.GetAllRequest.Builder getAllRequest = 
RegionAPI.GetAllRequest.newBuilder();
+    getAllRequest.setRegionName(name);
+    for (K key : keys) {
+      getAllRequest.addKey(ValueEncoder.encodeValue(key.toString()));
+    }
+    ClientProtocol.Message.newBuilder()
+        
.setRequest(ClientProtocol.Request.newBuilder().setGetAllRequest(getAllRequest)).build()
+        .writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    final RegionAPI.GetAllResponse getAllResponse =
+        
ClientProtocol.Message.parseDelimitedFrom(inputStream).getResponse().getGetAllResponse();
+    for (BasicTypes.Entry entry : getAllResponse.getEntriesList()) {
+      values.put((K) ValueEncoder.decodeValue(entry.getKey()),
+          (V) ValueEncoder.decodeValue(entry.getValue()));
+    }
+
+    return values;
+  }
+
+  /**
+   * Puts the <code>value</code> into this region for the <code>key</code>.
+   *
+   * @param key Unique key to associate with the <code>value</code>.
+   * @param value Value to associate with the <code>key</code>.
+   * @throws IOException
+   */
+  @Override
+  public void put(K key, V value) throws IOException {
+    final OutputStream outputStream = socket.getOutputStream();
+    ClientProtocol.Message.newBuilder()
+        .setRequest(ClientProtocol.Request.newBuilder()
+            
.setPutRequest(RegionAPI.PutRequest.newBuilder().setRegionName(name)
+                .setEntry(ValueEncoder.encodeEntry(key, value))))
+        .build().writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    
ClientProtocol.Message.parseDelimitedFrom(inputStream).getResponse().getPutResponse();
+  }
+
+  /**
+   * Puts the map from keys to <code>values</code> into this region. If any 
one key/value pair can
+   * not be inserted, the remaining pair insertions will be attempted.
+   *
+   * @param values Map from <code>keys</code> to their associated values.
+   * @throws IOException
+   */
+  @Override
+  public void putAll(Map<K, V> values) throws IOException {
+    final OutputStream outputStream = socket.getOutputStream();
+    RegionAPI.PutAllRequest.Builder putAllRequest = 
RegionAPI.PutAllRequest.newBuilder();
+    putAllRequest.setRegionName(name);
+    for (K key : values.keySet()) {
+      putAllRequest.addEntry(ValueEncoder.encodeEntry(key, values.get(key)));
+    }
+    ClientProtocol.Message.newBuilder()
+        
.setRequest(ClientProtocol.Request.newBuilder().setPutAllRequest(putAllRequest)).build()
+        .writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    final RegionAPI.PutAllResponse putAllResponse =
+        
ClientProtocol.Message.parseDelimitedFrom(inputStream).getResponse().getPutAllResponse();
+    if (0 < putAllResponse.getFailedKeysCount()) {
+      StringBuilder builder = new StringBuilder();
+      for (BasicTypes.KeyedError keyedError : 
putAllResponse.getFailedKeysList()) {
+        if (0 < builder.length()) {
+          builder.append(", ");
+        }
+        
builder.append(ValueEncoder.decodeValue(keyedError.getKey()).toString());
+      }
+      throw new IOException("Unable to put the following keys: " + 
builder.toString());
+    }
+  }
+
+  /**
+   * Removes any value associated with the <code>key</code> from this region.
+   *
+   * @param key Unique key associated with a value.
+   * @throws IOException
+   */
+  @Override
+  public void remove(K key) throws IOException {
+    final OutputStream outputStream = socket.getOutputStream();
+    ClientProtocol.Message.newBuilder()
+        
.setRequest(ClientProtocol.Request.newBuilder().setRemoveRequest(RegionAPI.RemoveRequest
+            
.newBuilder().setRegionName(name).setKey(ValueEncoder.encodeValue(key))))
+        .build().writeDelimitedTo(outputStream);
+
+    final InputStream inputStream = socket.getInputStream();
+    
ClientProtocol.Message.parseDelimitedFrom(inputStream).getResponse().getRemoveResponse();
+  }
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/Region.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/Region.java
new file mode 100644
index 0000000..75c2a94
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/Region.java
@@ -0,0 +1,86 @@
+/*
+ * 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.experimental.driver;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.geode.annotations.Experimental;
+
+/**
+ * Defines the behaviors of a GemFire region. A region is an associative array 
from unique keys to
+ * values. For each key, the region will contain zero or one value.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ *
+ * @param <K> Type of region keys.
+ * @param <V> Type of region values.
+ */
+@Experimental
+public interface Region<K, V> {
+  /**
+   * Captures a snapshot of the attributes (e.g., size) of this region.
+   *
+   * @return Attributes associated with this region.
+   * @throws IOException
+   */
+  RegionAttributes getRegionAttributes() throws IOException;
+
+  /**
+   * Gets the value, if any, contained in this region for the <code>key</code>.
+   *
+   * @param key Unique key associated with a value.
+   * @return Value, if any, associated with <code>key</code>.
+   * @throws IOException
+   */
+  V get(K key) throws IOException;
+
+  /**
+   * Gets the values, if any, contained in this region for the collection of 
<code>keys</code>.
+   *
+   * @param keys Collection of unique keys associated with values.
+   * @return Map from <code>keys</code> to their associated values.
+   * @throws IOException
+   */
+  Map<K, V> getAll(Collection<K> keys) throws IOException;
+
+  /**
+   * Puts the <code>value</code> into this region for the <code>key</code>.
+   *
+   * @param key Unique key to associate with the <code>value</code>.
+   * @param value Value to associate with the <code>key</code>.
+   * @throws IOException
+   */
+  void put(K key, V value) throws IOException;
+
+  /**
+   * Puts the map from keys to <code>values</code> into this region. If any 
one key/value pair can
+   * not be inserted, the remaining pair insertions will be attempted.
+   *
+   * @param values Map from <code>keys</code> to their associated values.
+   * @throws IOException
+   */
+  void putAll(Map<K, V> values) throws IOException;
+
+  /**
+   * Removes any value associated with the <code>key</code> from this region.
+   *
+   * @param key Unique key associated with a value.
+   * @throws IOException
+   */
+  void remove(K key) throws IOException;
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/RegionAttributes.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/RegionAttributes.java
new file mode 100644
index 0000000..fea1021
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/RegionAttributes.java
@@ -0,0 +1,79 @@
+/*
+ * 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.experimental.driver;
+
+import org.apache.geode.annotations.Experimental;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+
+/**
+ * Encapsulates the attributes of a region in a GemFire server.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ */
+@Experimental
+public class RegionAttributes {
+  /**
+   * String that uniquely identifies the region within a GemFire server.
+   */
+  public String name;
+
+  /**
+   * Specifies how a local cache will handle the data for the region.
+   */
+  public String dataPolicy;
+
+  /**
+   * Whether modifications to the region are acknowledged throughout the 
GemFire distributed system.
+   */
+  public String scope;
+
+  /**
+   * String that is the fully-qualified class name of the type of which all 
keys must be instances.
+   * May be <code>null</code>.
+   */
+  public String keyConstraint;
+
+  /**
+   * String that is the fully-qualified class name of the type of which all 
values must be
+   * instances. May be <code>null</code>.
+   */
+  public String valueConstraint;
+
+  /**
+   * Whether the region is persisted to disk.
+   */
+  public boolean persisted;
+
+  /**
+   * Number of key/value pairs in the region.
+   */
+  public long size;
+
+  /**
+   * Creates an encapsulation of region attributes.
+   *
+   * @param region Protobuf encoded region attributes.
+   */
+  public RegionAttributes(BasicTypes.Region region) {
+    this.name = region.getName();
+    this.dataPolicy = region.getDataPolicy();
+    this.scope = region.getScope();
+    this.keyConstraint = region.getKeyConstraint();
+    this.valueConstraint = region.getValueConstraint();
+    this.persisted = region.getPersisted();
+    this.size = region.getSize();
+  }
+}
diff --git 
a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ValueEncoder.java
 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ValueEncoder.java
new file mode 100644
index 0000000..4b99913
--- /dev/null
+++ 
b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ValueEncoder.java
@@ -0,0 +1,112 @@
+/*
+ * 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.experimental.driver;
+
+import com.google.protobuf.ByteString;
+
+import org.apache.geode.annotations.Experimental;
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+
+/**
+ * Encodes and decodes Java objects to and from Protobuf encoded values.
+ *
+ * <strong>This code is an experimental prototype and is presented "as is" 
with no warranty,
+ * suitability, or fitness of purpose implied.</strong>
+ */
+@Experimental
+class ValueEncoder {
+  /**
+   * Encodes a Java object into a Protobuf encoded value.
+   *
+   * @param unencodedValue Java object to encode.
+   * @return Encoded value of the Java object.
+   */
+  static BasicTypes.EncodedValue encodeValue(Object unencodedValue) {
+    BasicTypes.EncodedValue.Builder builder = 
BasicTypes.EncodedValue.newBuilder();
+    if (Integer.class.equals(unencodedValue.getClass())) {
+      builder.setIntResult((Integer) unencodedValue);
+    } else if (Long.class.equals(unencodedValue.getClass())) {
+      builder.setLongResult((Long) unencodedValue);
+    } else if (Short.class.equals(unencodedValue.getClass())) {
+      builder.setShortResult((Short) unencodedValue);
+    } else if (Byte.class.equals(unencodedValue.getClass())) {
+      builder.setByteResult((Byte) unencodedValue);
+    } else if (Double.class.equals(unencodedValue.getClass())) {
+      builder.setDoubleResult((Double) unencodedValue);
+    } else if (Float.class.equals(unencodedValue.getClass())) {
+      builder.setFloatResult((Float) unencodedValue);
+    } else if (byte[].class.equals(unencodedValue.getClass())) {
+      builder.setBinaryResult(ByteString.copyFrom((byte[]) unencodedValue));
+    } else if (Boolean.class.equals(unencodedValue.getClass())) {
+      builder.setBooleanResult((Boolean) unencodedValue);
+    } else if (String.class.equals(unencodedValue.getClass())) {
+      builder.setStringResult((String) unencodedValue);
+    } else {
+      throw new IllegalStateException("We don't know how to handle an object 
of type "
+          + unencodedValue.getClass() + ": " + unencodedValue);
+    }
+
+    return builder.build();
+  }
+
+  /**
+   * Decodes a Protobuf encoded value into a Java object.
+   *
+   * @param encodedValue Encoded value to decode.
+   * @return Decoded Java object.
+   */
+  static Object decodeValue(BasicTypes.EncodedValue encodedValue) {
+    switch (encodedValue.getValueCase()) {
+      case BINARYRESULT:
+        return encodedValue.getBinaryResult().toByteArray();
+      case BOOLEANRESULT:
+        return encodedValue.getBooleanResult();
+      case BYTERESULT:
+        return (byte) encodedValue.getByteResult();
+      case DOUBLERESULT:
+        return encodedValue.getDoubleResult();
+      case FLOATRESULT:
+        return encodedValue.getFloatResult();
+      case INTRESULT:
+        return encodedValue.getIntResult();
+      case LONGRESULT:
+        return encodedValue.getLongResult();
+      case SHORTRESULT:
+        return (short) encodedValue.getShortResult();
+      case STRINGRESULT:
+        return encodedValue.getStringResult();
+      case VALUE_NOT_SET:
+        return null;
+      default:
+        throw new IllegalStateException(
+            "Can't decode a value of type " + encodedValue.getValueCase() + ": 
" + encodedValue);
+    }
+  }
+
+  /**
+   * Encodes a Java object key and a Java object value into a Protobuf encoded 
entry.
+   *
+   * @param unencodedKey Java object key to encode.
+   * @param unencodedValue Java object value to encode.
+   * @return Encoded entry of the Java object key and value.
+   */
+  static BasicTypes.Entry encodeEntry(Object unencodedKey, Object 
unencodedValue) {
+    if (unencodedValue == null) {
+      return 
BasicTypes.Entry.newBuilder().setKey(encodeValue(unencodedKey)).build();
+    }
+    return BasicTypes.Entry.newBuilder().setKey(encodeValue(unencodedKey))
+        .setValue(encodeValue(unencodedValue)).build();
+  }
+}
diff --git 
a/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/RegionIntegrationTest.java
 
b/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/RegionIntegrationTest.java
new file mode 100644
index 0000000..4394ec4
--- /dev/null
+++ 
b/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/RegionIntegrationTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.experimental.driver;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.distributed.Locator;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+
+@Category(IntegrationTest.class)
+public class RegionIntegrationTest {
+  @Rule
+  public RestoreSystemProperties restoreSystemProperties = new 
RestoreSystemProperties();
+
+  public static final String REGION = "region";
+  private int locatorPort;
+  private Locator locator;
+  private CacheServer server;
+  private Cache cache;
+
+  @Before
+  public void createServer() throws IOException {
+    System.setProperty("geode.feature-protobuf-protocol", "true");
+
+    // Create a cache
+    CacheFactory cf = new CacheFactory();
+    cache = cf.create();
+
+    // Start a locator
+    locator = Locator.startLocatorAndDS(0, null, new Properties());
+    locatorPort = locator.getPort();
+
+    // Start a server
+    server = cache.addCacheServer();
+    server.setPort(0);
+    server.start();
+
+    // Create a region
+    cache.createRegionFactory(RegionShortcut.REPLICATE).create(REGION);
+  }
+
+  @After
+  public void cleanup() {
+    locator.stop();
+    cache.close();
+  }
+
+  @Test
+  public void getShouldReturnPutValue() throws Exception {
+    Driver driver = new DriverFactory().addLocator("localhost", 
locatorPort).create();
+    Region region = driver.getRegion("region");
+
+    region.put("key", "value");
+    assertEquals("value", region.get("key"));
+
+    region.remove("key");
+    assertEquals(null, region.get("key"));
+  }
+
+  @Test
+  public void putWithIntegerKey() throws Exception {
+    Driver driver = new DriverFactory().addLocator("localhost", 
locatorPort).create();
+    Region region = driver.getRegion("region");
+    region.put(37, 42);
+    assertEquals(42, region.get(37));
+  }
+
+  @Test
+  public void removeWithIntegerKey() throws Exception {
+    Driver driver = new DriverFactory().addLocator("localhost", 
locatorPort).create();
+    Region region = driver.getRegion("region");
+    region.put(37, 42);
+    region.remove(37);
+    assertEquals(null, region.get(37));
+  }
+}
diff --git 
a/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/ValueEncoderTest.java
 
b/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/ValueEncoderTest.java
new file mode 100644
index 0000000..2afe472
--- /dev/null
+++ 
b/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/ValueEncoderTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.experimental.driver;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.UnsupportedEncodingException;
+
+import com.google.protobuf.ByteString;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.internal.protocol.protobuf.v1.BasicTypes;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class ValueEncoderTest {
+  @Test
+  public void encodeAndDecode() throws Exception {
+    final Object[] objects = {37, (short) 37, (byte) 37, 37L, 37., 37.F, true, 
"hello, world"};
+    for (Object object : objects) {
+      assertEquals(object, 
ValueEncoder.decodeValue(ValueEncoder.encodeValue(object)));
+    }
+
+    final byte[] bytes = new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, 
(byte) 0xEF};
+    assertArrayEquals(bytes, (byte[]) 
ValueEncoder.decodeValue(ValueEncoder.encodeValue(bytes)));
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void cantDecodeJson() throws UnsupportedEncodingException {
+    BasicTypes.EncodedValue.Builder builder = 
BasicTypes.EncodedValue.newBuilder();
+    BasicTypes.CustomEncodedValue.Builder customEncodedValue =
+        
BasicTypes.CustomEncodedValue.newBuilder().setValue(ByteString.copyFrom("hello",
 "UTF-8"));
+    builder.setCustomEncodedValue(customEncodedValue);
+    ValueEncoder.decodeValue(builder.build());
+  }
+}
diff --git a/settings.gradle b/settings.gradle
index a319689..c9a4991 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,6 +39,7 @@ include 'extensions/geode-modules-session-internal'
 include 'extensions/geode-modules-session'
 include 'extensions/geode-modules-assembly'
 include 'geode-protobuf'
+include 'geode-experimental-driver'
 include 'geode-protobuf-messages'
 include 'extensions/session-testing-war'
 include 'geode-concurrency-test'

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to