azkaban-aplcache

Details

diff --git a/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackManager.java b/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackManager.java
index e60a512..6b31314 100644
--- a/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackManager.java
+++ b/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackManager.java
@@ -136,7 +136,7 @@ public class JobCallbackManager implements EventListener {
       return;
     }
 
-    // don't want to waste time resolving properties if there are
+    // don't want to waste time resolving properties if there are no
     // callback properties to parse
     Props props = PropsUtils.resolveProps(jobRunner.getProps());
 
diff --git a/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackUtil.java b/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackUtil.java
index c796186..cd576b7 100644
--- a/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackUtil.java
+++ b/azkaban-execserver/src/main/java/azkaban/execapp/event/JobCallbackUtil.java
@@ -19,6 +19,7 @@ import static azkaban.jobcallback.JobCallbackConstants.SERVER_TOKEN;
 import static azkaban.jobcallback.JobCallbackConstants.STATUS_TOKEN;
 
 import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -143,7 +144,7 @@ public class JobCallbackUtil {
         break;
       } else {
         String callbackUrlWithTokenReplaced =
-            replaceToken(callbackUrl, contextInfo);
+            replaceTokens(callbackUrl, contextInfo, true);
 
         String requestMethodKey =
             requestMethod.replace(SEQUENCE_TOKEN, sequenceStr);
@@ -162,7 +163,8 @@ public class JobCallbackUtil {
           } else {
             // put together an URL
             HttpPost httpPost = new HttpPost(callbackUrlWithTokenReplaced);
-            String postActualBody = replaceToken(httpBodyValue, contextInfo);
+            String postActualBody =
+                replaceTokens(httpBodyValue, contextInfo, false);
             privateLogger.info("postActualBody: " + postActualBody);
             httpPost.setEntity(createStringEntity(postActualBody));
             httpRequest = httpPost;
@@ -270,25 +272,52 @@ public class JobCallbackUtil {
   }
 
   /**
-   * Replace the supported tokens in the URL with values in the contextInfo
+   * Replace the supported tokens in the URL with values in the contextInfo.
+   * This will also make sure the values are HTTP encoded.
    * 
-   * @param url
+   * @param value
    * @param contextInfo
-   * @return String - url with tokens replaced with values
+   * @param withEncoding - whether the token values will be HTTP encoded
+   * @return String - value with tokens replaced with values
    */
-  public static String replaceToken(String url, Map<String, String> contextInfo) {
+  public static String replaceTokens(String value,
+      Map<String, String> contextInfo, boolean withEncoding) {
 
-    String result = url;
+    String result = value;
+    String tokenValue =
+        encodeQueryParam(contextInfo.get(SERVER_TOKEN), withEncoding);
+    result = result.replace(SERVER_TOKEN, tokenValue);
 
-    result = result.replace(SERVER_TOKEN, contextInfo.get(SERVER_TOKEN));
-    result = result.replace(PROJECT_TOKEN, contextInfo.get(PROJECT_TOKEN));
-    result = result.replace(FLOW_TOKEN, contextInfo.get(FLOW_TOKEN));
-    result = result.replace(JOB_TOKEN, contextInfo.get(JOB_TOKEN));
-    result =
-        result.replace(EXECUTION_ID_TOKEN, contextInfo.get(EXECUTION_ID_TOKEN));
-    result =
-        result.replace(JOB_STATUS_TOKEN, contextInfo.get(JOB_STATUS_TOKEN));
+    tokenValue = encodeQueryParam(contextInfo.get(PROJECT_TOKEN), withEncoding);
+    result = result.replace(PROJECT_TOKEN, tokenValue);
+
+    tokenValue = encodeQueryParam(contextInfo.get(FLOW_TOKEN), withEncoding);
+    result = result.replace(FLOW_TOKEN, tokenValue);
+
+    tokenValue = encodeQueryParam(contextInfo.get(JOB_TOKEN), withEncoding);
+    result = result.replace(JOB_TOKEN, tokenValue);
+
+    tokenValue =
+        encodeQueryParam(contextInfo.get(EXECUTION_ID_TOKEN), withEncoding);
+    result = result.replace(EXECUTION_ID_TOKEN, tokenValue);
+
+    tokenValue =
+        encodeQueryParam(contextInfo.get(JOB_STATUS_TOKEN), withEncoding);
+
+    result = result.replace(JOB_STATUS_TOKEN, tokenValue);
 
     return result;
   }
+
+  private static String encodeQueryParam(String str, boolean withEncoding) {
+    if (!withEncoding) {
+      return str;
+    }
+    try {
+      return URLEncoder.encode(str, "UTF-8");
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalArgumentException(
+          "Encountered problem during encoding:", e);
+    }
+  }
 }
diff --git a/azkaban-execserver/src/test/java/azkaban/execapp/event/JobCallbackUtilTest.java b/azkaban-execserver/src/test/java/azkaban/execapp/event/JobCallbackUtilTest.java
index 33dd0bd..d5e0902 100644
--- a/azkaban-execserver/src/test/java/azkaban/execapp/event/JobCallbackUtilTest.java
+++ b/azkaban-execserver/src/test/java/azkaban/execapp/event/JobCallbackUtilTest.java
@@ -9,6 +9,7 @@ import static azkaban.jobcallback.JobCallbackConstants.JOB_TOKEN;
 import static azkaban.jobcallback.JobCallbackConstants.PROJECT_TOKEN;
 import static azkaban.jobcallback.JobCallbackConstants.SERVER_TOKEN;
 
+import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -28,20 +29,22 @@ public class JobCallbackUtilTest {
   private static Map<String, String> contextInfo;
 
   private static final String SERVER_NAME = "localhost:9999";
-  private static final String PROJECT_NANE = "PROJECTX";
-  private static final String FLOW_NANE = "FLOWX";
-  private static final String JOB_NANE = "JOBX";
+  private static final String PROJECT_NAME = "PROJECTX";
+  private static final String FLOW_NAME = "FLOWX";
+  private static final String JOB_NAME = "JOBX";
   private static final String EXECUTION_ID = "1234";
+  private static final String JOB_STATUS_NAME = JobCallbackStatusEnum.STARTED
+      .name();
 
   @BeforeClass
   public static void setup() {
     contextInfo = new HashMap<String, String>();
     contextInfo.put(SERVER_TOKEN, SERVER_NAME);
-    contextInfo.put(PROJECT_TOKEN, PROJECT_NANE);
-    contextInfo.put(FLOW_TOKEN, FLOW_NANE);
+    contextInfo.put(PROJECT_TOKEN, PROJECT_NAME);
+    contextInfo.put(FLOW_TOKEN, FLOW_NAME);
     contextInfo.put(EXECUTION_ID_TOKEN, EXECUTION_ID);
-    contextInfo.put(JOB_TOKEN, JOB_NANE);
-    contextInfo.put(JOB_STATUS_TOKEN, JobCallbackStatusEnum.STARTED.name());
+    contextInfo.put(JOB_TOKEN, JOB_NAME);
+    contextInfo.put(JOB_STATUS_TOKEN, JOB_STATUS_NAME);
   }
 
   @Test
@@ -157,18 +160,21 @@ public class JobCallbackUtilTest {
   @Test
   public void noTokenTest() {
     String urlWithNoToken = "http://www.linkedin.com";
-    String result = JobCallbackUtil.replaceToken(urlWithNoToken, contextInfo);
+    String result =
+        JobCallbackUtil.replaceTokens(urlWithNoToken, contextInfo, true);
     Assert.assertEquals(urlWithNoToken, result);
   }
 
   @Test
   public void oneTokenTest() {
 
-    String urlWithOneToken = "http://www.linkedin.com?project=" + PROJECT_TOKEN;
+    String urlWithOneToken =
+        "http://www.linkedin.com?project=" + PROJECT_TOKEN + "&another=yes";
 
-    String result = JobCallbackUtil.replaceToken(urlWithOneToken, contextInfo);
-    Assert.assertEquals("http://www.linkedin.com?project=" + PROJECT_NANE,
-        result);
+    String result =
+        JobCallbackUtil.replaceTokens(urlWithOneToken, contextInfo, true);
+    Assert.assertEquals("http://www.linkedin.com?project=" + PROJECT_NAME
+        + "&another=yes", result);
   }
 
   @Test
@@ -178,9 +184,53 @@ public class JobCallbackUtilTest {
         "http://www.linkedin.com?project=" + PROJECT_TOKEN + "&flow="
             + FLOW_TOKEN;
 
-    String result = JobCallbackUtil.replaceToken(urlWithOneToken, contextInfo);
-    Assert.assertEquals("http://www.linkedin.com?project=" + PROJECT_NANE
-        + "&flow=" + FLOW_NANE, result);
+    String result =
+        JobCallbackUtil.replaceTokens(urlWithOneToken, contextInfo, true);
+    Assert.assertEquals("http://www.linkedin.com?project=" + PROJECT_NAME
+        + "&flow=" + FLOW_NAME, result);
+  }
+
+  @Test
+  public void allTokensTest() {
+
+    String urlWithOneToken =
+        "http://www.linkedin.com?server=" + SERVER_NAME + "&project="
+            + PROJECT_TOKEN + "&flow=" + FLOW_TOKEN + "&executionId="
+            + EXECUTION_ID_TOKEN + "&job=" + JOB_TOKEN + "&status="
+            + JOB_STATUS_TOKEN;
+
+    String result =
+        JobCallbackUtil.replaceTokens(urlWithOneToken, contextInfo, true);
+
+    String expectedResult =
+        "http://www.linkedin.com?server=" + SERVER_NAME + "&project="
+            + PROJECT_NAME + "&flow=" + FLOW_NAME + "&executionId="
+            + EXECUTION_ID + "&job=" + JOB_NAME + "&status=" + JOB_STATUS_NAME;
+
+    Assert.assertEquals(expectedResult, result);
+  }
+
+  @Test
+  public void tokenWithEncoding() throws Exception {
+    String jobNameWithSpaces = "my job";
+    String encodedJobName = URLEncoder.encode(jobNameWithSpaces, "UTF-8");
+
+    Map<String, String> customContextInfo = new HashMap<String, String>();
+    customContextInfo = new HashMap<String, String>();
+    customContextInfo.put(SERVER_TOKEN, SERVER_NAME);
+    customContextInfo.put(PROJECT_TOKEN, PROJECT_NAME);
+    customContextInfo.put(FLOW_TOKEN, FLOW_NAME);
+    customContextInfo.put(EXECUTION_ID_TOKEN, EXECUTION_ID);
+    customContextInfo.put(JOB_TOKEN, jobNameWithSpaces);
+    customContextInfo.put(JOB_STATUS_TOKEN, JOB_STATUS_NAME);
+
+    String urlWithOneToken =
+        "http://www.linkedin.com?job=" + JOB_TOKEN + "&flow=" + FLOW_TOKEN;
+
+    String result =
+        JobCallbackUtil.replaceTokens(urlWithOneToken, customContextInfo, true);
+    Assert.assertEquals("http://www.linkedin.com?job=" + encodedJobName
+        + "&flow=" + FLOW_NAME, result);
   }
 
   @Test