Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 5863B200D31 for ; Wed, 27 Sep 2017 06:25:29 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 56FCE1609D7; Wed, 27 Sep 2017 04:25:29 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 5518D1609EE for ; Wed, 27 Sep 2017 06:25:27 +0200 (CEST) Received: (qmail 46617 invoked by uid 500); 27 Sep 2017 04:25:26 -0000 Mailing-List: contact commits-help@atlas.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@atlas.apache.org Delivered-To: mailing list commits@atlas.apache.org Received: (qmail 46229 invoked by uid 99); 27 Sep 2017 04:25:26 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 27 Sep 2017 04:25:26 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 7B95DF5B65; Wed, 27 Sep 2017 04:25:25 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: madhan@apache.org To: commits@atlas.apache.org Date: Wed, 27 Sep 2017 04:25:30 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [6/7] atlas git commit: ATLAS-2179: Split Atlas client library to avoid unnecessary dependencies archived-at: Wed, 27 Sep 2017 04:25:29 -0000 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.any(), anyString())).thenReturn(response); + + List 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 @@ + + + + + atlas-client + org.apache.atlas + 1.0.0-SNAPSHOT + + 4.0.0 + + atlas-client-v2 + + + + org.apache.atlas + atlas-intg + + + org.apache.atlas + atlas-client-common + ${project.version} + + + 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 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) null, guid); + } + + public AtlasEntityWithExtInfo getEntityByAttribute(String type, Map attributes) throws AtlasServiceException { + MultivaluedMap queryParams = attributesToQueryParams(attributes); + + return callAPI(API_V2.GET_ENTITY_BY_ATTRIBUTE, AtlasEntityWithExtInfo.class, queryParams, type); + } + + public EntityMutationResponse updateEntityByAttribute(String type, Map attributes, AtlasEntityWithExtInfo entityInfo) + throws AtlasServiceException { + MultivaluedMap queryParams = attributesToQueryParams(attributes); + + return callAPI(API_V2.UPDATE_ENTITY_BY_ATTRIBUTE, EntityMutationResponse.class, entityInfo, queryParams, type); + } + + /* Lineage Calls */ + + public EntityMutationResponse deleteEntityByAttribute(String type, Map attributes) throws AtlasServiceException { + MultivaluedMap 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 guids) throws AtlasServiceException { + MultivaluedMap 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 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 classifications) throws AtlasServiceException { + callAPI(formatPathParameters(API_V2.ADD_CLASSIFICATIONS, guid), (Class)null, classifications, (String[]) null); + } + + public void updateClassifications(String guid, List classifications) throws AtlasServiceException { + callAPI(formatPathParameters(API_V2.UPDATE_CLASSIFICATIONS, guid), AtlasClassifications.class, classifications); + } + + public void deleteClassifications(String guid, List 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 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 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 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 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 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 attributesToQueryParams(Map attributes) { + return attributesToQueryParams(attributes, null); + } + + private MultivaluedMap attributesToQueryParams(Map attributes, + MultivaluedMap queryParams) { + if (queryParams == null) { + queryParams = new MultivaluedMapImpl(); + } + + if (MapUtils.isNotEmpty(attributes)) { + for (Map.Entry e : attributes.entrySet()) { + queryParams.putSingle(PREFIX_ATTR + e.getKey(), e.getValue()); + } + } + + return queryParams; + } + + private T getTypeDefByName(final String name, Class 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 getTypeDefByGuid(final String guid, Class 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 String getAtlasPath(Class 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 @@ + + + + + atlas-client + org.apache.atlas + 1.0.0-SNAPSHOT + + 4.0.0 + + atlas-client-common + + + + + org.apache.httpcomponents + httpclient + + + + com.sun.jersey + jersey-client + + + + org.apache.atlas + atlas-intg + + + \ 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 + *

+ * 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.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 callAPI(API api, Class responseType, Object requestObject, String... params) + throws AtlasServiceException { + return callAPIWithResource(api, getResource(api, params), requestObject, responseType); + } + + public T callAPI(API api, GenericType responseType, Object requestObject, String... params) + throws AtlasServiceException { + return callAPIWithResource(api, getResource(api, params), requestObject, responseType); + } + + public T callAPI(API api, Class responseType, Object requestBody, + MultivaluedMap queryParams, String... params) throws AtlasServiceException { + WebResource resource = getResource(api, queryParams, params); + return callAPIWithResource(api, resource, requestBody, responseType); + } + + public T callAPI(API api, Class responseType, MultivaluedMap queryParams, String... params) + throws AtlasServiceException { + WebResource resource = getResource(api, queryParams, params); + return callAPIWithResource(api, resource, null, responseType); + } + + public T callAPI(API api, GenericType responseType, MultivaluedMap queryParams, String... params) + throws AtlasServiceException { + WebResource resource = getResource(api, queryParams, params); + return callAPIWithResource(api, resource, null, responseType); + } + + public T callAPI(API api, Class responseType, MultivaluedMap queryParams) + throws AtlasServiceException { + return callAPIWithResource(api, getResource(api, queryParams), null, responseType); + } + + public T callAPI(API api, Class responseType, String queryParamKey, List 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 callAPIWithResource(API api, WebResource resource, Object requestObject, Class responseType) throws AtlasServiceException { + GenericType genericType = null; + if (responseType != null) { + genericType = new GenericType<>(responseType); + } + return callAPIWithResource(api, resource, requestObject, genericType); + } + + protected T callAPIWithResource(API api, WebResource resource, Object requestObject, GenericType 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 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 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 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 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 queryParams) { + WebResource resource = service.path(api.getPath()); + resource = appendQueryParams(queryParams, resource); + return resource; + } + + private WebResource appendQueryParams(MultivaluedMap queryParams, WebResource resource) { + if (null != queryParams && !queryParams.isEmpty()) { + for (Map.Entry> 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 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(); +}