atlas-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mad...@apache.org
Subject [6/7] atlas git commit: ATLAS-2179: Split Atlas client library to avoid unnecessary dependencies
Date Wed, 27 Sep 2017 04:25:30 GMT
http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/client-v1/src/test/java/org/apache/atlas/AtlasClientTest.java
----------------------------------------------------------------------
diff --git a/client/client-v1/src/test/java/org/apache/atlas/AtlasClientTest.java b/client/client-v1/src/test/java/org/apache/atlas/AtlasClientTest.java
new file mode 100644
index 0000000..1da6b45
--- /dev/null
+++ b/client/client-v1/src/test/java/org/apache/atlas/AtlasClientTest.java
@@ -0,0 +1,474 @@
+/*
+ * 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.atlas;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import org.apache.atlas.model.legacy.EntityResult;
+import org.apache.atlas.typesystem.Referenceable;
+import org.apache.atlas.typesystem.json.InstanceSerialization;
+import org.apache.commons.configuration.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.codehaus.jettison.json.JSONObject;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.net.ConnectException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+public class AtlasClientTest {
+
+    @Mock
+    private WebResource service;
+    @Mock
+    private WebResource.Builder resourceBuilderMock;
+
+    @Mock
+    private Configuration configuration;
+
+    @Mock
+    private Client client;
+
+    @BeforeMethod
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void shouldVerifyServerIsReady() throws AtlasServiceException {
+        setupRetryParams();
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.VERSION, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        when(response.getEntity(String.class)).thenReturn("{\"Version\":\"version-rrelease\",\"Name\":\"apache-atlas\"," +
+                "\"Description\":\"Metadata Management and Data Governance Platform over Hadoop\"}");
+        when(builder.method(AtlasClient.API_V1.VERSION.getMethod(), ClientResponse.class, null)).thenReturn(response);
+
+        assertTrue(atlasClient.isServerReady());
+    }
+
+    @Test
+    public void testCreateEntity() throws Exception {
+        setupRetryParams();
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.CREATE_ENTITY, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.CREATED.getStatusCode());
+
+        JSONObject jsonResponse = new JSONObject(new EntityResult(Arrays.asList("id"), null, null).toString());
+        when(response.getEntity(String.class)).thenReturn(jsonResponse.toString());
+        when(response.getLength()).thenReturn(jsonResponse.length());
+        String entityJson = InstanceSerialization.toJson(new Referenceable("type"), true);
+        when(builder.method(anyString(), Matchers.<Class>any(), anyString())).thenReturn(response);
+
+        List<String> ids = atlasClient.createEntity(entityJson);
+        assertEquals(ids.size(), 1);
+        assertEquals(ids.get(0), "id");
+    }
+
+    private WebResource.Builder setupBuilder(AtlasClient.API_V1 api, WebResource webResource) {
+        when(webResource.path(api.getPath())).thenReturn(service);
+        return getBuilder(service);
+    }
+
+    @Test
+    public void shouldReturnFalseIfServerIsNotReady() throws AtlasServiceException {
+        setupRetryParams();
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.VERSION, service);
+        when(builder.method(AtlasClient.API_V1.VERSION.getMethod(), ClientResponse.class, null)).thenThrow(
+                new ClientHandlerException());
+        assertFalse(atlasClient.isServerReady());
+    }
+
+    @Test
+    public void shouldReturnFalseIfServiceIsUnavailable() throws AtlasServiceException {
+        setupRetryParams();
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.VERSION, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.SERVICE_UNAVAILABLE.getStatusCode());
+        when(response.getClientResponseStatus()).thenReturn(ClientResponse.Status.SERVICE_UNAVAILABLE);
+
+        when(builder.method(AtlasClient.API_V1.VERSION.getMethod(), ClientResponse.class, null)).thenReturn(response);
+
+        assertFalse(atlasClient.isServerReady());
+    }
+
+    @Test(expectedExceptions = AtlasServiceException.class)
+    public void shouldThrowErrorIfAnyResponseOtherThanServiceUnavailable() throws AtlasServiceException {
+        setupRetryParams();
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.VERSION, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+        when(response.getClientResponseStatus()).thenReturn(ClientResponse.Status.INTERNAL_SERVER_ERROR);
+
+        when(builder.method(AtlasClient.API_V1.VERSION.getMethod(), ClientResponse.class, null)).thenReturn(response);
+
+        atlasClient.isServerReady();
+        fail("Should throw exception");
+    }
+    
+    @Test
+    public void shouldGetAdminStatus() throws AtlasServiceException {
+        setupRetryParams();
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"Active\"}";
+        when(response.getEntity(String.class)).thenReturn(activeStatus);
+        when(response.getLength()).thenReturn(activeStatus.length());
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).thenReturn(response);
+
+//         Fix after AtlasBaseClient
+//        atlasClient.setService();
+
+
+        String status = atlasClient.getAdminStatus();
+        assertEquals(status, "Active");
+    }
+
+    @Test(expectedExceptions = AtlasServiceException.class)
+    public void shouldReturnStatusAsUnknownOnException() throws AtlasServiceException {
+        setupRetryParams();
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
+        when(response.getClientResponseStatus()).thenReturn(ClientResponse.Status.INTERNAL_SERVER_ERROR);
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).thenReturn(response);
+
+        String status = atlasClient.getAdminStatus();
+        fail("Should fail with AtlasServiceException");
+    }
+
+    @Test
+    public void shouldReturnStatusAsUnknownIfJSONIsInvalid() throws AtlasServiceException {
+        setupRetryParams();
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        when(response.getEntity(String.class)).thenReturn("{\"status\":\"Active\"}");
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).thenReturn(response);
+
+        String status = atlasClient.getAdminStatus();
+        assertEquals(status, AtlasClient.UNKNOWN_STATUS);
+    }
+    
+    @Test
+    public void shouldReturnBaseURLAsPassedInURL() {
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        String serviceURL = atlasClient.determineActiveServiceURL(new String[]{"http://localhost:21000"}, client);
+        assertEquals(serviceURL, "http://localhost:21000");
+    }
+
+    @Test
+    public void shouldSelectActiveAmongMultipleServersIfHAIsEnabled() {
+        setupRetryParams();
+
+        when(client.resource(UriBuilder.fromUri("http://localhost:31000").build())).thenReturn(service);
+        when(client.resource(UriBuilder.fromUri("http://localhost:41000").build())).thenReturn(service);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse firstResponse = mock(ClientResponse.class);
+        when(firstResponse.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String passiveStatus = "{\"Status\":\"PASSIVE\"}";
+        when(firstResponse.getEntity(String.class)).thenReturn(passiveStatus);
+        when(firstResponse.getLength()).thenReturn(passiveStatus.length());
+        ClientResponse secondResponse = mock(ClientResponse.class);
+        when(secondResponse.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"ACTIVE\"}";
+        when(secondResponse.getEntity(String.class)).thenReturn(activeStatus);
+        when(secondResponse.getLength()).thenReturn(activeStatus.length());
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).
+                thenReturn(firstResponse).thenReturn(firstResponse).thenReturn(firstResponse).
+                thenReturn(secondResponse);
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        String serviceURL = atlasClient.determineActiveServiceURL(
+                new String[]{"http://localhost:31000", "http://localhost:41000"},
+                client);
+        assertEquals(serviceURL, "http://localhost:41000");
+    }
+
+    @Test
+    public void shouldRetryUntilServiceBecomesActive() {
+        setupRetryParams();
+
+        when(client.resource(UriBuilder.fromUri("http://localhost:31000").build())).thenReturn(service);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        when(response.getEntity(String.class)).thenReturn("{\"Status\":\"BECOMING_ACTIVE\"}");
+        ClientResponse nextResponse = mock(ClientResponse.class);
+        when(nextResponse.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"ACTIVE\"}";
+        when(response.getEntity(String.class)).thenReturn(activeStatus);
+        when(response.getLength()).thenReturn(activeStatus.length());
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).
+                thenReturn(response).thenReturn(response).thenReturn(nextResponse);
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        String serviceURL = atlasClient.determineActiveServiceURL(
+                new String[] {"http://localhost:31000","http://localhost:41000"},
+                client);
+        assertEquals(serviceURL, "http://localhost:31000");
+    }
+
+    @Test
+    public void shouldRetryIfCannotConnectToServiceInitially() {
+        setupRetryParams();
+
+        when(client.resource(UriBuilder.fromUri("http://localhost:31000").build())).thenReturn(service);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        when(response.getEntity(String.class)).thenReturn("{\"Status\":\"BECOMING_ACTIVE\"}");
+        ClientResponse nextResponse = mock(ClientResponse.class);
+        when(nextResponse.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"ACTIVE\"}";
+        when(response.getEntity(String.class)).thenReturn(activeStatus);
+        when(response.getLength()).thenReturn(activeStatus.length());
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).
+                thenThrow(new ClientHandlerException("Simulating connection exception")).
+                thenReturn(response).
+                thenReturn(nextResponse);
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+        atlasClient.setService(service);
+        atlasClient.setConfiguration(configuration);
+
+        String serviceURL = atlasClient.determineActiveServiceURL(
+                new String[] {"http://localhost:31000","http://localhost:41000"},
+                client);
+        assertEquals(serviceURL, "http://localhost:31000");
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void shouldThrowExceptionIfActiveServerIsNotFound() {
+        setupRetryParams();
+
+        when(client.resource(UriBuilder.fromUri("http://localhost:31000").build())).thenReturn(service);
+        WebResource.Builder builder = setupBuilder(AtlasClient.API_V1.STATUS, service);
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        when(response.getEntity(String.class)).thenReturn("{\"Status\":\"BECOMING_ACTIVE\"}");
+        when(builder.method(AtlasClient.API_V1.STATUS.getMethod(), ClientResponse.class, null)).
+                thenThrow(new ClientHandlerException("Simulating connection exception")).
+                thenReturn(response).
+                thenReturn(response);
+
+        AtlasClient atlasClient = new AtlasClient(service, configuration);
+
+        String serviceURL = atlasClient.determineActiveServiceURL(
+                new String[] {"http://localhost:31000","http://localhost:41000"},
+                client);
+        assertNull(serviceURL);
+    }
+
+    @Test
+    public void shouldRetryAPICallsOnClientHandlerException() throws AtlasServiceException, URISyntaxException {
+        setupRetryParams();
+
+        ResourceCreator resourceCreator = mock(ResourceCreator.class);
+        WebResource resourceObject = mock(WebResource.class);
+        when(resourceObject.getURI()).
+                thenReturn(new URI("http://localhost:31000/api/atlas/types")).
+                thenReturn(new URI("http://localhost:41000/api/atlas/types")).
+                thenReturn(new URI("http://localhost:41000/api/atlas/types"));
+
+        WebResource.Builder builder = getBuilder(resourceObject);
+
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"ACTIVE\"}";
+        when(response.getEntity(String.class)).thenReturn(activeStatus);
+        when(response.getLength()).thenReturn(activeStatus.length());
+
+        when(builder.method(AtlasClient.API_V1.LIST_TYPES.getMethod(), ClientResponse.class, null)).
+                thenThrow(new ClientHandlerException("simulating exception in calling API", new ConnectException())).
+                thenReturn(response);
+
+        when(resourceCreator.createResource()).thenReturn(resourceObject);
+
+        AtlasClient atlasClient = getClientForTest("http://localhost:31000","http://localhost:41000");
+
+        atlasClient.setService(service);
+        atlasClient.setConfiguration(configuration);
+
+        atlasClient.callAPIWithRetries(AtlasClient.API_V1.LIST_TYPES, null, resourceCreator);
+
+        verify(client).destroy();
+        verify(client).resource(UriBuilder.fromUri("http://localhost:31000").build());
+        verify(client).resource(UriBuilder.fromUri("http://localhost:41000").build());
+    }
+
+    @Test
+    public void shouldRetryWithSameClientIfSingleAddressIsUsed() throws URISyntaxException, AtlasServiceException {
+        setupRetryParams();
+
+        ResourceCreator resourceCreator = mock(ResourceCreator.class);
+        WebResource resourceObject = mock(WebResource.class);
+        when(resourceObject.getURI()).
+                thenReturn(new URI("http://localhost:31000/api/atlas/types"));
+
+        WebResource.Builder builder = getBuilder(resourceObject);
+
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"ACTIVE\"}";
+        when(response.getEntity(String.class)).thenReturn(activeStatus);
+        when(response.getLength()).thenReturn(activeStatus.length());
+
+        when(builder.method(AtlasClient.API_V1.LIST_TYPES.getMethod(), ClientResponse.class, null)).
+                thenThrow(new ClientHandlerException("simulating exception in calling API", new ConnectException())).
+                thenReturn(response);
+
+        when(resourceCreator.createResource()).thenReturn(resourceObject);
+        when(configuration.getString("atlas.http.authentication.type", "simple")).thenReturn("simple");
+
+        AtlasClient atlasClient = getClientForTest("http://localhost:31000");
+
+        atlasClient.setService(resourceObject);
+        atlasClient.setConfiguration(configuration);
+
+        atlasClient.callAPIWithRetries(AtlasClient.API_V1.LIST_TYPES, null, resourceCreator);
+
+        verify(client).destroy();
+        verify(client, times(2)).resource(UriBuilder.fromUri("http://localhost:31000").build());
+    }
+
+    @Test
+    public void shouldRetryAPICallsOnServiceUnavailable() throws AtlasServiceException, URISyntaxException {
+        setupRetryParams();
+
+        ResourceCreator resourceCreator = mock(ResourceCreator.class);
+        WebResource resourceObject = mock(WebResource.class);
+        when(resourceObject.getURI()).
+                thenReturn(new URI("http://localhost:31000/api/atlas/types")).
+                thenReturn(new URI("http://localhost:41000/api/atlas/types")).
+                thenReturn(new URI("http://localhost:41000/api/atlas/types"));
+
+        WebResource.Builder builder = getBuilder(resourceObject);
+
+        ClientResponse firstResponse = mock(ClientResponse.class);
+        when(firstResponse.getStatus()).thenReturn(Response.Status.SERVICE_UNAVAILABLE.getStatusCode());
+        when(firstResponse.getClientResponseStatus()).thenReturn(ClientResponse.Status.SERVICE_UNAVAILABLE);
+
+        ClientResponse response = mock(ClientResponse.class);
+        when(response.getStatus()).thenReturn(Response.Status.OK.getStatusCode());
+        String activeStatus = "{\"Status\":\"ACTIVE\"}";
+        when(response.getEntity(String.class)).thenReturn(activeStatus);
+        when(response.getLength()).thenReturn(activeStatus.length());
+
+        when(builder.method(AtlasClient.API_V1.LIST_TYPES.getMethod(), ClientResponse.class, null)).
+                thenThrow(new ClientHandlerException("simulating exception in calling API", new ConnectException())).
+                thenReturn(firstResponse).
+                thenReturn(response);
+
+        when(resourceCreator.createResource()).thenReturn(resourceObject);
+
+        AtlasClient atlasClient = getClientForTest("http://localhost:31000","http://localhost:41000");
+        atlasClient.setService(resourceObject);
+        atlasClient.setConfiguration(configuration);
+
+        atlasClient.callAPIWithRetries(AtlasClient.API_V1.LIST_TYPES, null, resourceCreator);
+
+
+        verify(client).destroy();
+        verify(client).resource(UriBuilder.fromUri("http://localhost:31000").build());
+        verify(client).resource(UriBuilder.fromUri("http://localhost:41000").build());
+    }
+
+    private WebResource.Builder getBuilder(WebResource resourceObject) {
+        when(resourceObject.getRequestBuilder()).thenReturn(resourceBuilderMock);
+        when(resourceObject.path(anyString())).thenReturn(resourceObject);
+        when(resourceBuilderMock.accept(AtlasBaseClient.JSON_MEDIA_TYPE)).thenReturn(resourceBuilderMock);
+        when(resourceBuilderMock.type(AtlasBaseClient.JSON_MEDIA_TYPE)).thenReturn(resourceBuilderMock);
+        return resourceBuilderMock;
+    }
+
+    private void setupRetryParams() {
+        when(configuration.getInt(AtlasClient.ATLAS_CLIENT_HA_RETRIES_KEY, AtlasClient.DEFAULT_NUM_RETRIES)).
+                thenReturn(3);
+        when(configuration.getInt(AtlasClient.ATLAS_CLIENT_HA_SLEEP_INTERVAL_MS_KEY,
+                AtlasClient.DEFAULT_SLEEP_BETWEEN_RETRIES_MS)).
+                thenReturn(1);
+    }
+
+    private AtlasClient getClientForTest(final String... baseUrls) {
+        return new AtlasClient((UserGroupInformation)null, (String)null, baseUrls) {
+            boolean firstCall = true;
+            @Override
+            protected String determineActiveServiceURL(String[] baseUrls, Client client) {
+                String returnUrl = baseUrls[0];
+                if (baseUrls.length > 1 && !firstCall) {
+                    returnUrl = baseUrls[1];
+                }
+                firstCall = false;
+                return returnUrl;
+            }
+
+            @Override
+            protected Configuration getClientProperties() {
+                return configuration;
+            }
+
+            @Override
+            protected Client getClient(Configuration configuration, UserGroupInformation ugi, String doAsUser) {
+                return client;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/client-v2/pom.xml
----------------------------------------------------------------------
diff --git a/client/client-v2/pom.xml b/client/client-v2/pom.xml
new file mode 100644
index 0000000..66013f5
--- /dev/null
+++ b/client/client-v2/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<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">
+    <parent>
+        <artifactId>atlas-client</artifactId>
+        <groupId>org.apache.atlas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>atlas-client-v2</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.atlas</groupId>
+            <artifactId>atlas-intg</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.atlas</groupId>
+            <artifactId>atlas-client-common</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
----------------------------------------------------------------------
diff --git a/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java b/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
new file mode 100644
index 0000000..24a3ef6
--- /dev/null
+++ b/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
@@ -0,0 +1,460 @@
+/**
+ * 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.atlas;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.apache.atlas.model.SearchFilter;
+import org.apache.atlas.model.discovery.AtlasSearchResult;
+import org.apache.atlas.model.discovery.SearchParameters;
+import org.apache.atlas.model.instance.AtlasClassification;
+import org.apache.atlas.model.instance.AtlasClassification.AtlasClassifications;
+import org.apache.atlas.model.instance.AtlasEntity.AtlasEntitiesWithExtInfo;
+import org.apache.atlas.model.instance.AtlasEntity.AtlasEntityWithExtInfo;
+import org.apache.atlas.model.instance.EntityMutationResponse;
+import org.apache.atlas.model.lineage.AtlasLineageInfo;
+import org.apache.atlas.model.lineage.AtlasLineageInfo.LineageDirection;
+import org.apache.atlas.model.typedef.AtlasClassificationDef;
+import org.apache.atlas.model.typedef.AtlasEntityDef;
+import org.apache.atlas.model.typedef.AtlasEnumDef;
+import org.apache.atlas.model.typedef.AtlasStructDef;
+import org.apache.atlas.model.typedef.AtlasTypesDef;
+import org.apache.atlas.type.AtlasType;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.configuration.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.List;
+import java.util.Map;
+
+public class AtlasClientV2 extends AtlasBaseClient {
+    // Type APIs
+    public static final  String TYPES_API            = BASE_URI + "v2/types/";
+    // Entity APIs
+    public static final  String ENTITY_API           = BASE_URI + "v2/entity/";
+    private static final String PREFIX_ATTR          = "attr:";
+    private static final String TYPEDEFS_API         = TYPES_API + "typedefs/";
+    private static final String TYPEDEF_BY_NAME      = TYPES_API + "typedef/name/";
+    private static final String TYPEDEF_BY_GUID      = TYPES_API + "typedef/guid/";
+    private static final String GET_BY_NAME_TEMPLATE = TYPES_API + "%s/name/%s";
+    private static final String GET_BY_GUID_TEMPLATE = TYPES_API + "%s/guid/%s";
+    private static final String ENTITY_BULK_API      = ENTITY_API + "bulk/";
+    // Lineage APIs
+    private static final String LINEAGE_URI  = BASE_URI + "v2/lineage/";
+
+    // Discovery APIs
+    private static final String DISCOVERY_URI      = BASE_URI + "v2/search";
+    private static final String DSL_URI            = DISCOVERY_URI + "/dsl";
+    private static final String FULL_TEXT_URI      = DISCOVERY_URI + "/fulltext";
+    private static final String BASIC_SEARCH_URI   = DISCOVERY_URI + "/basic";
+    private static final String FACETED_SEARCH_URI = BASIC_SEARCH_URI;
+
+    public AtlasClientV2(String[] baseUrl, String[] basicAuthUserNamePassword) {
+        super(baseUrl, basicAuthUserNamePassword);
+    }
+
+    public AtlasClientV2(String... baseUrls) throws AtlasException {
+        super(baseUrls);
+    }
+
+    public AtlasClientV2(UserGroupInformation ugi, String doAsUser, String... baseUrls) {
+        super(ugi, doAsUser, baseUrls);
+    }
+
+    /**
+     * Constructor for AtlasClient with cookie params as header
+     * @param baseUrl
+     * @param cookieName
+     * @param value
+     * @param path
+     * @param domain
+     */
+    public AtlasClientV2(String[] baseUrl, String cookieName, String value, String path, String domain) {
+        super(baseUrl, new Cookie(cookieName, value, path, domain));
+    }
+
+    /**
+     * Constructor for AtlasClient with cookie as header
+     * @param baseUrl
+     * @param cookie
+     */
+    public AtlasClientV2(String[] baseUrl, Cookie cookie) {
+        super(baseUrl, cookie);
+    }
+
+    @VisibleForTesting
+    AtlasClientV2(WebResource service, Configuration configuration) {
+        super(service, configuration);
+    }
+
+    /**
+     * Bulk retrieval API for retrieving all type definitions in Atlas
+     *
+     * @return A composite wrapper object with lists of all type definitions
+     */
+    public AtlasTypesDef getAllTypeDefs(SearchFilter searchFilter) throws AtlasServiceException {
+        return callAPI(API_V2.GET_ALL_TYPE_DEFS, AtlasTypesDef.class, searchFilter.getParams());
+    }
+
+    public boolean typeWithGuidExists(String guid) {
+        try {
+            callAPI(API_V2.GET_TYPEDEF_BY_GUID, String.class, null, guid);
+        } catch (AtlasServiceException e) {
+            return false;
+        }
+        return true;
+    }
+
+    public boolean typeWithNameExists(String name) {
+        try {
+            callAPI(API_V2.GET_TYPEDEF_BY_NAME, String.class, null, name);
+        } catch (AtlasServiceException e) {
+            return false;
+        }
+        return true;
+    }
+
+    public AtlasEnumDef getEnumDefByName(final String name) throws AtlasServiceException {
+        return getTypeDefByName(name, AtlasEnumDef.class);
+    }
+
+    public AtlasEnumDef getEnumDefByGuid(final String guid) throws AtlasServiceException {
+        return getTypeDefByGuid(guid, AtlasEnumDef.class);
+    }
+
+    public AtlasStructDef getStructDefByName(final String name) throws AtlasServiceException {
+        return getTypeDefByName(name, AtlasStructDef.class);
+    }
+
+    public AtlasStructDef getStructDefByGuid(final String guid) throws AtlasServiceException {
+        return getTypeDefByGuid(guid, AtlasStructDef.class);
+    }
+
+    public AtlasClassificationDef getClassificationDefByName(final String name) throws AtlasServiceException {
+        return getTypeDefByName(name, AtlasClassificationDef.class);
+    }
+
+    public AtlasClassificationDef getClassificationDefByGuid(final String guid) throws AtlasServiceException {
+        return getTypeDefByGuid(guid, AtlasClassificationDef.class);
+    }
+
+    public AtlasEntityDef getEntityDefByName(final String name) throws AtlasServiceException {
+        return getTypeDefByName(name, AtlasEntityDef.class);
+    }
+
+    public AtlasEntityDef getEntityDefByGuid(final String guid) throws AtlasServiceException {
+        return getTypeDefByGuid(guid, AtlasEntityDef.class);
+    }
+
+    @Deprecated
+    public AtlasEnumDef createEnumDef(AtlasEnumDef enumDef) throws AtlasServiceException {
+        AtlasTypesDef atlasTypesDef = new AtlasTypesDef();
+        atlasTypesDef.getEnumDefs().add(enumDef);
+        AtlasTypesDef created = createAtlasTypeDefs(atlasTypesDef);
+        assert created != null;
+        assert created.getEnumDefs() != null;
+        return created.getEnumDefs().get(0);
+    }
+
+    @Deprecated
+    public AtlasStructDef createStructDef(AtlasStructDef structDef) throws AtlasServiceException {
+        AtlasTypesDef atlasTypesDef = new AtlasTypesDef();
+        atlasTypesDef.getStructDefs().add(structDef);
+        AtlasTypesDef created = createAtlasTypeDefs(atlasTypesDef);
+        assert created != null;
+        assert created.getStructDefs() != null;
+        return created.getStructDefs().get(0);
+    }
+
+    @Deprecated
+    public AtlasEntityDef createEntityDef(AtlasEntityDef entityDef) throws AtlasServiceException {
+        AtlasTypesDef atlasTypesDef = new AtlasTypesDef();
+        atlasTypesDef.getEntityDefs().add(entityDef);
+        AtlasTypesDef created = createAtlasTypeDefs(atlasTypesDef);
+        assert created != null;
+        assert created.getEntityDefs() != null;
+        return created.getEntityDefs().get(0);
+    }
+
+    @Deprecated
+    public AtlasClassificationDef createClassificationDef(AtlasClassificationDef classificationDef)
+            throws AtlasServiceException {
+        AtlasTypesDef atlasTypesDef = new AtlasTypesDef();
+        atlasTypesDef.getClassificationDefs().add(classificationDef);
+        AtlasTypesDef created = createAtlasTypeDefs(atlasTypesDef);
+        assert created != null;
+        assert created.getClassificationDefs() != null;
+        return created.getClassificationDefs().get(0);
+    }
+
+    /**
+     * Bulk create APIs for all atlas type definitions, only new definitions will be created.
+     * Any changes to the existing definitions will be discarded
+     *
+     * @param typesDef A composite wrapper object with corresponding lists of the type definition
+     * @return A composite wrapper object with lists of type definitions that were successfully
+     * created
+     */
+    public AtlasTypesDef createAtlasTypeDefs(final AtlasTypesDef typesDef) throws AtlasServiceException {
+        return callAPI(API_V2.CREATE_ALL_TYPE_DEFS, AtlasTypesDef.class, AtlasType.toJson(typesDef));
+    }
+
+    /**
+     * Bulk update API for all types, changes detected in the type definitions would be persisted
+     *
+     * @param typesDef A composite object that captures all type definition changes
+     * @return A composite object with lists of type definitions that were updated
+     */
+    public AtlasTypesDef updateAtlasTypeDefs(final AtlasTypesDef typesDef) throws AtlasServiceException {
+        return callAPI(API_V2.UPDATE_ALL_TYPE_DEFS, AtlasTypesDef.class, AtlasType.toJson(typesDef));
+    }
+
+    /**
+     * Bulk delete API for all types
+     *
+     * @param typesDef A composite object that captures all types to be deleted
+     */
+    public void deleteAtlasTypeDefs(final AtlasTypesDef typesDef) throws AtlasServiceException {
+        callAPI(API_V2.DELETE_ALL_TYPE_DEFS, (Class<?>)null, AtlasType.toJson(typesDef));
+    }
+
+    public AtlasLineageInfo getLineageInfo(final String guid, final LineageDirection direction, final int depth) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+        queryParams.add("direction", direction.toString());
+        queryParams.add("depth", String.valueOf(depth));
+
+        return callAPI(API_V2.LINEAGE_INFO, AtlasLineageInfo.class, queryParams, guid);
+    }
+
+    public AtlasEntityWithExtInfo getEntityByGuid(String guid) throws AtlasServiceException {
+        return callAPI(API_V2.GET_ENTITY_BY_GUID, AtlasEntityWithExtInfo.class, (MultivaluedMap<String, String>) null, guid);
+    }
+
+    public AtlasEntityWithExtInfo getEntityByAttribute(String type, Map<String, String> attributes) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = attributesToQueryParams(attributes);
+
+        return callAPI(API_V2.GET_ENTITY_BY_ATTRIBUTE, AtlasEntityWithExtInfo.class, queryParams, type);
+    }
+
+    public EntityMutationResponse updateEntityByAttribute(String type, Map<String, String> attributes, AtlasEntityWithExtInfo entityInfo)
+            throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = attributesToQueryParams(attributes);
+
+        return callAPI(API_V2.UPDATE_ENTITY_BY_ATTRIBUTE, EntityMutationResponse.class, entityInfo, queryParams, type);
+    }
+
+    /* Lineage Calls  */
+
+    public EntityMutationResponse deleteEntityByAttribute(String type, Map<String, String> attributes) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = attributesToQueryParams(attributes);
+
+        return callAPI(API_V2.DELETE_ENTITY_BY_ATTRIBUTE, EntityMutationResponse.class, queryParams, type);
+    }
+
+    /* Entity Calls */
+
+    public EntityMutationResponse createEntity(AtlasEntityWithExtInfo entity) throws AtlasServiceException {
+        return callAPI(API_V2.CREATE_ENTITY, EntityMutationResponse.class, entity);
+    }
+
+    public EntityMutationResponse updateEntity(AtlasEntityWithExtInfo entity) throws AtlasServiceException {
+        return callAPI(API_V2.UPDATE_ENTITY, EntityMutationResponse.class, entity);
+    }
+
+    public EntityMutationResponse deleteEntityByGuid(String guid) throws AtlasServiceException {
+        return callAPI(API_V2.DELETE_ENTITY_BY_GUID, EntityMutationResponse.class, null, guid);
+    }
+
+    public AtlasEntitiesWithExtInfo getEntitiesByGuids(List<String> guids) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+
+        queryParams.put("guid", guids);
+
+        return callAPI(API_V2.GET_ENTITIES_BY_GUIDS, AtlasEntitiesWithExtInfo.class, queryParams);
+    }
+
+    public EntityMutationResponse createEntities(AtlasEntitiesWithExtInfo atlasEntities) throws AtlasServiceException {
+        return callAPI(API_V2.CREATE_ENTITIES, EntityMutationResponse.class, atlasEntities);
+    }
+
+    public EntityMutationResponse updateEntities(AtlasEntitiesWithExtInfo atlasEntities) throws AtlasServiceException {
+        return callAPI(API_V2.UPDATE_ENTITIES, EntityMutationResponse.class, atlasEntities);
+    }
+
+    public EntityMutationResponse deleteEntitiesByGuids(List<String> guids) throws AtlasServiceException {
+        return callAPI(API_V2.DELETE_ENTITIES_BY_GUIDS, EntityMutationResponse.class, "guid", guids);
+    }
+
+    public AtlasClassifications getClassifications(String guid) throws AtlasServiceException {
+        return callAPI(formatPathParameters(API_V2.GET_CLASSIFICATIONS, guid), AtlasClassifications.class, null);
+    }
+
+    public void addClassifications(String guid, List<AtlasClassification> classifications) throws AtlasServiceException {
+        callAPI(formatPathParameters(API_V2.ADD_CLASSIFICATIONS, guid), (Class<?>)null, classifications, (String[]) null);
+    }
+
+    public void updateClassifications(String guid, List<AtlasClassification> classifications) throws AtlasServiceException {
+        callAPI(formatPathParameters(API_V2.UPDATE_CLASSIFICATIONS, guid), AtlasClassifications.class, classifications);
+    }
+
+    public void deleteClassifications(String guid, List<AtlasClassification> classifications) throws AtlasServiceException {
+        callAPI(formatPathParameters(API_V2.GET_CLASSIFICATIONS, guid), AtlasClassifications.class, classifications);
+    }
+
+    public void deleteClassification(String guid, String classificationName) throws AtlasServiceException {
+        callAPI(formatPathParameters(API_V2.DELETE_CLASSIFICATION, guid, classificationName), null, null);
+    }
+
+    /* Discovery calls */
+    public AtlasSearchResult dslSearch(final String query) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+        queryParams.add(QUERY, query);
+
+        return callAPI(API_V2.DSL_SEARCH, AtlasSearchResult.class, queryParams);
+    }
+
+    public AtlasSearchResult dslSearchWithParams(final String query, final int limit, final int offset) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+        queryParams.add(QUERY, query);
+        queryParams.add(LIMIT, String.valueOf(limit));
+        queryParams.add(OFFSET, String.valueOf(offset));
+
+        return callAPI(API_V2.DSL_SEARCH, AtlasSearchResult.class, queryParams);
+    }
+
+    public AtlasSearchResult fullTextSearch(final String query) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+        queryParams.add(QUERY, query);
+
+        return callAPI(API_V2.FULL_TEXT_SEARCH, AtlasSearchResult.class, queryParams);
+    }
+
+    public AtlasSearchResult fullTextSearchWithParams(final String query, final int limit, final int offset) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+        queryParams.add(QUERY, query);
+        queryParams.add(LIMIT, String.valueOf(limit));
+        queryParams.add(OFFSET, String.valueOf(offset));
+
+        return callAPI(API_V2.FULL_TEXT_SEARCH, AtlasSearchResult.class, queryParams);
+    }
+
+    public AtlasSearchResult basicSearch(final String typeName, final String classification, final String query,
+                                         final boolean excludeDeletedEntities, final int limit, final int offset) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
+        queryParams.add("typeName", typeName);
+        queryParams.add("classification", classification);
+        queryParams.add(QUERY, query);
+        queryParams.add("excludeDeletedEntities", String.valueOf(excludeDeletedEntities));
+        queryParams.add(LIMIT, String.valueOf(limit));
+        queryParams.add(OFFSET, String.valueOf(offset));
+
+        return callAPI(API_V2.BASIC_SEARCH, AtlasSearchResult.class, queryParams);
+    }
+
+    public AtlasSearchResult facetedSearch(SearchParameters searchParameters) throws AtlasServiceException {
+        return callAPI(API_V2.FACETED_SEARCH, AtlasSearchResult.class, searchParameters);
+    }
+
+    @Override
+    protected API formatPathParameters(final API api, final String... params) {
+        return new API(String.format(api.getPath(), params), api.getMethod(), api.getExpectedStatus());
+    }
+
+    private MultivaluedMap<String, String> attributesToQueryParams(Map<String, String> attributes) {
+        return attributesToQueryParams(attributes, null);
+    }
+
+    private MultivaluedMap<String, String> attributesToQueryParams(Map<String, String>            attributes,
+                                                                   MultivaluedMap<String, String> queryParams) {
+        if (queryParams == null) {
+            queryParams = new MultivaluedMapImpl();
+        }
+
+        if (MapUtils.isNotEmpty(attributes)) {
+            for (Map.Entry<String, String> e : attributes.entrySet()) {
+                queryParams.putSingle(PREFIX_ATTR + e.getKey(), e.getValue());
+            }
+        }
+
+        return queryParams;
+    }
+
+    private <T> T getTypeDefByName(final String name, Class<T> typeDefClass) throws AtlasServiceException {
+        String atlasPath = getAtlasPath(typeDefClass);
+        API    api       = new API(String.format(GET_BY_NAME_TEMPLATE, atlasPath, name), HttpMethod.GET, Response.Status.OK);
+        return callAPI(api, typeDefClass, null);
+    }
+
+    private <T> T getTypeDefByGuid(final String guid, Class<T> typeDefClass) throws AtlasServiceException {
+        String atlasPath = getAtlasPath(typeDefClass);
+        API    api       = new API(String.format(GET_BY_GUID_TEMPLATE, atlasPath, guid), HttpMethod.GET, Response.Status.OK);
+        return callAPI(api, typeDefClass, null);
+    }
+
+    private <T> String getAtlasPath(Class<T> typeDefClass) {
+        if (AtlasEnumDef.class.isAssignableFrom(typeDefClass)) {
+            return "enumdef";
+        } else if (AtlasEntityDef.class.isAssignableFrom(typeDefClass)) {
+            return "entitydef";
+        } else if (AtlasClassificationDef.class.isAssignableFrom(typeDefClass)) {
+            return "classificationdef";
+        } else if (AtlasStructDef.class.isAssignableFrom(typeDefClass)) {
+            return "structdef";
+        }
+        // Code should never reach this point
+        return "";
+    }
+
+    public static class API_V2 extends API {
+        public static final API_V2 GET_TYPEDEF_BY_NAME        = new API_V2(TYPEDEF_BY_NAME, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 GET_TYPEDEF_BY_GUID        = new API_V2(TYPEDEF_BY_GUID, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 GET_ALL_TYPE_DEFS          = new API_V2(TYPEDEFS_API, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 CREATE_ALL_TYPE_DEFS       = new API_V2(TYPEDEFS_API, HttpMethod.POST, Response.Status.OK);
+        public static final API_V2 UPDATE_ALL_TYPE_DEFS       = new API_V2(TYPEDEFS_API, HttpMethod.PUT, Response.Status.OK);
+        public static final API_V2 DELETE_ALL_TYPE_DEFS       = new API_V2(TYPEDEFS_API, HttpMethod.DELETE, Response.Status.NO_CONTENT);
+        public static final API_V2 GET_ENTITY_BY_GUID         = new API_V2(ENTITY_API + "guid/", HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 GET_ENTITY_BY_ATTRIBUTE    = new API_V2(ENTITY_API + "uniqueAttribute/type/", HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 CREATE_ENTITY              = new API_V2(ENTITY_API, HttpMethod.POST, Response.Status.OK);
+        public static final API_V2 UPDATE_ENTITY              = new API_V2(ENTITY_API, HttpMethod.POST, Response.Status.OK);
+        public static final API_V2 UPDATE_ENTITY_BY_ATTRIBUTE = new API_V2(ENTITY_API + "uniqueAttribute/type/", HttpMethod.PUT, Response.Status.OK);
+        public static final API_V2 DELETE_ENTITY_BY_GUID      = new API_V2(ENTITY_API + "guid/", HttpMethod.DELETE, Response.Status.OK);
+        public static final API_V2 DELETE_ENTITY_BY_ATTRIBUTE = new API_V2(ENTITY_API + "uniqueAttribute/type/", HttpMethod.DELETE, Response.Status.OK);
+        public static final API_V2 GET_ENTITIES_BY_GUIDS      = new API_V2(ENTITY_BULK_API, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 CREATE_ENTITIES            = new API_V2(ENTITY_BULK_API, HttpMethod.POST, Response.Status.OK);
+        public static final API_V2 UPDATE_ENTITIES            = new API_V2(ENTITY_BULK_API, HttpMethod.POST, Response.Status.OK);
+        public static final API_V2 DELETE_ENTITIES_BY_GUIDS   = new API_V2(ENTITY_BULK_API, HttpMethod.DELETE, Response.Status.OK);
+        public static final API_V2 GET_CLASSIFICATIONS        = new API_V2(ENTITY_API + "guid/%s/classifications", HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 ADD_CLASSIFICATIONS        = new API_V2(ENTITY_API + "guid/%s/classifications", HttpMethod.POST, Response.Status.NO_CONTENT);
+        public static final API_V2 UPDATE_CLASSIFICATIONS     = new API_V2(ENTITY_API + "guid/%s/classifications", HttpMethod.PUT, Response.Status.OK);
+        public static final API_V2 DELETE_CLASSIFICATION      = new API_V2(ENTITY_API + "guid/%s/classification/%s", HttpMethod.DELETE, Response.Status.NO_CONTENT);
+        public static final API_V2 LINEAGE_INFO               = new API_V2(LINEAGE_URI, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 DSL_SEARCH                 = new API_V2(DSL_URI, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 FULL_TEXT_SEARCH           = new API_V2(FULL_TEXT_URI, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 BASIC_SEARCH               = new API_V2(BASIC_SEARCH_URI, HttpMethod.GET, Response.Status.OK);
+        public static final API_V2 FACETED_SEARCH             = new API_V2(FACETED_SEARCH_URI, HttpMethod.POST, Response.Status.OK);
+
+        private API_V2(String path, String method, Response.Status status) {
+            super(path, method, status);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/common/pom.xml
----------------------------------------------------------------------
diff --git a/client/common/pom.xml b/client/common/pom.xml
new file mode 100644
index 0000000..c6ef0b8
--- /dev/null
+++ b/client/common/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<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">
+    <parent>
+        <artifactId>atlas-client</artifactId>
+        <groupId>org.apache.atlas</groupId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>atlas-client-common</artifactId>
+
+    <dependencies>
+        <!-- supports simple auth handler -->
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-client</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.atlas</groupId>
+            <artifactId>atlas-intg</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
----------------------------------------------------------------------
diff --git a/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java b/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
new file mode 100644
index 0000000..5e1d101
--- /dev/null
+++ b/client/common/src/main/java/org/apache/atlas/AtlasBaseClient.java
@@ -0,0 +1,621 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.atlas;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.GenericType;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+import com.sun.jersey.api.json.JSONConfiguration;
+import com.sun.jersey.client.urlconnection.URLConnectionClientHandler;
+import org.apache.atlas.model.metrics.AtlasMetrics;
+import org.apache.atlas.security.SecureClientUtils;
+import org.apache.atlas.utils.AuthenticationUtil;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.atlas.security.SecurityProperties.TLS_ENABLED;
+
+public abstract class AtlasBaseClient {
+    public static final String BASE_URI = "api/atlas/";
+    public static final String TYPES = "types";
+    public static final String ADMIN_VERSION = "admin/version";
+    public static final String ADMIN_STATUS = "admin/status";
+    public static final String ADMIN_METRICS = "admin/metrics";
+    public static final String HTTP_AUTHENTICATION_ENABLED = "atlas.http.authentication.enabled";
+
+    public static final String QUERY = "query";
+    public static final String LIMIT = "limit";
+    public static final String OFFSET = "offset";
+
+    public static final API API_STATUS  = new API(BASE_URI + ADMIN_STATUS, HttpMethod.GET, Response.Status.OK);;
+    public static final API API_VERSION = new API(BASE_URI + ADMIN_VERSION, HttpMethod.GET, Response.Status.OK);;
+    public static final API API_METRICS = new API(BASE_URI + ADMIN_METRICS, HttpMethod.GET, Response.Status.OK);;
+
+    static final        String JSON_MEDIA_TYPE                       = MediaType.APPLICATION_JSON + "; charset=UTF-8";
+    static final        String UNKNOWN_STATUS                        = "Unknown status";
+    static final        String ATLAS_CLIENT_HA_RETRIES_KEY           = "atlas.client.ha.retries";
+    // Setting the default value based on testing failovers while client code like quickstart is running.
+    static final        int    DEFAULT_NUM_RETRIES                   = 4;
+    static final        String ATLAS_CLIENT_HA_SLEEP_INTERVAL_MS_KEY = "atlas.client.ha.sleep.interval.ms";
+    // Setting the default value based on testing failovers while client code like quickstart is running.
+    // With number of retries, this gives a total time of about 20s for the server to start.
+    static final int DEFAULT_SLEEP_BETWEEN_RETRIES_MS = 5000;
+    private static final Logger LOG = LoggerFactory.getLogger(AtlasBaseClient.class);
+    protected WebResource service;
+    protected Configuration configuration;
+    private String basicAuthUser;
+    private String basicAuthPassword;
+    private AtlasClientContext atlasClientContext;
+    private boolean retryEnabled = false;
+    private Cookie cookie = null;
+
+    protected AtlasBaseClient() {
+    }
+
+    protected AtlasBaseClient(String[] baseUrl, String[] basicAuthUserNamePassword) {
+        if (basicAuthUserNamePassword != null) {
+            if (basicAuthUserNamePassword.length > 0) {
+                this.basicAuthUser = basicAuthUserNamePassword[0];
+            }
+            if (basicAuthUserNamePassword.length > 1) {
+                this.basicAuthPassword = basicAuthUserNamePassword[1];
+            }
+        }
+
+        initializeState(baseUrl, null, null);
+    }
+
+    protected AtlasBaseClient(String... baseUrls) throws AtlasException {
+        this(getCurrentUGI(), baseUrls);
+    }
+
+    protected AtlasBaseClient(UserGroupInformation ugi, String[] baseUrls) {
+        this(ugi, ugi.getShortUserName(), baseUrls);
+    }
+
+    protected AtlasBaseClient(UserGroupInformation ugi, String doAsUser, String[] baseUrls) {
+        initializeState(baseUrls, ugi, doAsUser);
+    }
+
+    protected AtlasBaseClient(String[] baseUrls, Cookie cookie) {
+        this.cookie = cookie;
+        initializeState(baseUrls, null, null);
+    }
+
+    @VisibleForTesting
+    protected AtlasBaseClient(WebResource service, Configuration configuration) {
+        this.service = service;
+        this.configuration = configuration;
+    }
+
+    @VisibleForTesting
+    protected AtlasBaseClient(Configuration configuration, String[] baseUrl, String[] basicAuthUserNamePassword) {
+        if (basicAuthUserNamePassword != null) {
+            if (basicAuthUserNamePassword.length > 0) {
+                this.basicAuthUser = basicAuthUserNamePassword[0];
+            }
+            if (basicAuthUserNamePassword.length > 1) {
+                this.basicAuthPassword = basicAuthUserNamePassword[1];
+            }
+        }
+
+        initializeState(configuration, baseUrl, null, null);
+    }
+
+    protected static UserGroupInformation getCurrentUGI() throws AtlasException {
+        try {
+            return UserGroupInformation.getCurrentUser();
+        } catch (IOException e) {
+            throw new AtlasException(e);
+        }
+    }
+
+    public void setCookie(Cookie cookie) {
+        this.cookie = cookie;
+    }
+
+    public boolean isServerReady() throws AtlasServiceException {
+        WebResource resource   = getResource(API_VERSION.getPath());
+        try {
+            callAPIWithResource(API_VERSION, resource, null, JSONObject.class);
+            return true;
+        } catch (ClientHandlerException che) {
+            return false;
+        } catch (AtlasServiceException ase) {
+            if (ase.getStatus() != null && ase.getStatus().equals(ClientResponse.Status.SERVICE_UNAVAILABLE)) {
+                LOG.warn("Received SERVICE_UNAVAILABLE, server is not yet ready");
+                return false;
+            }
+            throw ase;
+        }
+    }
+
+    /**
+     * Return status of the service instance the client is pointing to.
+     *
+     * @return One of the values in ServiceState.ServiceStateValue or {@link #UNKNOWN_STATUS} if
+     * there is a JSON parse exception
+     * @throws AtlasServiceException if there is a HTTP error.
+     */
+    public String getAdminStatus() throws AtlasServiceException {
+        String      result    = AtlasBaseClient.UNKNOWN_STATUS;
+        WebResource resource  = getResource(service, API_STATUS.getPath());
+        JSONObject  response  = callAPIWithResource(API_STATUS, resource, null, JSONObject.class);
+        try {
+            result = response.getString("Status");
+        } catch (JSONException e) {
+            LOG.error("Exception while parsing admin status response. Returned response {}", response.toString(), e);
+        }
+        return result;
+    }
+
+    /**
+     * @return Return metrics of the service instance the client is pointing to
+     * @throws AtlasServiceException
+     */
+    public AtlasMetrics getAtlasMetrics() throws AtlasServiceException {
+        return callAPI(API_METRICS, AtlasMetrics.class, null);
+    }
+
+    public <T> T callAPI(API api, Class<T> responseType, Object requestObject, String... params)
+            throws AtlasServiceException {
+        return callAPIWithResource(api, getResource(api, params), requestObject, responseType);
+    }
+
+    public <T> T callAPI(API api, GenericType<T> responseType, Object requestObject, String... params)
+            throws AtlasServiceException {
+        return callAPIWithResource(api, getResource(api, params), requestObject, responseType);
+    }
+
+    public <T> T callAPI(API api, Class<T> responseType, Object requestBody,
+                         MultivaluedMap<String, String> queryParams, String... params) throws AtlasServiceException {
+        WebResource resource = getResource(api, queryParams, params);
+        return callAPIWithResource(api, resource, requestBody, responseType);
+    }
+
+    public <T> T callAPI(API api, Class<T> responseType, MultivaluedMap<String, String> queryParams, String... params)
+            throws AtlasServiceException {
+        WebResource resource = getResource(api, queryParams, params);
+        return callAPIWithResource(api, resource, null, responseType);
+    }
+
+    public <T> T callAPI(API api, GenericType<T> responseType, MultivaluedMap<String, String> queryParams, String... params)
+            throws AtlasServiceException {
+        WebResource resource = getResource(api, queryParams, params);
+        return callAPIWithResource(api, resource, null, responseType);
+    }
+
+    public <T> T callAPI(API api, Class<T> responseType, MultivaluedMap<String, String> queryParams)
+            throws AtlasServiceException {
+        return callAPIWithResource(api, getResource(api, queryParams), null, responseType);
+    }
+
+    public <T> T callAPI(API api, Class<T> responseType, String queryParamKey, List<String> queryParamValues)
+            throws AtlasServiceException {
+        return callAPIWithResource(api, getResource(api, queryParamKey, queryParamValues), null, responseType);
+    }
+
+    @VisibleForTesting
+    protected Client getClient(Configuration configuration, UserGroupInformation ugi, String doAsUser) {
+        DefaultClientConfig config = new DefaultClientConfig();
+        // Enable POJO mapping feature
+        config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
+        int readTimeout = configuration.getInt("atlas.client.readTimeoutMSecs", 60000);
+        int connectTimeout = configuration.getInt("atlas.client.connectTimeoutMSecs", 60000);
+        if (configuration.getBoolean(TLS_ENABLED, false)) {
+            // create an SSL properties configuration if one doesn't exist.  SSLFactory expects a file, so forced
+            // to create a
+            // configuration object, persist it, then subsequently pass in an empty configuration to SSLFactory
+            try {
+                SecureClientUtils.persistSSLClientConfiguration(configuration);
+            } catch (Exception e) {
+                LOG.info("Error processing client configuration.", e);
+            }
+        }
+
+        final URLConnectionClientHandler handler;
+
+        if ((AuthenticationUtil.isKerberosAuthenticationEnabled())) {
+            handler = SecureClientUtils.getClientConnectionHandler(config, configuration, doAsUser, ugi);
+        } else {
+            if (configuration.getBoolean(TLS_ENABLED, false)) {
+                handler = SecureClientUtils.getUrlConnectionClientHandler();
+            } else {
+                handler = new URLConnectionClientHandler();
+            }
+        }
+        Client client = new Client(handler, config);
+        client.setReadTimeout(readTimeout);
+        client.setConnectTimeout(connectTimeout);
+        return client;
+    }
+
+    @VisibleForTesting
+    protected String determineActiveServiceURL(String[] baseUrls, Client client) {
+        if (baseUrls.length == 0) {
+            throw new IllegalArgumentException("Base URLs cannot be null or empty");
+        }
+        final String baseUrl;
+        AtlasServerEnsemble atlasServerEnsemble = new AtlasServerEnsemble(baseUrls);
+        if (atlasServerEnsemble.hasSingleInstance()) {
+            baseUrl = atlasServerEnsemble.firstURL();
+            LOG.info("Client has only one service URL, will use that for all actions: {}", baseUrl);
+        } else {
+            try {
+                baseUrl = selectActiveServerAddress(client, atlasServerEnsemble);
+            } catch (AtlasServiceException e) {
+                LOG.error("None of the passed URLs are active: {}", atlasServerEnsemble, e);
+                throw new IllegalArgumentException("None of the passed URLs are active " + atlasServerEnsemble, e);
+            }
+        }
+        return baseUrl;
+    }
+
+    protected Configuration getClientProperties() {
+        try {
+            if (configuration == null) {
+                configuration = ApplicationProperties.get();
+            }
+        } catch (AtlasException e) {
+            LOG.error("Exception while loading configuration.", e);
+        }
+        return configuration;
+    }
+
+    protected WebResource getResource(String path, String... pathParams) {
+        return getResource(service, path, pathParams);
+    }
+
+    protected <T> T callAPIWithResource(API api, WebResource resource, Object requestObject, Class<T> responseType) throws AtlasServiceException {
+        GenericType<T> genericType = null;
+        if (responseType != null) {
+            genericType = new GenericType<>(responseType);
+        }
+        return callAPIWithResource(api, resource, requestObject, genericType);
+    }
+
+    protected <T> T callAPIWithResource(API api, WebResource resource, Object requestObject, GenericType<T> responseType) throws AtlasServiceException {
+        ClientResponse clientResponse = null;
+        int i = 0;
+        do {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Calling API [ {} : {} ] {}", api.getMethod(), api.getPath(), requestObject != null ? "<== " + requestObject : "");
+            }
+
+            WebResource.Builder requestBuilder = resource.getRequestBuilder();
+
+            // Set content headers
+            requestBuilder
+                    .accept(JSON_MEDIA_TYPE)
+                    .type(JSON_MEDIA_TYPE);
+
+            // Set cookie if present
+            if (cookie != null) {
+                requestBuilder.cookie(cookie);
+            }
+
+            clientResponse = requestBuilder.method(api.getMethod(), ClientResponse.class, requestObject);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("API {} returned status {}", resource.getURI(), clientResponse.getStatus());
+            }
+
+            if (clientResponse.getStatus() == api.getExpectedStatus().getStatusCode()) {
+                if (null == responseType) {
+                    return null;
+                }
+                try {
+                    if (responseType.getRawClass() == JSONObject.class) {
+                        String stringEntity = clientResponse.getEntity(String.class);
+                        try {
+                            JSONObject jsonObject = new JSONObject(stringEntity);
+                            LOG.info("Response = {}", jsonObject);
+                            return (T) jsonObject;
+                        } catch (JSONException e) {
+                            throw new AtlasServiceException(api, e);
+                        }
+                    } else {
+                        T entity = clientResponse.getEntity(responseType);
+                        return entity;
+                    }
+                } catch (ClientHandlerException e) {
+                    throw new AtlasServiceException(api, e);
+                }
+            } else if (clientResponse.getStatus() != ClientResponse.Status.SERVICE_UNAVAILABLE.getStatusCode()) {
+                break;
+            } else {
+                LOG.error("Got a service unavailable when calling: {}, will retry..", resource);
+                sleepBetweenRetries();
+            }
+
+            i++;
+        } while (i < getNumberOfRetries());
+
+        throw new AtlasServiceException(api, clientResponse);
+    }
+
+    protected WebResource getResource(API api, String... pathParams) {
+        return getResource(service, api, pathParams);
+    }
+
+    protected WebResource getResource(API api, MultivaluedMap<String, String> queryParams, String... pathParams) {
+        WebResource resource = service.path(api.getPath());
+        resource = appendPathParams(resource, pathParams);
+        resource = appendQueryParams(queryParams, resource);
+        return resource;
+    }
+
+    protected WebResource getResource(API api, MultivaluedMap<String, String> queryParams) {
+        return getResource(service, api, queryParams);
+    }
+
+    protected abstract API formatPathParameters(API api, String ... params);
+
+    void initializeState(String[] baseUrls, UserGroupInformation ugi, String doAsUser) {
+        initializeState(getClientProperties(), baseUrls, ugi, doAsUser);
+    }
+
+    void initializeState(Configuration configuration, String[] baseUrls, UserGroupInformation ugi, String doAsUser) {
+        this.configuration = configuration;
+        Client client = getClient(configuration, ugi, doAsUser);
+
+        if ((!AuthenticationUtil.isKerberosAuthenticationEnabled()) && basicAuthUser != null && basicAuthPassword != null) {
+            final HTTPBasicAuthFilter authFilter = new HTTPBasicAuthFilter(basicAuthUser, basicAuthPassword);
+            client.addFilter(authFilter);
+        }
+
+        String activeServiceUrl = determineActiveServiceURL(baseUrls, client);
+        atlasClientContext = new AtlasClientContext(baseUrls, client, ugi, doAsUser);
+        service = client.resource(UriBuilder.fromUri(activeServiceUrl).build());
+    }
+
+    void sleepBetweenRetries() {
+        try {
+            Thread.sleep(getSleepBetweenRetriesMs());
+        } catch (InterruptedException e) {
+            LOG.error("Interrupted from sleeping between retries.", e);
+        }
+    }
+
+    int getNumberOfRetries() {
+        return configuration.getInt(AtlasBaseClient.ATLAS_CLIENT_HA_RETRIES_KEY, AtlasBaseClient.DEFAULT_NUM_RETRIES);
+    }
+
+    boolean isRetryableException(ClientHandlerException che) {
+        return che.getCause().getClass().equals(IOException.class)
+                || che.getCause().getClass().equals(ConnectException.class);
+    }
+
+    void handleClientHandlerException(ClientHandlerException che) {
+        if (isRetryableException(che)) {
+            atlasClientContext.getClient().destroy();
+            LOG.warn("Destroyed current context while handling ClientHandlerEception.");
+            LOG.warn("Will retry and create new context.");
+            sleepBetweenRetries();
+            initializeState(atlasClientContext.getBaseUrls(), atlasClientContext.getUgi(),
+                    atlasClientContext.getDoAsUser());
+            return;
+        }
+        throw che;
+    }
+
+    @VisibleForTesting
+    JSONObject callAPIWithRetries(API api, Object requestObject, ResourceCreator resourceCreator)
+            throws AtlasServiceException {
+        for (int i = 0; i < getNumberOfRetries(); i++) {
+            WebResource resource = resourceCreator.createResource();
+            try {
+                LOG.debug("Using resource {} for {} times", resource.getURI(), i + 1);
+                return callAPIWithResource(api, resource, requestObject, JSONObject.class);
+            } catch (ClientHandlerException che) {
+                if (i == (getNumberOfRetries() - 1)) {
+                    throw che;
+                }
+                LOG.warn("Handled exception in calling api {}", api.getPath(), che);
+                LOG.warn("Exception's cause: {}", che.getCause().getClass());
+                handleClientHandlerException(che);
+            }
+        }
+        throw new AtlasServiceException(api, new RuntimeException("Could not get response after retries."));
+    }
+
+    @VisibleForTesting
+    void setConfiguration(Configuration configuration) {
+        this.configuration = configuration;
+    }
+
+    @VisibleForTesting
+    void setService(WebResource resource) {
+        this.service = resource;
+    }
+
+    private String selectActiveServerAddress(Client client, AtlasServerEnsemble serverEnsemble)
+            throws AtlasServiceException {
+        List<String> serverInstances = serverEnsemble.getMembers();
+        String activeServerAddress = null;
+        for (String serverInstance : serverInstances) {
+            LOG.info("Trying with address {}", serverInstance);
+            activeServerAddress = getAddressIfActive(client, serverInstance);
+            if (activeServerAddress != null) {
+                LOG.info("Found service {} as active service.", serverInstance);
+                break;
+            }
+        }
+        if (activeServerAddress != null)
+            return activeServerAddress;
+        else
+            throw new AtlasServiceException(API_STATUS, new RuntimeException("Could not find any active instance"));
+    }
+
+    private String getAddressIfActive(Client client, String serverInstance) {
+        String activeServerAddress = null;
+        for (int i = 0; i < getNumberOfRetries(); i++) {
+            try {
+                service = client.resource(UriBuilder.fromUri(serverInstance).build());
+                String adminStatus = getAdminStatus();
+                if (StringUtils.equals(adminStatus, "ACTIVE")) {
+                    activeServerAddress = serverInstance;
+                    break;
+                } else {
+                    LOG.info("attempt #{}: Service {} - is not active. status={}", (i + 1), serverInstance, adminStatus);
+                }
+            } catch (Exception e) {
+                LOG.error("attempt #{}: Service {} - could not get status", (i + 1), serverInstance, e);
+            }
+            sleepBetweenRetries();
+        }
+        return activeServerAddress;
+    }
+
+    private WebResource getResource(WebResource service, String path, String... pathParams) {
+        WebResource resource = service.path(path);
+        resource = appendPathParams(resource, pathParams);
+        return resource;
+    }
+
+    private int getSleepBetweenRetriesMs() {
+        return configuration.getInt(AtlasBaseClient.ATLAS_CLIENT_HA_SLEEP_INTERVAL_MS_KEY, AtlasBaseClient.DEFAULT_SLEEP_BETWEEN_RETRIES_MS);
+    }
+
+    // Modify URL to include the path params
+    private WebResource getResource(WebResource service, API api, String... pathParams) {
+        WebResource resource = service.path(api.getPath());
+        resource = appendPathParams(resource, pathParams);
+        return resource;
+    }
+
+    private WebResource getResource(API api, String queryParamKey, List<String> queryParamValues) {
+        WebResource resource = service.path(api.getPath());
+        for (String queryParamValue : queryParamValues) {
+            if (StringUtils.isNotBlank(queryParamKey) && StringUtils.isNotBlank(queryParamValue)) {
+                resource = resource.queryParam(queryParamKey, queryParamValue);
+            }
+        }
+        return resource;
+    }
+
+    private WebResource appendPathParams(WebResource resource, String[] pathParams) {
+        if (pathParams != null) {
+            for (String pathParam : pathParams) {
+                resource = resource.path(pathParam);
+            }
+        }
+        return resource;
+    }
+
+    // Modify URL to include the query params
+    private WebResource getResource(WebResource service, API api, MultivaluedMap<String, String> queryParams) {
+        WebResource resource = service.path(api.getPath());
+        resource = appendQueryParams(queryParams, resource);
+        return resource;
+    }
+
+    private WebResource appendQueryParams(MultivaluedMap<String, String> queryParams, WebResource resource) {
+        if (null != queryParams && !queryParams.isEmpty()) {
+            for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
+                for (String value : entry.getValue()) {
+                    if (StringUtils.isNotBlank(value)) {
+                        resource = resource.queryParam(entry.getKey(), value);
+                    }
+                }
+            }
+        }
+        return resource;
+    }
+
+    public static class API {
+        private final String method;
+        private final String path;
+        private final Response.Status status;
+
+        public API(String path, String method, Response.Status status) {
+            this.path = path;
+            this.method = method;
+            this.status = status;
+        }
+
+        public String getMethod() {
+            return method;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public Response.Status getExpectedStatus() {
+            return status;
+        }
+    }
+
+    /**
+     * A class to capture input state while creating the client.
+     *
+     * The information here will be reused when the client is re-initialized on switch-over
+     * in case of High Availability.
+     */
+    private class AtlasClientContext {
+        private String[] baseUrls;
+        private Client client;
+        private String doAsUser;
+        private UserGroupInformation ugi;
+
+        public AtlasClientContext(String[] baseUrls, Client client, UserGroupInformation ugi, String doAsUser) {
+            this.baseUrls = baseUrls;
+            this.client = client;
+            this.ugi = ugi;
+            this.doAsUser = doAsUser;
+        }
+
+        public Client getClient() {
+            return client;
+        }
+
+        public String[] getBaseUrls() {
+            return baseUrls;
+        }
+
+        public String getDoAsUser() {
+            return doAsUser;
+        }
+
+        public UserGroupInformation getUgi() {
+            return ugi;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/common/src/main/java/org/apache/atlas/AtlasServerEnsemble.java
----------------------------------------------------------------------
diff --git a/client/common/src/main/java/org/apache/atlas/AtlasServerEnsemble.java b/client/common/src/main/java/org/apache/atlas/AtlasServerEnsemble.java
new file mode 100644
index 0000000..01e4e32
--- /dev/null
+++ b/client/common/src/main/java/org/apache/atlas/AtlasServerEnsemble.java
@@ -0,0 +1,52 @@
+/**
+ * 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.atlas;
+
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang.StringUtils;
+import java.util.Arrays;
+
+import java.util.List;
+
+public class AtlasServerEnsemble {
+
+    private final String[] urls;
+
+    public AtlasServerEnsemble(String[] baseUrls) {
+        Preconditions.checkArgument((baseUrls!=null && baseUrls.length>0),
+                "List of baseURLs cannot be null or empty.");
+        for (String baseUrl : baseUrls) {
+            Preconditions.checkArgument(StringUtils.isNotEmpty(baseUrl),
+                    "Base URL cannot be null or empty.");
+        }
+        urls = baseUrls;
+    }
+
+    public boolean hasSingleInstance() {
+        return urls.length==1;
+    }
+
+    public String firstURL() {
+        return urls[0];
+    }
+
+    public List<String> getMembers() {
+        return Arrays.asList(urls);
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/common/src/main/java/org/apache/atlas/AtlasServiceException.java
----------------------------------------------------------------------
diff --git a/client/common/src/main/java/org/apache/atlas/AtlasServiceException.java b/client/common/src/main/java/org/apache/atlas/AtlasServiceException.java
new file mode 100755
index 0000000..83f4f8d
--- /dev/null
+++ b/client/common/src/main/java/org/apache/atlas/AtlasServiceException.java
@@ -0,0 +1,56 @@
+/**
+ * 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.atlas;
+
+import com.sun.jersey.api.client.ClientResponse;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+import javax.ws.rs.WebApplicationException;
+
+public class AtlasServiceException extends Exception {
+    private ClientResponse.Status status;
+
+    public AtlasServiceException(AtlasBaseClient.API api, Exception e) {
+        super("Metadata service API " + api.getMethod() + " : " + api.getPath() + " failed", e);
+    }
+
+    public AtlasServiceException(AtlasBaseClient.API api, WebApplicationException e) throws JSONException {
+        this(api, ClientResponse.Status.fromStatusCode(e.getResponse().getStatus()),
+             ((JSONObject) e.getResponse().getEntity()).getString("stackTrace"));
+    }
+
+    private AtlasServiceException(AtlasBaseClient.API api, ClientResponse.Status status, String response) {
+        super("Metadata service API " + api + " failed with status " + (status != null ? status.getStatusCode() : -1)
+                + " (" + status + ") Response Body (" + response + ")");
+        this.status = status;
+    }
+
+    public AtlasServiceException(AtlasBaseClient.API api, ClientResponse response) {
+        this(api, ClientResponse.Status.fromStatusCode(response.getStatus()), response.getEntity(String.class));
+    }
+
+    public AtlasServiceException(Exception e) {
+        super(e);
+    }
+
+    public ClientResponse.Status getStatus() {
+        return status;
+    }
+}

http://git-wip-us.apache.org/repos/asf/atlas/blob/187730dd/client/common/src/main/java/org/apache/atlas/ResourceCreator.java
----------------------------------------------------------------------
diff --git a/client/common/src/main/java/org/apache/atlas/ResourceCreator.java b/client/common/src/main/java/org/apache/atlas/ResourceCreator.java
new file mode 100644
index 0000000..2017065
--- /dev/null
+++ b/client/common/src/main/java/org/apache/atlas/ResourceCreator.java
@@ -0,0 +1,29 @@
+/**
+ * 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.atlas;
+
+import com.sun.jersey.api.client.WebResource;
+
+/**
+ * An interface to capture the closure of how a WebResource is created.
+ */
+@Deprecated
+public interface ResourceCreator {
+    WebResource createResource();
+}


Mime
View raw message