ONOS-4919: Implement RESTCONF client

Adding code to support RESTCONF protocol as one of
the supported SBI protocols of ONOS. This RESTCONF SBI extends
the current REST SBI protocl and adds some new APIs/functinalities
so that a provider can subscribe/register to an external restconf
server to receive notification stream.

Change-Id: I21bf0d0f0394cf788e066d743b3ade04735fe07e
diff --git a/protocols/restconf/client/api/BUCK b/protocols/restconf/client/api/BUCK
new file mode 100644
index 0000000..c1f3e2c
--- /dev/null
+++ b/protocols/restconf/client/api/BUCK
@@ -0,0 +1,10 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//incubator/api:onos-incubator-api',
+    '//utils/rest:onlab-rest',
+    '//protocols/rest/api:onos-protocols-rest-api',
+]
+
+osgi_jar_with_tests (
+    deps = COMPILE_DEPS,
+)
diff --git a/protocols/restconf/client/api/pom.xml b/protocols/restconf/client/api/pom.xml
new file mode 100644
index 0000000..5925337
--- /dev/null
+++ b/protocols/restconf/client/api/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016-present Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.onosproject</groupId>
+    <artifactId>onos-restconf-client</artifactId>
+    <version>1.8.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <artifactId>onos-restconf-client-api</artifactId>
+  <packaging>bundle</packaging>
+  <description>ONOS RESTCONF southbound plugin API</description>
+  <dependencies>
+      <dependency>
+          <groupId>org.onosproject</groupId>
+          <artifactId>onos-restsb-api</artifactId>
+          <version>${project.version}</version>
+          <type>bundle</type>
+      </dependency>
+  </dependencies>
+</project>
diff --git a/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfNotificationEventListener.java b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfNotificationEventListener.java
new file mode 100644
index 0000000..dc49a72
--- /dev/null
+++ b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfNotificationEventListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.protocol.restconf;
+
+import org.onosproject.net.DeviceId;
+
+/**
+ * Notifies providers about incoming RESTCONF notification events.
+ */
+public interface RestConfNotificationEventListener {
+
+    /**
+     * Handles the notification event.
+     *
+     * @param <T>
+     *
+     * @param deviceId of the restconf device
+     * @param eventJsonString the json string representation of the event
+     */
+    <T> void handleNotificationEvent(DeviceId deviceId, T eventJsonString);
+
+}
diff --git a/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java
new file mode 100644
index 0000000..772aaea
--- /dev/null
+++ b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/RestConfSBController.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.protocol.restconf;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.protocol.http.HttpSBController;
+
+/**
+ * Abstraction of a RESTCONF controller. Serves as a one stop shop for obtaining
+ * RESTCONF southbound devices and (un)register listeners.
+ */
+public interface RestConfSBController extends HttpSBController {
+
+    /**
+     * This method is to be called by whoever is interested to receive
+     * Notifications from a specific device. It does a REST GET request
+     * with specified parameters to the device, and calls the provided
+     * callBackListener upon receiving notifications to notify the requester
+     * about notifications.
+     *
+     *
+     * @param device device to make the request to
+     * @param request url of the request
+     * @param mediaType format to retrieve the content in
+     * @param callBackListener method to call when notifications arrives
+     */
+    void enableNotifications(DeviceId device, String request, String mediaType,
+                          RestConfNotificationEventListener callBackListener);
+
+    /**
+     * Register a listener for notification events that occur to restconf
+     * devices.
+     *
+     * @param deviceId the deviceId
+     * @param listener the listener to notify
+     */
+    void addNotificationListener(DeviceId deviceId,
+                                 RestConfNotificationEventListener listener);
+
+    /**
+     * Unregister the listener for the device.
+     *
+     * @param deviceId the deviceId
+     */
+    void removeNotificationListener(DeviceId deviceId);
+}
diff --git a/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/package-info.java b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/package-info.java
new file mode 100644
index 0000000..15f2308
--- /dev/null
+++ b/protocols/restconf/client/api/src/main/java/org/onosproject/protocol/restconf/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * RESTCONF southbound protocols libraries.
+ */
+package org.onosproject.protocol.restconf;
diff --git a/protocols/restconf/client/ctl/BUCK b/protocols/restconf/client/ctl/BUCK
new file mode 100644
index 0000000..ce0292e
--- /dev/null
+++ b/protocols/restconf/client/ctl/BUCK
@@ -0,0 +1,19 @@
+COMPILE_DEPS = [
+    '//lib:CORE_DEPS',
+    '//lib:jersey-client',
+    '//lib:jersey-common',
+    '//lib:httpclient-osgi',
+    '//lib:httpcore-osgi',
+    '//lib:javax.ws.rs-api',
+    '//lib:hk2-api',
+    '//lib:jersey-guava',
+    '//lib:aopalliance-repackaged',
+    '//lib:javax.inject',
+    '//protocols/restconf/client/api:onos-protocols-restconf-client-api',
+    '//protocols/rest/api:onos-protocols-rest-api',
+]
+
+osgi_jar_with_tests (
+    deps = COMPILE_DEPS,
+)
+
diff --git a/protocols/restconf/client/ctl/pom.xml b/protocols/restconf/client/ctl/pom.xml
new file mode 100644
index 0000000..6878151
--- /dev/null
+++ b/protocols/restconf/client/ctl/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016-present Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.onosproject</groupId>
+    <artifactId>onos-restconf-client</artifactId>
+    <version>1.8.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <artifactId>onos-restconf-client-ctl</artifactId>
+
+  <packaging>bundle</packaging>
+
+  <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-restconf-client-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.core</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient-osgi</artifactId>
+            <version>4.5.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore-osgi</artifactId>
+            <version>4.4.4</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-restsb-api</artifactId>
+            <version>${project.version}</version>
+            <type>bundle</type>
+        </dependency>
+  </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java
new file mode 100644
index 0000000..e5fb377
--- /dev/null
+++ b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.protocol.restconf.ctl;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.glassfish.jersey.client.ChunkedInput;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
+import org.onosproject.protocol.rest.RestSBDevice;
+import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
+import org.onosproject.protocol.restconf.RestConfSBController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The implementation of RestConfSBController.
+ */
+@Component(immediate = true)
+@Service
+public class RestConfSBControllerImpl extends HttpSBControllerImpl
+        implements RestConfSBController {
+
+    private static final Logger log = LoggerFactory
+            .getLogger(RestConfSBControllerImpl.class);
+
+    // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
+    // fully support root resource discovery, ROOT_RESOURCE constant will be
+    // removed and rather the value would get discovered dynamically.
+    private static final String ROOT_RESOURCE = "/onos/restconf";
+
+    private static final String RESOURCE_PATH_PREFIX = "/data/";
+    private static final String NOTIFICATION_PATH_PREFIX = "/data/";
+
+    private Map<DeviceId, RestConfNotificationEventListener>
+                                            restconfNotificationListenerMap = new ConcurrentHashMap<>();
+    private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
+
+    ExecutorService executor = Executors.newCachedThreadPool();
+
+    @Activate
+    public void activate() {
+        log.info("RESTCONF SBI Started");
+    }
+
+    @Deactivate
+    public void deactivate() {
+        log.info("RESTCONF SBI Stopped");
+        executor.shutdown();
+        this.getClientMap().clear();
+        this.getDeviceMap().clear();
+    }
+
+    @Override
+    public Map<DeviceId, RestSBDevice> getDevices() {
+        log.trace("RESTCONF SBI::getDevices");
+        return super.getDevices();
+    }
+
+    @Override
+    public RestSBDevice getDevice(DeviceId deviceInfo) {
+        log.trace("RESTCONF SBI::getDevice with deviceId");
+        return super.getDevice(deviceInfo);
+    }
+
+    @Override
+    public RestSBDevice getDevice(IpAddress ip, int port) {
+        log.trace("RESTCONF SBI::getDevice with ip and port");
+        return super.getDevice(ip, port);
+    }
+
+    @Override
+    public void addDevice(RestSBDevice device) {
+        log.trace("RESTCONF SBI::addDevice");
+        super.addDevice(device);
+    }
+
+    @Override
+    public void removeDevice(DeviceId deviceId) {
+        log.trace("RESTCONF SBI::removeDevice");
+        super.removeDevice(deviceId);
+    }
+
+    @Override
+    public boolean post(DeviceId device, String request, InputStream payload,
+                        String mediaType) {
+        request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+                + request;
+        return super.post(device, request, payload, mediaType);
+    }
+
+    @Override
+    public <T> T post(DeviceId device, String request, InputStream payload,
+                      String mediaType, Class<T> responseClass) {
+        request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+                + request;
+        return super.post(device, request, payload, mediaType, responseClass);
+    }
+
+    @Override
+    public boolean put(DeviceId device, String request, InputStream payload,
+                       String mediaType) {
+        request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+                + request;
+        return super.put(device, request, payload, mediaType);
+    }
+
+    @Override
+    public InputStream get(DeviceId device, String request, String mediaType) {
+        request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+                + request;
+        return super.get(device, request, mediaType);
+    }
+
+    @Override
+    public boolean patch(DeviceId device, String request, InputStream payload,
+                         String mediaType) {
+        request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+                + request;
+        return super.patch(device, request, payload, mediaType);
+    }
+
+    @Override
+    public boolean delete(DeviceId device, String request, InputStream payload,
+                          String mediaType) {
+        request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
+                + request;
+        return super.delete(device, request, payload, mediaType);
+    }
+
+    @Override
+    public void enableNotifications(DeviceId device, String request,
+                                 String mediaType,
+                                 RestConfNotificationEventListener listener) {
+
+        request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
+                + request;
+
+        addNotificationListener(device, listener);
+
+        GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
+                                                           device);
+        runnableTable.put(device, runnable);
+        executor.execute(runnable);
+    }
+
+    public void stopNotifications(DeviceId device) {
+
+        runnableTable.get(device).terminate();
+        runnableTable.remove(device);
+        removeNotificationListener(device);
+        log.debug("Stop sending notifications for device URI: " + device.uri().toString());
+
+    }
+
+    public class GetChunksRunnable implements Runnable {
+        private String request;
+        private String mediaType;
+        private DeviceId device;
+
+        private volatile boolean running = true;
+
+        public void terminate() {
+            running = false;
+        }
+
+        /**
+         * @param request
+         * @param mediaType
+         * @param device
+         */
+        public GetChunksRunnable(String request, String mediaType,
+                                 DeviceId device) {
+            this.request = request;
+            this.mediaType = mediaType;
+            this.device = device;
+        }
+
+        @Override
+        public void run() {
+            WebTarget wt = getWebTarget(device, request);
+            Response clientResp = wt.request(mediaType).get();
+            RestConfNotificationEventListener listener = restconfNotificationListenerMap
+                    .get(device);
+            final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
+                    .readEntity(new GenericType<ChunkedInput<String>>() {
+                    });
+
+            String chunk;
+            // Note that the read() is a blocking operation and the invoking
+            // thread is blocked until a new chunk comes. Jersey implementation
+            // of this IO operation is in a way that it does not respond to
+            // interrupts.
+            while (running) {
+                chunk = chunkedInput.read();
+                if (chunk != null) {
+                    if (running) {
+                        listener.handleNotificationEvent(device, chunk);
+                    } else {
+                        log.trace("the requesting client is no more interested "
+                                + "to receive such notifications.");
+                    }
+                } else {
+                    log.trace("The received notification chunk is null. do not continue any more.");
+                    break;
+                }
+            }
+            log.trace("out of while loop -- end of run");
+        }
+    }
+
+    public String discoverRootResource(DeviceId device) {
+        // FIXME: send a GET command to the device to discover the root resource.
+        // The plan to fix this is for the Ibis release when the RESTCONF server and
+        // the RESTCONF client both support root resource discovery.
+        return ROOT_RESOURCE;
+    }
+
+    @Override
+    public void addNotificationListener(DeviceId deviceId,
+                                        RestConfNotificationEventListener listener) {
+        if (!restconfNotificationListenerMap.containsKey(deviceId)) {
+            this.restconfNotificationListenerMap.put(deviceId, listener);
+        }
+    }
+
+    @Override
+    public void removeNotificationListener(DeviceId deviceId) {
+        this.restconfNotificationListenerMap.remove(deviceId);
+    }
+
+}
diff --git a/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/package-info.java b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/package-info.java
new file mode 100644
index 0000000..3e0043b
--- /dev/null
+++ b/protocols/restconf/client/ctl/src/main/java/org/onosproject/protocol/restconf/ctl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * RESTCONF southbound protocol implementation.
+ */
+package org.onosproject.protocol.restconf.ctl;
diff --git a/protocols/restconf/client/ctl/src/test/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImplTest.java b/protocols/restconf/client/ctl/src/test/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImplTest.java
new file mode 100644
index 0000000..1f7f363
--- /dev/null
+++ b/protocols/restconf/client/ctl/src/test/java/org/onosproject/protocol/restconf/ctl/RestConfSBControllerImplTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.protocol.restconf.ctl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.protocol.rest.DefaultRestSBDevice;
+import org.onosproject.protocol.rest.RestSBDevice;
+
+/**
+ * Basic testing for RestSBController.
+ */
+public class RestConfSBControllerImplTest {
+
+    RestConfSBControllerImpl restConfController;
+
+    RestSBDevice device3;
+
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+
+    @Before
+    public void setUp() {
+        restConfController = new RestConfSBControllerImpl();
+        restConfController.activate();
+        device3 = new DefaultRestSBDevice(IpAddress.valueOf("127.0.0.1"), 8181,
+                                          "", "", "http", null, true);
+        restConfController.addDevice(device3);
+
+    }
+
+    @Test
+    public void basics() {
+        assertTrue("Device3 non added",
+                   restConfController.getDevices().containsValue(device3));
+        assertEquals("Device3 added but with wrong key",
+                     restConfController.getDevices().get(device3.deviceId()),
+                     device3);
+        assertEquals("Incorrect Get Device by ID",
+                     restConfController.getDevice(device3.deviceId()), device3);
+        assertEquals("Incorrect Get Device by IP, Port",
+                     restConfController.getDevice(device3.ip(), device3.port()),
+                     device3);
+        restConfController.removeDevice(device3.deviceId());
+        assertFalse("Device3 not removed",
+                    restConfController.getDevices().containsValue(device3));
+    }
+}
diff --git a/protocols/restconf/client/pom.xml b/protocols/restconf/client/pom.xml
new file mode 100644
index 0000000..84dc398
--- /dev/null
+++ b/protocols/restconf/client/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016-present Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<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.onosproject</groupId>
+    <artifactId>onos-restconf</artifactId>
+    <version>1.8.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <artifactId>onos-restconf-client</artifactId>
+  <packaging>pom</packaging>
+  <dependencies>
+      <dependency>
+          <groupId>org.onosproject</groupId>
+          <artifactId>onos-api</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+  </dependencies>
+  <modules>
+    <module>api</module>
+    <module>ctl</module>
+  </modules>
+  <description>RESTCONF Client Module</description>
+</project>
diff --git a/protocols/restconf/pom.xml b/protocols/restconf/pom.xml
index 6abb229..9bde8d0 100644
--- a/protocols/restconf/pom.xml
+++ b/protocols/restconf/pom.xml
@@ -28,6 +28,7 @@
 
   <modules>
     <module>server</module>
+    <module>client</module>
   </modules>
 
 </project>