Skip to content

Commit 4062b38

Browse files
Merge pull request #94 from newrelic/feat/staging-token-issue
fix: Staging credentials
2 parents 2aa7a99 + 8aef92d commit 4062b38

File tree

3 files changed

+74
-33
lines changed

3 files changed

+74
-33
lines changed

NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/NRVideoConfiguration.java

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public final class NRVideoConfiguration {
4242
private final boolean memoryOptimized;
4343
private final boolean debugLoggingEnabled;
4444
private final boolean isTV;
45+
private final String collectorAddress;
4546

4647
// Performance optimization constants
4748
private static final int DEFAULT_HARVEST_CYCLE_SECONDS = 5 * 60; // 5 minutes
@@ -78,6 +79,7 @@ private NRVideoConfiguration(Builder builder) {
7879
this.memoryOptimized = builder.memoryOptimized;
7980
this.debugLoggingEnabled = builder.debugLoggingEnabled;
8081
this.isTV = builder.isTV;
82+
this.collectorAddress = builder.collectorAddress;
8183
}
8284

8385
// Immutable getters
@@ -91,6 +93,7 @@ private NRVideoConfiguration(Builder builder) {
9193
public boolean isMemoryOptimized() { return memoryOptimized; }
9294
public boolean isDebugLoggingEnabled() { return debugLoggingEnabled; }
9395
public boolean isTV() { return isTV; }
96+
public String getCollectorAddress() { return collectorAddress; }
9497

9598
/**
9699
* Get dead letter retry interval in milliseconds
@@ -107,41 +110,56 @@ public long getDeadLetterRetryInterval() {
107110
}
108111

109112
/**
110-
* Enterprise-grade region identification with multiple fallback strategies
111-
* Thread-safe and optimized for performance
113+
* Parse region code from application token prefix
114+
* Matches NewRelic iOS Agent pattern: extracts region prefix before 'x'
115+
* Examples: "EUxABCD..." -> "EU", "APxABCD..." -> "AP", "AA..." -> ""
116+
*/
117+
private static String parseRegionFromToken(String applicationToken) {
118+
if (applicationToken == null || applicationToken.length() < 3) {
119+
return "";
120+
}
121+
122+
// Find the first 'x' in the token
123+
int xIndex = applicationToken.indexOf('x');
124+
if (xIndex == -1) {
125+
return ""; // No region prefix found
126+
}
127+
128+
// Extract everything before the first 'x'
129+
String regionCode = applicationToken.substring(0, xIndex);
130+
131+
// Remove any trailing 'x' characters
132+
while (regionCode.length() > 0 && regionCode.charAt(regionCode.length() - 1) == 'x') {
133+
regionCode = regionCode.substring(0, regionCode.length() - 1);
134+
}
135+
136+
return regionCode;
137+
}
138+
139+
/**
140+
* Identify region with proper token parsing and fallback logic
141+
* Behavior similar to NewRelic iOS Agent's NRMAAgentConfiguration
112142
*/
113143
private static String identifyRegion(String applicationToken) {
114144
if (applicationToken == null || applicationToken.length() < 10) {
115145
return "US"; // Safe default
116146
}
117147

118-
String cleanToken = applicationToken.trim().toLowerCase();
148+
// First, try to parse region from token prefix (e.g., "EUx", "APx")
149+
String regionCode = parseRegionFromToken(applicationToken);
119150

120-
// Strategy 1: Direct prefix matching (most reliable)
121-
for (Map.Entry<String, String> entry : REGION_MAPPINGS.entrySet()) {
122-
String regionKey = entry.getKey().toLowerCase();
123-
if (cleanToken.startsWith(regionKey) || cleanToken.contains("-" + regionKey + "-")) {
124-
return entry.getValue();
125-
}
126-
}
151+
if (regionCode != null && regionCode.length() > 0) {
152+
// Convert region code to uppercase and validate
153+
String upperRegion = regionCode.toUpperCase();
127154

128-
// Strategy 2: Token structure analysis
129-
if (cleanToken.length() >= 40) { // Standard NR token length
130-
// EU tokens often have specific patterns
131-
if (cleanToken.contains("eu") || cleanToken.contains("europe")) {
132-
return "EU";
133-
}
134-
// AP tokens often have specific patterns
135-
if (cleanToken.contains("ap") || cleanToken.contains("asia") || cleanToken.contains("pacific")) {
136-
return "AP";
137-
}
138-
// Gov tokens have specific patterns
139-
if (cleanToken.contains("gov") || cleanToken.contains("fed")) {
140-
return "GOV";
155+
// Map region codes to standard regions
156+
String mappedRegion = REGION_MAPPINGS.get(upperRegion);
157+
if (mappedRegion != null) {
158+
return mappedRegion;
141159
}
142160
}
143161

144-
// Strategy 3: Default to US for production stability
162+
// Default to US for standard tokens without region prefix
145163
return "US";
146164
}
147165

@@ -158,6 +176,7 @@ public static final class Builder {
158176
private boolean memoryOptimized = true;
159177
private boolean debugLoggingEnabled = false;
160178
private boolean isTV = false;
179+
private String collectorAddress = null;
161180

162181
public Builder(String applicationToken) {
163182
this.applicationToken = applicationToken;
@@ -235,6 +254,16 @@ public Builder enableLogging() {
235254
return this;
236255
}
237256

257+
/**
258+
* Set custom collector domain address for /connect and /data endpoints (optional)
259+
* Example: "staging-mobile-collector.newrelic.com" or "mobile-collector.newrelic.com"
260+
* If not set, will be auto-detected from application token region
261+
*/
262+
public Builder withCollectorAddress(String collectorAddress) {
263+
this.collectorAddress = collectorAddress;
264+
return this;
265+
}
266+
238267
private void applyTVOptimizations() {
239268
this.harvestCycleSeconds = TV_HARVEST_CYCLE_SECONDS;
240269
this.liveHarvestCycleSeconds = TV_LIVE_HARVEST_CYCLE_SECONDS;

NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/auth/TokenManager.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,18 +154,25 @@ private boolean isTokenValid() {
154154

155155
/**
156156
* Build token endpoint URL based on region
157+
* If collectorAddress is explicitly set, use it for /connect endpoint
158+
* Otherwise, auto-detect from region
157159
*/
158160
private String buildTokenEndpoint() {
159-
String region = configuration.getRegion().toUpperCase();
161+
// If collectorAddress is explicitly set, use it for /connect endpoint
162+
if (configuration.getCollectorAddress() != null && !configuration.getCollectorAddress().isEmpty()) {
163+
return "https://" + configuration.getCollectorAddress() + "/mobile/v5/connect";
164+
}
165+
166+
// Otherwise, auto-detect from region
167+
String region = configuration.getRegion();
168+
region = (region != null) ? region.toUpperCase() : "US";
160169
switch (region) {
161170
case "EU":
162171
return "https://mobile-collector.eu.newrelic.com/mobile/v5/connect";
163172
case "AP":
164173
return "https://mobile-collector.ap.newrelic.com/mobile/v5/connect";
165174
case "GOV":
166175
return "https://mobile-collector.gov.newrelic.com/mobile/v5/connect";
167-
case "STAGING":
168-
return "https://mobile-collector.staging.newrelic.com/mobile/v5/connect";
169176
default:
170177
return "https://mobile-collector.newrelic.com/mobile/v5/connect";
171178
}

NewRelicVideoCore/src/main/java/com/newrelic/videoagent/core/harvest/OptimizedHttpClient.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ public class OptimizedHttpClient implements HttpClientInterface {
4343
REGIONAL_ENDPOINTS.put("EU", "https://mobile-collector.eu.newrelic.com/mobile/v3/data");
4444
REGIONAL_ENDPOINTS.put("AP", "https://mobile-collector.ap.newrelic.com/mobile/v3/data");
4545
REGIONAL_ENDPOINTS.put("GOV", "https://mobile-collector.gov.newrelic.com/mobile/v3/data");
46-
REGIONAL_ENDPOINTS.put("STAGING", "https://mobile-collector.staging.newrelic.com/mobile/v3/data");
4746
REGIONAL_ENDPOINTS.put("DEFAULT", REGIONAL_ENDPOINTS.get("US"));
4847
}
4948

@@ -59,10 +58,16 @@ public OptimizedHttpClient(NRVideoConfiguration configuration, android.content.C
5958
this.tokenManager = new TokenManager(context, configuration);
6059
this.deviceInfo = DeviceInformation.getInstance(context);
6160

62-
// Set endpoint URL based on region, defaulting to US if not found
63-
String region = configuration.getRegion().toUpperCase();
64-
String regionEndpoint = REGIONAL_ENDPOINTS.get(region);
65-
this.endpointUrl = regionEndpoint != null ? regionEndpoint : REGIONAL_ENDPOINTS.get("DEFAULT");
61+
// If collectorAddress is explicitly set, use it
62+
if (configuration.getCollectorAddress() != null && !configuration.getCollectorAddress().isEmpty()) {
63+
this.endpointUrl = "https://" + configuration.getCollectorAddress() + "/mobile/v3/data";
64+
} else {
65+
// Otherwise, auto-detect from region
66+
String region = configuration.getRegion();
67+
region = (region != null) ? region.toUpperCase() : "US";
68+
String endpoint = REGIONAL_ENDPOINTS.get(region);
69+
this.endpointUrl = (endpoint != null) ? endpoint : REGIONAL_ENDPOINTS.get("DEFAULT");
70+
}
6671

6772
if (configuration.isMemoryOptimized()) {
6873
connectionTimeoutMs = 6000;
@@ -74,7 +79,7 @@ public OptimizedHttpClient(NRVideoConfiguration configuration, android.content.C
7479
System.setProperty("http.keepAliveDuration", "300000"); // 5 minutes
7580
System.setProperty("http.maxConnections", "5");
7681

77-
NRLog.d("Initialized with region: " + region +
82+
NRLog.d("Initialized with region: " + configuration.getRegion() +
7883
", endpoint URL: " + endpointUrl);
7984
}
8085

0 commit comments

Comments
 (0)