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

ramackri pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new 5aa6629ba RANGER-5656: TagSync Atlas REST sync fails with 
AbstractMethodError after RANGER-4076 Jersey 2 migration (#1035)
5aa6629ba is described below

commit 5aa6629baa3d4bb9e078285c041d3aff2cb22a68
Author: Ramachandran Krishnan <[email protected]>
AuthorDate: Sun Jun 28 07:29:18 2026 +0530

    RANGER-5656: TagSync Atlas REST sync fails with AbstractMethodError after 
RANGER-4076 Jersey 2 migration (#1035)
    
    * RANGER-5656: TagSync Atlas REST sync fails with AbstractMethodError after 
RANGER-4076 Jersey 2 migration
    
    Replace AtlasClientV2 with HttpURLConnection-based AtlasRESTHttpClient so 
TagSync
    does not load Jersey 1.x on the same classpath as Ranger Jersey 2.x REST 
sink.
    Remove atlas-client-v1/v2 from tagsync dependencies and assembly tarball.
    
    * RANGER-5656: Address review feedback on AtlasRESTHttpClient and restore 
resolveTag comments
    
    Collapse multi-line statements in AtlasRESTHttpClient and restore original
    resolveTag block and inline comments in AtlasRESTTagSource.
    
    * RANGER-5656: Use RangerRESTClient for Atlas REST SSL and HA URL failover
    
    Replace HttpURLConnection Atlas client with RangerRESTClient so TagSync
    reuses the same SSL and multi-URL failover as TagAdminRESTSink. Initialize
    AtlasRESTHttpClient from comma-separated endpoints and keep basic auth when
    credentials are configured alongside Kerberos.
    
    * RANGER-5656: Fix checkstyle ImportOrder and apply review style in 
AtlasRESTHttpClient
    
    Add blank line between javax and java imports for checkstyle, use single
    return in execute(), and keep Atlas REST client initialization on one line.
    
    * Update AtlasRESTHttpClient.java
    
    * Update AtlasRESTTagSource.java
    
    ---------
    
    Co-authored-by: ramk <[email protected]>
    Co-authored-by: Madhan Neethiraj <[email protected]>
---
 distro/src/main/assembly/tagsync.xml               |   3 -
 tagsync/pom.xml                                    |  54 ------
 .../source/atlasrest/AtlasRESTHttpClient.java      | 146 ++++++++++++++++
 .../source/atlasrest/AtlasRESTTagSource.java       | 186 ++++++++++-----------
 4 files changed, 233 insertions(+), 156 deletions(-)

diff --git a/distro/src/main/assembly/tagsync.xml 
b/distro/src/main/assembly/tagsync.xml
index 6d230c5d1..f919f18d1 100644
--- a/distro/src/main/assembly/tagsync.xml
+++ b/distro/src/main/assembly/tagsync.xml
@@ -69,9 +69,6 @@
                                                        
<include>javax.inject:javax.inject</include>
                                                        
<include>javax.xml.bind:jaxb-api</include>
                                                        
<include>joda-time:joda-time</include>
-                                                       
<include>org.apache.atlas:atlas-client-common:jar:${atlas.version}</include>
-                                                       
<include>org.apache.atlas:atlas-client-v1:jar:${atlas.version}</include>
-                                                       
<include>org.apache.atlas:atlas-client-v2:jar:${atlas.version}</include>
                                                        
<include>org.apache.atlas:atlas-common:jar:${atlas.version}</include>
                                                        
<include>org.apache.atlas:atlas-intg:jar:${atlas.version}</include>
                                                        
<include>org.apache.atlas:atlas-notification:jar:${atlas.version}</include>
diff --git a/tagsync/pom.xml b/tagsync/pom.xml
index 30d5eea5b..5d8ca7a39 100644
--- a/tagsync/pom.xml
+++ b/tagsync/pom.xml
@@ -155,60 +155,6 @@
             <artifactId>jaxb-api</artifactId>
             <version>${jaxb.api.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.atlas</groupId>
-            <artifactId>atlas-client-v1</artifactId>
-            <version>${atlas.version}</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>jakarta.ws.rs</groupId>
-                    <artifactId>jakarta.ws.rs-api</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>javax.xml.bind</groupId>
-                    <artifactId>jaxb-api</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>javax.xml.bind</groupId>
-                    <artifactId>jaxb-api</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.springframework</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.atlas</groupId>
-            <artifactId>atlas-client-v2</artifactId>
-            <version>${atlas.version}</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>jakarta.ws.rs</groupId>
-                    <artifactId>jakarta.ws.rs-api</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.springframework</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
         <dependency>
             <groupId>org.apache.atlas</groupId>
             <artifactId>atlas-common</artifactId>
diff --git 
a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTHttpClient.java
 
b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTHttpClient.java
new file mode 100644
index 000000000..f78f6b0bb
--- /dev/null
+++ 
b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTHttpClient.java
@@ -0,0 +1,146 @@
+/*
+ * 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.ranger.tagsync.source.atlasrest;
+
+import org.apache.atlas.model.discovery.AtlasSearchResult;
+import org.apache.atlas.model.discovery.SearchParameters;
+import org.apache.atlas.model.typedef.AtlasTypesDef;
+import org.apache.atlas.type.AtlasType;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.plugin.util.RangerRESTClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Response;
+
+import java.io.IOException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * Minimal Atlas REST client for TagSync.
+ *
+ * <p>Uses {@link RangerRESTClient} for SSL, HA URL failover, and auth so 
TagSync
+ * does not load the Atlas Jersey 1.x client on the same classpath as Ranger's
+ * Jersey 2.x REST sink.
+ */
+public final class AtlasRESTHttpClient {
+    private static final Logger LOG = 
LoggerFactory.getLogger(AtlasRESTHttpClient.class);
+
+    private static final String SEARCH_PATH   = "/api/atlas/v2/search/basic";
+    private static final String TYPEDEFS_PATH = 
"/api/atlas/v2/types/typedefs/";
+
+    private final RangerRESTClient restClient;
+    private final boolean          kerberized;
+
+    /**
+     * Create an Atlas REST client backed by {@link RangerRESTClient}.
+     *
+     * @param restUrls          comma-separated Atlas REST base URLs
+     * @param sslConfigFile     SSL config file for HTTPS endpoints
+     * @param config            Hadoop configuration
+     * @param userNamePassword  basic-auth credentials when configured
+     * @param useKerberos       {@code true} when TagSync uses a keytab
+     */
+    public AtlasRESTHttpClient(final String restUrls, final String 
sslConfigFile, final Configuration config, final String[] userNamePassword, 
final boolean useKerberos) {
+        this.kerberized = useKerberos;
+        this.restClient = new RangerRESTClient(restUrls, sslConfigFile, 
config, "ranger.tagsync");
+
+        if (userNamePassword != null && userNamePassword.length >= 2 && 
StringUtils.isNotEmpty(userNamePassword[0])) {
+            restClient.setBasicAuthInfo(userNamePassword[0], 
userNamePassword[1]);
+        }
+
+        restClient.getClient();
+    }
+
+    /**
+     * Search Atlas for entities matching the given parameters.
+     *
+     * @param searchParameters search filter (classification, limit, etc.)
+     * @return parsed search result
+     * @throws Exception when the REST call fails on all configured URLs
+     */
+    public AtlasSearchResult facetedSearch(final SearchParameters 
searchParameters) throws Exception {
+        Response response = execute(() -> restClient.post(SEARCH_PATH, null, 
searchParameters));
+        String   json     = readResponseBody(response, SEARCH_PATH);
+
+        return AtlasType.fromJson(json, AtlasSearchResult.class);
+    }
+
+    /**
+     * Download all Atlas type definitions.
+     *
+     * @return typedef bundle from Atlas
+     * @throws Exception when the REST call fails on all configured URLs
+     */
+    public AtlasTypesDef getAllTypeDefs() throws Exception {
+        Response response = execute(() -> restClient.get(TYPEDEFS_PATH, null));
+        String   json     = readResponseBody(response, TYPEDEFS_PATH);
+
+        return AtlasType.fromJson(json, AtlasTypesDef.class);
+    }
+
+    private Response execute(final RestCall restCall) throws Exception {
+        Response ret;
+
+        if (kerberized) {
+            UserGroupInformation ugi = UserGroupInformation.getLoginUser();
+
+            ugi.checkTGTAndReloginFromKeytab();
+
+            try {
+                ret = ugi.doAs((PrivilegedExceptionAction<Response>) 
restCall::run);
+            } catch (InterruptedException interruptedException) {
+                Thread.currentThread().interrupt();
+
+                throw new IOException("Atlas REST call interrupted", 
interruptedException);
+            }
+        } else {
+            ret = restCall.run();
+        }
+
+        return ret;
+    }
+
+    private String readResponseBody(final Response response, final String 
relativeUrl) throws IOException {
+        if (response == null) {
+            throw new IOException("Atlas REST " + relativeUrl + " failed: no 
response from any configured URL");
+        }
+
+        int    status = response.getStatus();
+        String body   = response.hasEntity() ? 
response.readEntity(String.class) : "";
+
+        if (status != Response.Status.OK.getStatusCode()) {
+            throw new IOException("Atlas REST " + relativeUrl + " failed with 
HTTP " + status + ": " + body);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Atlas REST {} -> HTTP {}", relativeUrl, status);
+        }
+
+        return body;
+    }
+
+    @FunctionalInterface
+    private interface RestCall {
+        Response run() throws Exception;
+    }
+}
diff --git 
a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
 
b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
index 8ac40cbac..c2547a39f 100644
--- 
a/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
+++ 
b/tagsync/src/main/java/org/apache/ranger/tagsync/source/atlasrest/AtlasRESTTagSource.java
@@ -19,10 +19,6 @@
 
 package org.apache.ranger.tagsync.source.atlasrest;
 
-import org.apache.atlas.AtlasClientV2;
-import org.apache.atlas.AtlasServiceException;
-import org.apache.atlas.exception.AtlasBaseException;
-import org.apache.atlas.model.SearchFilter;
 import org.apache.atlas.model.TimeBoundary;
 import org.apache.atlas.model.discovery.AtlasSearchResult;
 import org.apache.atlas.model.discovery.SearchParameters;
@@ -39,7 +35,6 @@
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.ranger.authorization.utils.JsonUtils;
 import org.apache.ranger.plugin.model.RangerValiditySchedule;
 import org.apache.ranger.plugin.util.ServiceTags;
@@ -77,7 +72,7 @@ public class AtlasRESTTagSource extends AbstractTagSource 
implements Runnable {
     });
 
     private long     sleepTimeBetweenCycleInMillis;
-    private String[] restUrls;
+    private AtlasRESTHttpClient atlasRestClient;
     private boolean  isKerberized;
     private String[] userNamePassword;
     private int      entitiesBatchSize = 
TagSyncConfig.DEFAULT_TAGSYNC_ATLASREST_SOURCE_ENTITIES_BATCH_SIZE;
@@ -142,12 +137,11 @@ public boolean initialize(Properties properties) {
         LOG.debug("kerberized={}", isKerberized);
 
         if (StringUtils.isNotEmpty(restEndpoint)) {
-            this.restUrls = restEndpoint.split(",");
-
-            for (int i = 0; i < restUrls.length; i++) {
-                if (!restUrls[i].endsWith("/")) {
-                    restUrls[i] += "/";
-                }
+            try {
+                atlasRestClient = new AtlasRESTHttpClient(restEndpoint, 
sslConfigFile, TagSyncConfig.getInstance(), userNamePassword, isKerberized);
+            } catch (RuntimeException exception) {
+                LOG.error("Failed to initialize Atlas REST client", exception);
+                ret = false;
             }
         } else {
             LOG.info("AtlasEndpoint not specified, Initial download of 
Atlas-entities cannot be done.");
@@ -233,97 +227,107 @@ private List<RangerAtlasEntityWithTags> 
getAtlasActiveEntities() {
 
         List<RangerAtlasEntityWithTags> ret = new ArrayList<>();
 
-        AtlasClientV2 atlasClient = null;
-        try {
-            atlasClient = getAtlasClient();
-        } catch (IOException exception) {
-            LOG.error("Failed to get Atlas client.", exception);
+        if (atlasRestClient == null) {
+            return ret;
         }
 
-        if (atlasClient != null) {
-            SearchParameters searchParams = new SearchParameters();
-
-            searchParams.setExcludeDeletedEntities(true);
-            searchParams.setClassification("*");
-            //searchParams.setIncludeSubClassifications(true);
-            //searchParams.setIncludeSubTypes(true);
-            searchParams.setIncludeClassificationAttributes(true);
-            searchParams.setLimit(entitiesBatchSize);
-
-            boolean isMoreData;
-            int     nextStartIndex = 0;
-
-            do {
-                AtlasTypeRegistry                            typeRegistry  = 
new AtlasTypeRegistry();
-                AtlasTypeRegistry.AtlasTransientTypeRegistry tty           = 
null;
-                AtlasSearchResult                            searchResult  = 
null;
-                boolean                                      commitUpdates = 
false;
-
-                searchParams.setOffset(nextStartIndex);
-                isMoreData = false;
-
-                try {
-                    searchResult = atlasClient.facetedSearch(searchParams);
-                    AtlasTypesDef typesDef = atlasClient.getAllTypeDefs(new 
SearchFilter());
-                    tty = typeRegistry.lockTypeRegistryForUpdate();
-                    tty.addTypes(typesDef);
-                    commitUpdates = true;
-                } catch (AtlasServiceException | AtlasBaseException excp) {
-                    LOG.error("failed to download tags from Atlas", excp);
-                    ret = null;
-                } catch (Exception unexpectedException) {
-                    LOG.error("Failed to download tags from Atlas due to 
unexpected exception", unexpectedException);
-                    ret = null;
-                } finally {
-                    if (tty != null) {
-                        typeRegistry.releaseTypeRegistryForUpdate(tty, 
commitUpdates);
-                    }
+        SearchParameters searchParams = new SearchParameters();
+
+        searchParams.setExcludeDeletedEntities(true);
+        searchParams.setClassification("*");
+        searchParams.setIncludeClassificationAttributes(true);
+        searchParams.setLimit(entitiesBatchSize);
+
+        boolean isMoreData;
+        int     nextStartIndex = 0;
+
+        do {
+            AtlasTypeRegistry                            typeRegistry  = new 
AtlasTypeRegistry();
+            AtlasTypeRegistry.AtlasTransientTypeRegistry tty           = null;
+            AtlasSearchResult                            searchResult  = null;
+            boolean                                      commitUpdates = false;
+
+            searchParams.setOffset(nextStartIndex);
+
+            isMoreData = false;
+
+            try {
+                searchResult = atlasRestClient.facetedSearch(searchParams);
+
+                AtlasTypesDef typesDef = atlasRestClient.getAllTypeDefs();
+
+                tty = typeRegistry.lockTypeRegistryForUpdate();
+
+                tty.addTypes(typesDef);
+
+                commitUpdates = true;
+            } catch (IOException excp) {
+                LOG.error("failed to download tags from Atlas", excp);
+
+                ret = null;
+
+                break;
+            } catch (Exception unexpectedException) {
+                LOG.error("Failed to download tags from Atlas due to 
unexpected exception", unexpectedException);
+
+                ret = null;
+
+                break;
+            } finally {
+                if (tty != null) {
+                    typeRegistry.releaseTypeRegistryForUpdate(tty, 
commitUpdates);
                 }
+            }
 
-                if (commitUpdates && searchResult != null) {
+            if (commitUpdates && searchResult != null) {
+                if (LOG.isDebugEnabled()) {
                     LOG.debug(AtlasType.toJson(searchResult));
+                }
 
-                    List<AtlasEntityHeader> entityHeaders = 
searchResult.getEntities();
+                List<AtlasEntityHeader> entityHeaders = 
searchResult.getEntities();
 
-                    if (CollectionUtils.isNotEmpty(entityHeaders)) {
-                        nextStartIndex += entityHeaders.size();
-                        isMoreData = true;
+                if (CollectionUtils.isNotEmpty(entityHeaders)) {
+                    nextStartIndex += entityHeaders.size();
+                    isMoreData     = true;
 
-                        for (AtlasEntityHeader header : entityHeaders) {
-                            if 
(!header.getStatus().equals(AtlasEntity.Status.ACTIVE)) {
-                                LOG.debug("Skipping entity because it is not 
ACTIVE, header:[{}]", header);
-                                continue;
-                            }
+                    for (AtlasEntityHeader header : entityHeaders) {
+                        if 
(!header.getStatus().equals(AtlasEntity.Status.ACTIVE)) {
+                            LOG.debug("Skipping entity because it is not 
ACTIVE, header:[{}]", header);
 
-                            String typeName = header.getTypeName();
-                            if 
(!AtlasResourceMapperUtil.isEntityTypeHandled(typeName)) {
-                                LOG.debug("Not fetching Atlas entities of 
type:[{}]", typeName);
-                                continue;
-                            }
+                            continue;
+                        }
 
-                            
List<EntityNotificationWrapper.RangerAtlasClassification> allTagsForEntity = 
new ArrayList<>();
+                        String typeName = header.getTypeName();
 
-                            for (AtlasClassification classification : 
header.getClassifications()) {
-                                
List<EntityNotificationWrapper.RangerAtlasClassification> tags = 
resolveTag(typeRegistry, classification);
-                                if (tags != null) {
-                                    allTagsForEntity.addAll(tags);
-                                }
-                            }
+                        if 
(!AtlasResourceMapperUtil.isEntityTypeHandled(typeName)) {
+                            LOG.debug("Not fetching Atlas entities of 
type:[{}]", typeName);
 
-                            if (CollectionUtils.isNotEmpty(allTagsForEntity)) {
-                                RangerAtlasEntity         entity         = new 
RangerAtlasEntity(typeName, header.getGuid(), header.getAttributes());
-                                RangerAtlasEntityWithTags entityWithTags = new 
RangerAtlasEntityWithTags(entity, allTagsForEntity, typeRegistry);
+                            continue;
+                        }
+
+                        
List<EntityNotificationWrapper.RangerAtlasClassification> allTagsForEntity = 
new ArrayList<>();
+
+                        for (AtlasClassification classification : 
header.getClassifications()) {
+                            
List<EntityNotificationWrapper.RangerAtlasClassification> tags = 
resolveTag(typeRegistry, classification);
 
-                                ret.add(entityWithTags);
+                            if (tags != null) {
+                                allTagsForEntity.addAll(tags);
                             }
                         }
+
+                        if (CollectionUtils.isNotEmpty(allTagsForEntity)) {
+                            RangerAtlasEntity         entity         = new 
RangerAtlasEntity(typeName, header.getGuid(), header.getAttributes());
+                            RangerAtlasEntityWithTags entityWithTags = new 
RangerAtlasEntityWithTags(entity, allTagsForEntity, typeRegistry);
+
+                            ret.add(entityWithTags);
+                        }
                     }
                 }
             }
-            while (isMoreData);
         }
+        while (isMoreData);
 
-        LOG.debug("<== getAtlasActiveEntities()");
+        LOG.debug("<== getAtlasActiveEntities(), count={}", ret == null ? 0 : 
ret.size());
 
         return ret;
     }
@@ -404,20 +408,4 @@ private 
List<EntityNotificationWrapper.RangerAtlasClassification> resolveTag(Atl
         }
         return ret;
     }
-
-    private AtlasClientV2 getAtlasClient() throws IOException {
-        final AtlasClientV2 ret;
-
-        if (isKerberized) {
-            UserGroupInformation ugi = UserGroupInformation.getLoginUser();
-
-            ugi.checkTGTAndReloginFromKeytab();
-
-            ret = new AtlasClientV2(ugi, ugi.getShortUserName(), restUrls);
-        } else {
-            ret = new AtlasClientV2(restUrls, userNamePassword);
-        }
-
-        return ret;
-    }
 }

Reply via email to