Repository: pinpoint-apm/pinpoint
Issue: Handle cases where services have the same application name with different service type.
Labels: bug
enhancement

Backporting fixes to 1.1.0.

Issue resolves the following cases.
1. Services with the same application name but different service types should be selectable separately - #214 
2. User nodes now take the destination's service type along with the application name when drawing the server map - #254 


Issue comments:
fix applied to 1.1.x branch


Pull request review comments:

Patch that resolved the issue:
diff --git a/commons/src/main/java/com/navercorp/pinpoint/common/util/ApplicationMapStatisticsUtils.java b/commons/src/main/java/com/navercorp/pinpoint/common/util/ApplicationMapStatisticsUtils.java
index a0ff208e00d1..948e912ba191 100644
--- a/commons/src/main/java/com/navercorp/pinpoint/common/util/ApplicationMapStatisticsUtils.java
+++ b/commons/src/main/java/com/navercorp/pinpoint/common/util/ApplicationMapStatisticsUtils.java
@@ -103,6 +103,12 @@ public static String getDestApplicationNameFromColumnName(byte[] bytes) {
         return BytesUtils.toStringAndRightTrim(bytes, 6, length);
     }
 
+    public static String getDestApplicationNameFromColumnNameForUser(byte[] bytes, ServiceType destServiceType) {
+        String destApplicationName = getDestApplicationNameFromColumnName(bytes);
+        String destServiceTypeName = destServiceType.name();
+        return destApplicationName + "_" + destServiceTypeName;
+    }
+
     public static String getHost(byte[] bytes) {
         int offset = 6 + BytesUtils.bytesToShort(bytes, 4);
 
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseApplicationIndexDao.java b/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseApplicationIndexDao.java
index f4ef0055002f..9b5d1cc420f3 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseApplicationIndexDao.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/dao/hbase/HbaseApplicationIndexDao.java
@@ -16,6 +16,7 @@
 
 package com.navercorp.pinpoint.web.dao.hbase;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.lang.StringUtils;
@@ -45,7 +46,7 @@ public class HbaseApplicationIndexDao implements ApplicationIndexDao {
 
     @Autowired
     @Qualifier("applicationNameMapper")
-    private RowMapper<Application> applicationNameMapper;
+    private RowMapper<List<Application>> applicationNameMapper;
 
     @Autowired
     @Qualifier("agentIdMapper")
@@ -55,7 +56,12 @@ public class HbaseApplicationIndexDao implements ApplicationIndexDao {
     public List<Application> selectAllApplicationNames() {
         Scan scan = new Scan();
         scan.setCaching(30);
-        return hbaseOperations2.find(HBaseTables.APPLICATION_INDEX, scan, applicationNameMapper);
+        List<List<Application>> results = hbaseOperations2.find(HBaseTables.APPLICATION_INDEX, scan, applicationNameMapper);
+        List<Application> applications = new ArrayList<Application>();
+        for (List<Application> result : results) {
+            applications.addAll(result);
+        }
+        return applications;
     }
 
     @Override
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApplicationNameMapper.java b/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApplicationNameMapper.java
index 186e061e0c3e..62220fc0f37e 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApplicationNameMapper.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/mapper/ApplicationNameMapper.java
@@ -16,25 +16,50 @@
 
 package com.navercorp.pinpoint.web.mapper;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.springframework.data.hadoop.hbase.RowMapper;
 import org.springframework.stereotype.Component;
 
+import com.navercorp.pinpoint.common.ServiceType;
 import com.navercorp.pinpoint.web.vo.Application;
 
 /**
  *
  */
 @Component
-public class ApplicationNameMapper implements RowMapper<Application> {
+public class ApplicationNameMapper implements RowMapper<List<Application>> {
+    
     @Override
-    public Application mapRow(Result result, int rowNum) throws Exception {
+    public List<Application> mapRow(Result result, int rowNum) throws Exception {
         if (result.isEmpty()) {
-            return null;
+            return Collections.emptyList();
         }
+        Set<Short> uniqueTypeCodes = new HashSet<Short>();
         String applicationName = Bytes.toString(result.getRow());
-        short serviceType = Bytes.toShort(result.value());
-        return new Application(applicationName, serviceType);
+        
+        List<Cell> list = result.listCells();
+        if (list == null) {
+            return Collections.emptyList();
+        }
+        
+        for (Cell cell : list) {
+            short serviceTypeCode = Bytes.toShort(CellUtil.cloneValue(cell));
+            uniqueTypeCodes.add(serviceTypeCode);
+        }
+        List<Application> applications = new ArrayList<Application>();
+        for (short serviceTypeCode : uniqueTypeCodes) {
+            applications.add(new Application(applicationName, ServiceType.findServiceType(serviceTypeCode)));
+        }      
+
+        return applications;
     }
 }
diff --git a/web/src/main/java/com/navercorp/pinpoint/web/mapper/MapStatisticsCalleeMapper.java b/web/src/main/java/com/navercorp/pinpoint/web/mapper/MapStatisticsCalleeMapper.java
index 2040310f4724..6afdf3812b55 100644
--- a/web/src/main/java/com/navercorp/pinpoint/web/mapper/MapStatisticsCalleeMapper.java
+++ b/web/src/main/java/com/navercorp/pinpoint/web/mapper/MapStatisticsCalleeMapper.java
@@ -16,6 +16,7 @@
 
 package com.navercorp.pinpoint.web.mapper;
 
+import com.navercorp.pinpoint.common.ServiceType;
 import com.navercorp.pinpoint.common.buffer.Buffer;
 import com.navercorp.pinpoint.common.buffer.FixedBuffer;
 import com.navercorp.pinpoint.common.util.ApplicationMapStatisticsUtils;
@@ -71,7 +72,7 @@ public LinkDataMap mapRow(Result result, int rowNum) throws Exception {
         for (Cell cell : result.rawCells()) {
 
             final byte[] qualifier = CellUtil.cloneQualifier(cell);
-            final Application callerApplication = readCallerApplication(qualifier);
+            final Application callerApplication = readCallerApplication(qualifier, calleeApplication.getServiceType());
             if (filter.filter(callerApplication)) {
                 continue;
             }
@@ -98,9 +99,15 @@ public LinkDataMap mapRow(Result result, int rowNum) throws Exception {
         return linkDataMap;
     }
 
-    private Application readCallerApplication(byte[] qualifier) {
-        String callerApplicationName = ApplicationMapStatisticsUtils.getDestApplicationNameFromColumnName(qualifier);
-        short callerServiceType = ApplicationMapStatisticsUtils.getDestServiceTypeFromColumnName(qualifier);
+    private Application readCallerApplication(byte[] qualifier, ServiceType calleeServiceType) {
+        short callerServiceType = ApplicationMapStatisticsUtils.getDestServiceTypeFromColumnName(qualifier);// Caller may be a user node, and user nodes may call nodes with the same application name but different service type.
+        // To distinguish between these user nodes, append callee's service type to the application name.
+        String callerApplicationName;
+        if (ServiceType.findServiceType(callerServiceType).isUser()) {
+            callerApplicationName = ApplicationMapStatisticsUtils.getDestApplicationNameFromColumnNameForUser(qualifier, calleeServiceType);
+        } else {
+            callerApplicationName = ApplicationMapStatisticsUtils.getDestApplicationNameFromColumnName(qualifier);
+        }
         return new Application(callerApplicationName, callerServiceType);
     }
 


Test patch: