You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rn...@apache.org on 2016/04/22 01:23:19 UTC
ambari git commit: AMBARI-16021. Update LogSearch integration to use
configuration to obtain credential. (rnettleton)
Repository: ambari
Updated Branches:
refs/heads/trunk 1c7a284b2 -> 9c1427150
AMBARI-16021. Update LogSearch integration to use configuration to obtain credential. (rnettleton)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9c142715
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9c142715
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9c142715
Branch: refs/heads/trunk
Commit: 9c142715019fb028bf9ee5acad5343dc4b0a9d04
Parents: 1c7a284
Author: Bob Nettleton <rn...@hortonworks.com>
Authored: Thu Apr 21 19:22:20 2016 -0400
Committer: Bob Nettleton <rn...@hortonworks.com>
Committed: Thu Apr 21 19:23:06 2016 -0400
----------------------------------------------------------------------
.../LoggingRequestHelperFactoryImpl.java | 2 +-
.../logging/LoggingRequestHelperImpl.java | 181 +++--
.../logging/LoggingRequestHelperImplTest.java | 672 +++++++++++++++++++
3 files changed, 793 insertions(+), 62 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/9c142715/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperFactoryImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperFactoryImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperFactoryImpl.java
index 970a92e..f892f04 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperFactoryImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperFactoryImpl.java
@@ -86,7 +86,7 @@ public class LoggingRequestHelperFactoryImpl implements LoggingRequestHelperFact
final String logSearchPortNumber =
logSearchSiteConfig.getProperties().get(LOGSEARCH_UI_PORT_PROPERTY_NAME);
- return new LoggingRequestHelperImpl(logSearchHostName, logSearchPortNumber, ambariManagementController.getCredentialStoreService(), clusterName);
+ return new LoggingRequestHelperImpl(logSearchHostName, logSearchPortNumber, ambariManagementController.getCredentialStoreService(), cluster);
}
} catch (AmbariException ambariException) {
LOG.error("Error occurred while trying to obtain the cluster, cluster name = " + clusterName, ambariException);
http://git-wip-us.apache.org/repos/asf/ambari/blob/9c142715/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImpl.java
index 624977e..a5cd369 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImpl.java
@@ -23,6 +23,8 @@ import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.security.credential.Credential;
import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Config;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.utils.URIBuilder;
import org.apache.log4j.Logger;
@@ -53,15 +55,17 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
private static Logger LOG = Logger.getLogger(LoggingRequestHelperImpl.class);
- public static String LOGSEARCH_QUERY_PATH = "/service/dashboard/solr/logs_search";
+ private static final String LOGSEARCH_ADMIN_PROPERTIES_CONFIG_TYPE_NAME = "logsearch-admin-properties";
- public static String LOGSEARCH_GET_LOG_LEVELS_PATH = "/service/dashboard/getLogLevelCounts";
+ private static final String LOGSEARCH_ADMIN_USERNAME_PROPERTY_NAME = "logsearch_admin_username";
- private static String DEFAULT_LOGSEARCH_USER = "admin";
+ private static final String LOGSEARCH_ADMIN_PASSWORD_PROPERTY_NAME = "logsearch_admin_password";
- private static String DEFAULT_LOGSEARCH_PWD = "admin";
+ private static final String LOGSEARCH_QUERY_PATH = "/service/dashboard/solr/logs_search";
- public static final String LOGSEARCH_ADMIN_CREDENTIAL_NAME = "logsearch.admin.credential";
+ private static final String LOGSEARCH_GET_LOG_LEVELS_PATH = "/service/dashboard/getLogLevelCounts";
+
+ private static final String LOGSEARCH_ADMIN_CREDENTIAL_NAME = "logsearch.admin.credential";
private final String hostName;
@@ -69,13 +73,20 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
private final CredentialStoreService credentialStoreService;
- private final String clusterName;
+ private final Cluster cluster;
+
+ private final NetworkConnection networkConnection;
+
+ public LoggingRequestHelperImpl(String hostName, String portNumber, CredentialStoreService credentialStoreService, Cluster cluster) {
+ this(hostName, portNumber, credentialStoreService, cluster, new DefaultNetworkConnection());
+ }
- public LoggingRequestHelperImpl(String hostName, String portNumber, CredentialStoreService credentialStoreService, String clusterName) {
+ protected LoggingRequestHelperImpl(String hostName, String portNumber, CredentialStoreService credentialStoreService, Cluster cluster, NetworkConnection networkConnection) {
this.hostName = hostName;
this.portNumber = portNumber;
this.credentialStoreService = credentialStoreService;
- this.clusterName = clusterName;
+ this.cluster = cluster;
+ this.networkConnection = networkConnection;
}
public LogQueryResponse sendQueryRequest(Map<String, String> queryParameters) {
@@ -89,7 +100,7 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
setupCredentials(httpURLConnection);
- StringBuffer buffer = readQueryResponseFromServer(httpURLConnection);
+ StringBuffer buffer = networkConnection.readQueryResponseFromServer(httpURLConnection);
// setup a reader for the JSON response
StringReader stringReader =
@@ -108,21 +119,55 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
}
private void setupCredentials(HttpURLConnection httpURLConnection) {
- PrincipalKeyCredential principalKeyCredential =
- getLogSearchCredentials();
-
- // determine the credential to use for connecting to LogSearch
- if (principalKeyCredential != null) {
- // setup credential stored in credential service
- LOG.debug("Credential found in CredentialStore, will be used to connect to LogSearch");
- setupBasicAuthentication(httpURLConnection, createEncodedCredentials(principalKeyCredential));
+ final String logSearchAdminUser =
+ getLogSearchAdminUser();
+ final String logSearchAdminPassword =
+ getLogSearchAdminPassword();
+
+ // first attempt to use the LogSearch admin configuration to
+ // obtain the LogSearch server credential
+ if ((logSearchAdminUser != null) && (logSearchAdminPassword != null)) {
+ LOG.debug("Credential found in config, will be used to connect to LogSearch");
+ networkConnection.setupBasicAuthentication(httpURLConnection, createEncodedCredentials(logSearchAdminUser, logSearchAdminPassword));
} else {
- // fall back to hard-coded credential for now
- LOG.debug("No credential found in CredentialStore, defaulting to fall-back credential for now");
- setupBasicAuthentication(httpURLConnection, createDefaultEncodedCredentials());
+ // if no credential found in config, attempt to locate the credential using
+ // the Ambari CredentialStoreService
+ PrincipalKeyCredential principalKeyCredential =
+ getLogSearchCredentials();
+
+ // determine the credential to use for connecting to LogSearch
+ if (principalKeyCredential != null) {
+ // setup credential stored in credential service
+ LOG.debug("Credential found in CredentialStore, will be used to connect to LogSearch");
+ networkConnection.setupBasicAuthentication(httpURLConnection, createEncodedCredentials(principalKeyCredential));
+ } else {
+ LOG.debug("No LogSearch credential could be found, this is probably an error in configuration");
+ }
}
}
+ private String getLogSearchAdminUser() {
+ Config logSearchAdminConfig =
+ cluster.getDesiredConfigByType(LOGSEARCH_ADMIN_PROPERTIES_CONFIG_TYPE_NAME);
+
+ if (logSearchAdminConfig != null) {
+ return logSearchAdminConfig.getProperties().get(LOGSEARCH_ADMIN_USERNAME_PROPERTY_NAME);
+ }
+
+ return null;
+ }
+
+ private String getLogSearchAdminPassword() {
+ Config logSearchAdminConfig =
+ cluster.getDesiredConfigByType(LOGSEARCH_ADMIN_PROPERTIES_CONFIG_TYPE_NAME);
+
+ if (logSearchAdminConfig != null) {
+ return logSearchAdminConfig.getProperties().get(LOGSEARCH_ADMIN_PASSWORD_PROPERTY_NAME);
+ }
+
+ return null;
+ }
+
public Set<String> sendGetLogFileNamesRequest(String componentName, String hostName) {
Map<String, String> queryParameters =
new HashMap<String, String>();
@@ -161,7 +206,7 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
setupCredentials(httpURLConnection);
- StringBuffer buffer = readQueryResponseFromServer(httpURLConnection);
+ StringBuffer buffer = networkConnection.readQueryResponseFromServer(httpURLConnection);
// setup a reader for the JSON response
StringReader stringReader =
@@ -185,9 +230,6 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
return mapper.reader(type);
}
-
-
-
private URI createLogSearchQueryURI(String scheme, Map<String, String> queryParameters) throws URISyntaxException {
URIBuilder uriBuilder = createBasicURI(scheme);
uriBuilder.setPath(LOGSEARCH_QUERY_PATH);
@@ -238,36 +280,10 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
return mapper;
}
- private StringBuffer readQueryResponseFromServer(HttpURLConnection httpURLConnection) throws IOException {
- InputStream resultStream = null;
- try {
- // read in the response from LogSearch
- resultStream = httpURLConnection.getInputStream();
- BufferedReader reader = new BufferedReader(new InputStreamReader(resultStream));
- LOG.debug("Response code from LogSearch Service is = " + httpURLConnection.getResponseCode());
-
- String line = reader.readLine();
- StringBuffer buffer = new StringBuffer();
- while (line != null) {
- buffer.append(line);
- line = reader.readLine();
- }
-
- LOG.debug("Sucessfully retrieved response from server, response = " + buffer);
-
- return buffer;
- } finally {
- // make sure to close the stream after request is completed
- if (resultStream != null) {
- resultStream.close();
- }
- }
- }
-
private PrincipalKeyCredential getLogSearchCredentials() {
try {
Credential credential =
- credentialStoreService.getCredential(clusterName, LOGSEARCH_ADMIN_CREDENTIAL_NAME);
+ credentialStoreService.getCredential(cluster.getClusterName(), LOGSEARCH_ADMIN_CREDENTIAL_NAME);
if ((credential != null) && (credential instanceof PrincipalKeyCredential)) {
return (PrincipalKeyCredential)credential;
}
@@ -281,19 +297,9 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
LOG.error("Error encountered while trying to obtain LogSearch admin credentials.", ambariException);
}
-
return null;
}
- private static void setupBasicAuthentication(HttpURLConnection httpURLConnection, String encodedCredentials) {
- httpURLConnection.setRequestProperty("Authorization", "Basic " + encodedCredentials);
- }
-
- // might need to remove this once the credential integration is in place
- private static String createDefaultEncodedCredentials() {
- return createEncodedCredentials(DEFAULT_LOGSEARCH_USER, DEFAULT_LOGSEARCH_PWD);
- }
-
private static String createEncodedCredentials(PrincipalKeyCredential principalKeyCredential) {
return createEncodedCredentials(principalKeyCredential.getPrincipal(), new String(principalKeyCredential.getKey()));
}
@@ -302,5 +308,58 @@ public class LoggingRequestHelperImpl implements LoggingRequestHelper {
return Base64.encodeBase64String((userName + ":" + password).getBytes());
}
+ /**
+ * Interface used to abstract out the network access needed to
+ * connect to the LogSearch Server.
+ *
+ * This abstraction is useful for unit testing this class, and simulating
+ * different output and error conditions.
+ */
+ interface NetworkConnection {
+ StringBuffer readQueryResponseFromServer(HttpURLConnection httpURLConnection) throws IOException;
+
+ void setupBasicAuthentication(HttpURLConnection httpURLConnection, String encodedCredentials);
+ }
+
+ /**
+ * The default implementation of NetworkConnection, that reads
+ * the InputStream associated with the HttpURL connection passed in.
+ */
+ private static class DefaultNetworkConnection implements NetworkConnection {
+ @Override
+ public StringBuffer readQueryResponseFromServer(HttpURLConnection httpURLConnection) throws IOException {
+ InputStream resultStream = null;
+ try {
+ // read in the response from LogSearch
+ resultStream = httpURLConnection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(resultStream));
+ LOG.debug("Response code from LogSearch Service is = " + httpURLConnection.getResponseCode());
+
+ String line = reader.readLine();
+ StringBuffer buffer = new StringBuffer();
+ while (line != null) {
+ buffer.append(line);
+ line = reader.readLine();
+ }
+
+ LOG.debug("Sucessfully retrieved response from server, response = " + buffer);
+
+ return buffer;
+ } finally {
+ // make sure to close the stream after request is completed
+ if (resultStream != null) {
+ resultStream.close();
+ }
+ }
+ }
+
+ @Override
+ public void setupBasicAuthentication(HttpURLConnection httpURLConnection, String encodedCredentials) {
+ // default implementation for this method should just set the Authorization header
+ // required for Basic Authentication to the LogSearch Server
+ httpURLConnection.setRequestProperty("Authorization", "Basic " + encodedCredentials);
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/9c142715/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImplTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImplTest.java
new file mode 100644
index 0000000..ceb11d8
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/logging/LoggingRequestHelperImplTest.java
@@ -0,0 +1,672 @@
+/**
+ * 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.ambari.server.controller.logging;
+
+import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
+import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Config;
+import org.apache.commons.codec.binary.Base64;
+import org.easymock.Capture;
+import org.easymock.EasyMockSupport;
+import org.junit.Test;
+
+import java.net.HttpURLConnection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+
+public class LoggingRequestHelperImplTest {
+
+ private static final String TEST_JSON_INPUT_TWO_LIST_ENTRIES =
+ "{" +
+ " \"startIndex\" : 0," +
+ " \"pageSize\" : 5," +
+ " \"totalCount\" : 10452," +
+ " \"resultSize\" : 5," +
+ " \"queryTimeMS\" : 1458148754113," +
+ " \"logList\" : [ {" +
+ " \"cluster\" : \"clusterone\"," +
+ " \"method\" : \"chooseUnderReplicatedBlocks\"," +
+ " \"level\" : \"INFO\"," +
+ " \"event_count\" : 1," +
+ " \"ip\" : \"192.168.1.1\"," +
+ " \"type\" : \"hdfs_namenode\"," +
+ " \"seq_num\" : 10584," +
+ " \"path\" : \"/var/log/hadoop/hdfs/hadoop-hdfs-namenode-c6401.ambari.apache.org.log\"," +
+ " \"file\" : \"UnderReplicatedBlocks.java\"," +
+ " \"line_number\" : 394," +
+ " \"host\" : \"c6401.ambari.apache.org\"," +
+ " \"log_message\" : \"chooseUnderReplicatedBlocks selected 2 blocks at priority level 0; Total=2 Reset bookmarks? false\"," +
+ " \"logger_name\" : \"BlockStateChange\"," +
+ " \"id\" : \"9c5562fb-123f-47c8-aaf5-b5e407326c08\"," +
+ " \"message_md5\" : \"-3892769501348410581\"," +
+ " \"logtime\" : 1458148749036," +
+ " \"event_md5\" : \"1458148749036-2417481968206345035\"," +
+ " \"logfile_line_number\" : 2084," +
+ " \"_ttl_\" : \"+7DAYS\"," +
+ " \"_expire_at_\" : 1458753550322," +
+ " \"_version_\" : 1528979784023932928" +
+ " }, {" +
+ " \"cluster\" : \"clusterone\"," +
+ " \"method\" : \"putMetrics\"," +
+ " \"level\" : \"WARN\"," +
+ " \"event_count\" : 1," +
+ " \"ip\" : \"192.168.1.1\"," +
+ " \"type\" : \"yarn_resourcemanager\"," +
+ " \"seq_num\" : 10583," +
+ " \"path\" : \"/var/log/hadoop-yarn/yarn/yarn-yarn-resourcemanager-c6401.ambari.apache.org.log\"," +
+ " \"file\" : \"HadoopTimelineMetricsSink.java\"," +
+ " \"line_number\" : 262," +
+ " \"host\" : \"c6401.ambari.apache.org\"," +
+ " \"log_message\" : \"Unable to send metrics to collector by address:http://c6401.ambari.apache.org:6188/ws/v1/timeline/metrics\"," +
+ " \"logger_name\" : \"timeline.HadoopTimelineMetricsSink\"," +
+ " \"id\" : \"8361c5a9-5b1c-4f44-bc8f-4c6f07d94228\"," +
+ " \"message_md5\" : \"5942185045779825717\"," +
+ " \"logtime\" : 1458148746937," +
+ " \"event_md5\" : \"14581487469371427138486123628676\"," +
+ " \"logfile_line_number\" : 549," +
+ " \"_ttl_\" : \"+7DAYS\"," +
+ " \"_expire_at_\" : 1458753550322," +
+ " \"_version_\" : 1528979784022884357" +
+ " }" +
+ "]" +
+ "}";
+
+ private static final String TEST_JSON_INPUT_LOG_LEVEL_QUERY =
+ "{\"pageSize\":\"0\",\"queryTimeMS\":\"1459970731998\",\"resultSize\":\"6\",\"startIndex\":\"0\",\"totalCount\":\"0\"," +
+ "\"vNameValues\":[{\"name\":\"FATAL\",\"value\":\"0\"},{\"name\":\"ERROR\",\"value\":\"0\"}," +
+ "{\"name\":\"WARN\",\"value\":\"41\"},{\"name\":\"INFO\",\"value\":\"186\"},{\"name\":\"DEBUG\",\"value\":\"0\"}," +
+ "{\"name\":\"TRACE\",\"value\":\"0\"}]}";
+
+
+ private final String EXPECTED_HOST_NAME = "c6401.ambari.apache.org";
+
+ private final String EXPECTED_PORT_NUMBER = "61888";
+
+ private static final String EXPECTED_USER_NAME = "admin-user";
+
+ private static final String EXPECTED_ADMIN_PASSWORD = "admin-pwd";
+
+ private static final String EXPECTED_ENCODED_CREDENTIALS =
+ Base64.encodeBase64String((EXPECTED_USER_NAME + ":" + EXPECTED_ADMIN_PASSWORD).getBytes());
+
+
+
+ @Test
+ public void testLogQueryRequestBasic() throws Exception {
+ EasyMockSupport mockSupport =
+ new EasyMockSupport();
+
+ CredentialStoreService credentialStoreServiceMock =
+ mockSupport.createMock(CredentialStoreService.class);
+
+ Cluster clusterMock =
+ mockSupport.createMock(Cluster.class);
+
+ LoggingRequestHelperImpl.NetworkConnection networkConnectionMock =
+ mockSupport.createMock(LoggingRequestHelperImpl.NetworkConnection.class);
+
+ Config adminPropertiesConfigMock =
+ mockSupport.createMock(Config.class);
+
+ Map<String, String> testConfigProperties =
+ new HashMap<String, String>();
+
+ testConfigProperties.put("logsearch_admin_username", EXPECTED_USER_NAME);
+ testConfigProperties.put("logsearch_admin_password", EXPECTED_ADMIN_PASSWORD);
+ testConfigProperties = Collections.unmodifiableMap(testConfigProperties);
+
+ Capture<HttpURLConnection> captureURLConnection = new Capture<HttpURLConnection>();
+ Capture<HttpURLConnection> captureURLConnectionForAuthentication = new Capture<HttpURLConnection>();
+
+ expect(clusterMock.getDesiredConfigByType("logsearch-admin-properties")).andReturn(adminPropertiesConfigMock).atLeastOnce();
+ expect(adminPropertiesConfigMock.getProperties()).andReturn(testConfigProperties).atLeastOnce();
+ expect(networkConnectionMock.readQueryResponseFromServer(capture(captureURLConnection))).andReturn(new StringBuffer(TEST_JSON_INPUT_TWO_LIST_ENTRIES)).atLeastOnce();
+
+ // expect that basic authentication is setup, with the expected encoded credentials
+ networkConnectionMock.setupBasicAuthentication(capture(captureURLConnectionForAuthentication), eq(EXPECTED_ENCODED_CREDENTIALS));
+
+ mockSupport.replayAll();
+
+ LoggingRequestHelper helper =
+ new LoggingRequestHelperImpl(EXPECTED_HOST_NAME, EXPECTED_PORT_NUMBER, credentialStoreServiceMock, clusterMock, networkConnectionMock);
+
+ // invoke query request
+ LogQueryResponse result =
+ helper.sendQueryRequest(Collections.<String,String>emptyMap());
+
+ // verify that the HttpURLConnection was created with the proper values
+ HttpURLConnection httpURLConnection =
+ captureURLConnection.getValue();
+
+ assertEquals("URLConnection did not have the correct hostname information",
+ EXPECTED_HOST_NAME, httpURLConnection.getURL().getHost());
+ assertEquals("URLConnection did not have the correct port information",
+ EXPECTED_PORT_NUMBER, httpURLConnection.getURL().getPort() + "");
+ assertEquals("URLConnection did not have the expected http protocol scheme",
+ "http", httpURLConnection.getURL().getProtocol());
+ assertEquals("URLConnection did not have the expected method set",
+ "GET", httpURLConnection.getRequestMethod());
+
+ assertSame("HttpUrlConnection instances passed into NetworkConnection mock should have been the same instance",
+ httpURLConnection, captureURLConnectionForAuthentication.getValue());
+
+
+ assertNotNull("Response object should not be null",
+ result);
+
+ // verify that the JSON response returned from the simulated server
+ // is parsed properly, and has the expected values
+ assertEquals("startIndex not parsed properly",
+ "0", result.getStartIndex());
+ assertEquals("pageSize not parsed properly",
+ "5", result.getPageSize());
+ assertEquals("totalCount not parsed properly",
+ "10452", result.getTotalCount());
+ assertEquals("resultSize not parsed properly",
+ "5", result.getResultSize());
+ assertEquals("queryTimeMS not parsed properly",
+ "1458148754113", result.getQueryTimeMS());
+
+ assertEquals("incorrect number of LogLineResult items parsed",
+ 2, result.getListOfResults().size());
+
+ List<LogLineResult> listOfLineResults =
+ result.getListOfResults();
+
+ {
+ LogLineResult resultOne = listOfLineResults.get(0);
+ // verify that all fields in this class are parsed as expected
+ assertEquals("Cluster name not parsed properly",
+ "clusterone", resultOne.getClusterName());
+ assertEquals("Method Name not parsed properly",
+ "chooseUnderReplicatedBlocks", resultOne.getLogMethod());
+ assertEquals("Log Level not parsed properly",
+ "INFO", resultOne.getLogLevel());
+ assertEquals("event_count not parsed properly",
+ "1", resultOne.getEventCount());
+ assertEquals("ip address not parsed properly",
+ "192.168.1.1", resultOne.getIpAddress());
+ assertEquals("component type not parsed properly",
+ "hdfs_namenode", resultOne.getComponentType());
+ assertEquals("sequence number not parsed properly",
+ "10584", resultOne.getSequenceNumber());
+ assertEquals("log file path not parsed properly",
+ "/var/log/hadoop/hdfs/hadoop-hdfs-namenode-c6401.ambari.apache.org.log", resultOne.getLogFilePath());
+ assertEquals("log src file name not parsed properly",
+ "UnderReplicatedBlocks.java", resultOne.getSourceFile());
+ assertEquals("log src line number not parsed properly",
+ "394", resultOne.getSourceFileLineNumber());
+ assertEquals("host name not parsed properly",
+ "c6401.ambari.apache.org", resultOne.getHostName());
+ assertEquals("log message not parsed properly",
+ "chooseUnderReplicatedBlocks selected 2 blocks at priority level 0; Total=2 Reset bookmarks? false", resultOne.getLogMessage());
+ assertEquals("logger name not parsed properly",
+ "BlockStateChange", resultOne.getLoggerName());
+ assertEquals("id not parsed properly",
+ "9c5562fb-123f-47c8-aaf5-b5e407326c08", resultOne.getId());
+ assertEquals("message MD5 not parsed properly",
+ "-3892769501348410581", resultOne.getMessageMD5());
+ assertEquals("log time not parsed properly",
+ "1458148749036", resultOne.getLogTime());
+ assertEquals("event MD5 not parsed properly",
+ "1458148749036-2417481968206345035", resultOne.getEventMD5());
+ assertEquals("logfile line number not parsed properly",
+ "2084", resultOne.getLogFileLineNumber());
+ assertEquals("ttl not parsed properly",
+ "+7DAYS", resultOne.getTtl());
+ assertEquals("expire at not parsed properly",
+ "1458753550322", resultOne.getExpirationTime());
+ assertEquals("version not parsed properly",
+ "1528979784023932928", resultOne.getVersion());
+ }
+
+ {
+ LogLineResult resultTwo = listOfLineResults.get(1);
+ // verify second log line record's data is parsed correctly
+ assertEquals("Cluster name not parsed properly",
+ "clusterone", resultTwo.getClusterName());
+ assertEquals("Method Name not parsed properly",
+ "putMetrics", resultTwo.getLogMethod());
+ assertEquals("Log Level not parsed properly",
+ "WARN", resultTwo.getLogLevel());
+ assertEquals("event_count not parsed properly",
+ "1", resultTwo.getEventCount());
+ assertEquals("ip address not parsed properly",
+ "192.168.1.1", resultTwo.getIpAddress());
+ assertEquals("component type not parsed properly",
+ "yarn_resourcemanager", resultTwo.getComponentType());
+ assertEquals("sequence number not parsed properly",
+ "10583", resultTwo.getSequenceNumber());
+ assertEquals("log file path not parsed properly",
+ "/var/log/hadoop-yarn/yarn/yarn-yarn-resourcemanager-c6401.ambari.apache.org.log", resultTwo.getLogFilePath());
+ assertEquals("log src file name not parsed properly",
+ "HadoopTimelineMetricsSink.java", resultTwo.getSourceFile());
+ assertEquals("log src line number not parsed properly",
+ "262", resultTwo.getSourceFileLineNumber());
+ assertEquals("host name not parsed properly",
+ "c6401.ambari.apache.org", resultTwo.getHostName());
+ assertEquals("log message not parsed properly",
+ "Unable to send metrics to collector by address:http://c6401.ambari.apache.org:6188/ws/v1/timeline/metrics", resultTwo.getLogMessage());
+ assertEquals("logger name not parsed properly",
+ "timeline.HadoopTimelineMetricsSink", resultTwo.getLoggerName());
+ assertEquals("id not parsed properly",
+ "8361c5a9-5b1c-4f44-bc8f-4c6f07d94228", resultTwo.getId());
+ assertEquals("message MD5 not parsed properly",
+ "5942185045779825717", resultTwo.getMessageMD5());
+ assertEquals("log time not parsed properly",
+ "1458148746937", resultTwo.getLogTime());
+ assertEquals("event MD5 not parsed properly",
+ "14581487469371427138486123628676", resultTwo.getEventMD5());
+ assertEquals("logfile line number not parsed properly",
+ "549", resultTwo.getLogFileLineNumber());
+ assertEquals("ttl not parsed properly",
+ "+7DAYS", resultTwo.getTtl());
+ assertEquals("expire at not parsed properly",
+ "1458753550322", resultTwo.getExpirationTime());
+ assertEquals("version not parsed properly",
+ "1528979784022884357", resultTwo.getVersion());
+ }
+
+ mockSupport.verifyAll();
+ }
+
+ @Test
+ public void testLogLevelRequestBasic() throws Exception {
+ EasyMockSupport mockSupport =
+ new EasyMockSupport();
+
+ CredentialStoreService credentialStoreServiceMock =
+ mockSupport.createMock(CredentialStoreService.class);
+
+ Cluster clusterMock =
+ mockSupport.createMock(Cluster.class);
+
+ LoggingRequestHelperImpl.NetworkConnection networkConnectionMock =
+ mockSupport.createMock(LoggingRequestHelperImpl.NetworkConnection.class);
+
+ Config adminPropertiesConfigMock =
+ mockSupport.createMock(Config.class);
+
+ Map<String, String> testConfigProperties =
+ new HashMap<String, String>();
+ testConfigProperties.put("logsearch_admin_username", "admin-user");
+ testConfigProperties.put("logsearch_admin_password", "admin-pwd");
+ testConfigProperties = Collections.unmodifiableMap(testConfigProperties);
+
+ Capture<HttpURLConnection> captureURLConnection = new Capture<HttpURLConnection>();
+ Capture<HttpURLConnection> captureURLConnectionForAuthentication = new Capture<HttpURLConnection>();
+
+ expect(clusterMock.getDesiredConfigByType("logsearch-admin-properties")).andReturn(adminPropertiesConfigMock).atLeastOnce();
+ expect(adminPropertiesConfigMock.getProperties()).andReturn(testConfigProperties).atLeastOnce();
+ expect(networkConnectionMock.readQueryResponseFromServer(capture(captureURLConnection))).andReturn(new StringBuffer(TEST_JSON_INPUT_LOG_LEVEL_QUERY)).atLeastOnce();
+
+ // expect that basic authentication is setup, with the expected encoded credentials
+ networkConnectionMock.setupBasicAuthentication(capture(captureURLConnectionForAuthentication), eq(EXPECTED_ENCODED_CREDENTIALS));
+
+
+ mockSupport.replayAll();
+
+
+ LoggingRequestHelper helper =
+ new LoggingRequestHelperImpl(EXPECTED_HOST_NAME, EXPECTED_PORT_NUMBER, credentialStoreServiceMock, clusterMock, networkConnectionMock);
+
+ // invoke query request
+ LogLevelQueryResponse result =
+ helper.sendLogLevelQueryRequest("hdfs_datanode", EXPECTED_HOST_NAME);
+
+ // verify that the HttpURLConnection was created with the proper values
+ HttpURLConnection httpURLConnection =
+ captureURLConnection.getValue();
+
+ assertEquals("URLConnection did not have the correct hostname information",
+ EXPECTED_HOST_NAME, httpURLConnection.getURL().getHost());
+ assertEquals("URLConnection did not have the correct port information",
+ EXPECTED_PORT_NUMBER, httpURLConnection.getURL().getPort() + "");
+ assertEquals("URLConnection did not have the expected http protocol scheme",
+ "http", httpURLConnection.getURL().getProtocol());
+ assertEquals("URLConnection did not have the expected method set",
+ "GET", httpURLConnection.getRequestMethod());
+
+ assertSame("HttpUrlConnection instances passed into NetworkConnection mock should have been the same instance",
+ httpURLConnection, captureURLConnectionForAuthentication.getValue());
+
+ assertNotNull("Response object should not be null",
+ result);
+
+ // expected values taken from JSON input string declared above
+ assertEquals("startIndex not parsed properly",
+ "0", result.getStartIndex());
+ assertEquals("pageSize not parsed properly",
+ "0", result.getPageSize());
+ assertEquals("totalCount not parsed properly",
+ "0", result.getTotalCount());
+ assertEquals("resultSize not parsed properly",
+ "6", result.getResultSize());
+ assertEquals("queryTimeMS not parsed properly",
+ "1459970731998", result.getQueryTimeMS());
+
+ assertEquals("Incorrect number of log level count items parsed",
+ 6, result.getNameValueList().size());
+
+ List<NameValuePair> resultList =
+ result.getNameValueList();
+ assertNameValuePair("FATAL", "0", resultList.get(0));
+ assertNameValuePair("ERROR", "0", resultList.get(1));
+ assertNameValuePair("WARN", "41", resultList.get(2));
+ assertNameValuePair("INFO", "186", resultList.get(3));
+ assertNameValuePair("DEBUG", "0", resultList.get(4));
+ assertNameValuePair("TRACE", "0", resultList.get(5));
+
+ mockSupport.verifyAll();
+ }
+
+ @Test
+ public void testLogFileNameRequestBasic() throws Exception {
+ final String expectedComponentName = "hdfs_namenode";
+
+ EasyMockSupport mockSupport =
+ new EasyMockSupport();
+
+ CredentialStoreService credentialStoreServiceMock =
+ mockSupport.createMock(CredentialStoreService.class);
+
+ Cluster clusterMock =
+ mockSupport.createMock(Cluster.class);
+
+ LoggingRequestHelperImpl.NetworkConnection networkConnectionMock =
+ mockSupport.createMock(LoggingRequestHelperImpl.NetworkConnection.class);
+
+ Config adminPropertiesConfigMock =
+ mockSupport.createMock(Config.class);
+
+ Map<String, String> testConfigProperties =
+ new HashMap<String, String>();
+ testConfigProperties.put("logsearch_admin_username", "admin-user");
+ testConfigProperties.put("logsearch_admin_password", "admin-pwd");
+ testConfigProperties = Collections.unmodifiableMap(testConfigProperties);
+
+ Capture<HttpURLConnection> captureURLConnection = new Capture<HttpURLConnection>();
+ Capture<HttpURLConnection> captureURLConnectionForAuthentication = new Capture<HttpURLConnection>();
+
+ expect(clusterMock.getDesiredConfigByType("logsearch-admin-properties")).andReturn(adminPropertiesConfigMock).atLeastOnce();
+ expect(adminPropertiesConfigMock.getProperties()).andReturn(testConfigProperties).atLeastOnce();
+ expect(networkConnectionMock.readQueryResponseFromServer(capture(captureURLConnection))).andReturn(new StringBuffer(TEST_JSON_INPUT_TWO_LIST_ENTRIES)).atLeastOnce();
+
+ // expect that basic authentication is setup, with the expected encoded credentials
+ networkConnectionMock.setupBasicAuthentication(capture(captureURLConnectionForAuthentication), eq(EXPECTED_ENCODED_CREDENTIALS));
+
+ mockSupport.replayAll();
+
+ LoggingRequestHelper helper =
+ new LoggingRequestHelperImpl(EXPECTED_HOST_NAME, EXPECTED_PORT_NUMBER, credentialStoreServiceMock, clusterMock, networkConnectionMock);
+
+ // invoke query request
+ Set<String> result =
+ helper.sendGetLogFileNamesRequest(expectedComponentName, EXPECTED_HOST_NAME);
+
+ // verify that the HttpURLConnection was created with the propert values
+ HttpURLConnection httpURLConnection =
+ captureURLConnection.getValue();
+
+ assertEquals("URLConnection did not have the correct hostname information",
+ EXPECTED_HOST_NAME, httpURLConnection.getURL().getHost());
+ assertEquals("URLConnection did not have the correct port information",
+ EXPECTED_PORT_NUMBER, httpURLConnection.getURL().getPort() + "");
+ assertEquals("URLConnection did not have the expected http protocol scheme",
+ "http", httpURLConnection.getURL().getProtocol());
+ assertEquals("URLConnection did not have the expected method set",
+ "GET", httpURLConnection.getRequestMethod());
+
+ assertSame("HttpUrlConnection instances passed into NetworkConnection mock should have been the same instance",
+ httpURLConnection, captureURLConnectionForAuthentication.getValue());
+
+ final String resultQuery =
+ httpURLConnection.getURL().getQuery();
+
+ // verify that the query contains the three required parameters
+ assertTrue("host parameter was not included in query",
+ resultQuery.contains("host=c6401.ambari.apache.org"));
+ assertTrue("components_name parameter was not included in the query",
+ resultQuery.contains("components_name=" + expectedComponentName));
+ assertTrue("pageSize parameter was not included in query",
+ resultQuery.contains("pageSize=1"));
+
+ assertNotNull("Response object should not be null",
+ result);
+ assertEquals("Response Set was not of the expected size",
+ 1, result.size());
+ assertEquals("Response did not include the expected file name",
+ "/var/log/hadoop/hdfs/hadoop-hdfs-namenode-c6401.ambari.apache.org.log",
+ result.iterator().next());
+ }
+
+ /**
+ * Verifies that if the LogSearch admin configuration for user/password credentials
+ * is not available, the integration layer will attempt to locate the LogSearch credential
+ * in the CredentialStoreService as a fallback mechanism.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testLogQueryRequestBasicCredentialsNotInConfig() throws Exception {
+ final String expectedClusterName = "my-test-cluster";
+
+ EasyMockSupport mockSupport =
+ new EasyMockSupport();
+
+ CredentialStoreService credentialStoreServiceMock =
+ mockSupport.createMock(CredentialStoreService.class);
+
+ Cluster clusterMock =
+ mockSupport.createMock(Cluster.class);
+
+ LoggingRequestHelperImpl.NetworkConnection networkConnectionMock =
+ mockSupport.createMock(LoggingRequestHelperImpl.NetworkConnection.class);
+
+ Config adminPropertiesConfigMock =
+ mockSupport.createMock(Config.class);
+
+ Capture<HttpURLConnection> captureURLConnection = new Capture<HttpURLConnection>();
+ Capture<HttpURLConnection> captureURLConnectionForAuthentication = new Capture<HttpURLConnection>();
+
+ expect(clusterMock.getDesiredConfigByType("logsearch-admin-properties")).andReturn(adminPropertiesConfigMock).atLeastOnce();
+ expect(clusterMock.getClusterName()).andReturn(expectedClusterName).atLeastOnce();
+ expect(adminPropertiesConfigMock.getProperties()).andReturn(Collections.<String, String>emptyMap()).atLeastOnce();
+ expect(networkConnectionMock.readQueryResponseFromServer(capture(captureURLConnection))).andReturn(new StringBuffer(TEST_JSON_INPUT_TWO_LIST_ENTRIES)).atLeastOnce();
+ // the credential store service should be consulted in this case, in order
+ // to attempt to obtain the LogSearch credential from the store
+ expect(credentialStoreServiceMock.getCredential(expectedClusterName, "logsearch.admin.credential")).andReturn(new PrincipalKeyCredential(EXPECTED_USER_NAME, EXPECTED_ADMIN_PASSWORD)).atLeastOnce();
+
+ // expect that basic authentication is setup, with the expected encoded credentials
+ networkConnectionMock.setupBasicAuthentication(capture(captureURLConnectionForAuthentication), eq(EXPECTED_ENCODED_CREDENTIALS));
+
+ mockSupport.replayAll();
+
+ LoggingRequestHelper helper =
+ new LoggingRequestHelperImpl(EXPECTED_HOST_NAME, EXPECTED_PORT_NUMBER, credentialStoreServiceMock, clusterMock, networkConnectionMock);
+
+ // invoke query request
+ LogQueryResponse result =
+ helper.sendQueryRequest(Collections.<String,String>emptyMap());
+
+ // verify that the HttpURLConnection was created with the proper values
+ HttpURLConnection httpURLConnection =
+ captureURLConnection.getValue();
+
+ assertEquals("URLConnection did not have the correct hostname information",
+ EXPECTED_HOST_NAME, httpURLConnection.getURL().getHost());
+ assertEquals("URLConnection did not have the correct port information",
+ EXPECTED_PORT_NUMBER, httpURLConnection.getURL().getPort() + "");
+ assertEquals("URLConnection did not have the expected http protocol scheme",
+ "http", httpURLConnection.getURL().getProtocol());
+ assertEquals("URLConnection did not have the expected method set",
+ "GET", httpURLConnection.getRequestMethod());
+
+ assertSame("HttpUrlConnection instances passed into NetworkConnection mock should have been the same instance",
+ httpURLConnection, captureURLConnectionForAuthentication.getValue());
+
+
+ assertNotNull("Response object should not be null",
+ result);
+
+ // verify that the JSON response returned from the simulated server
+ // is parsed properly, and has the expected values
+ assertEquals("startIndex not parsed properly",
+ "0", result.getStartIndex());
+ assertEquals("pageSize not parsed properly",
+ "5", result.getPageSize());
+ assertEquals("totalCount not parsed properly",
+ "10452", result.getTotalCount());
+ assertEquals("resultSize not parsed properly",
+ "5", result.getResultSize());
+ assertEquals("queryTimeMS not parsed properly",
+ "1458148754113", result.getQueryTimeMS());
+
+ assertEquals("incorrect number of LogLineResult items parsed",
+ 2, result.getListOfResults().size());
+
+ List<LogLineResult> listOfLineResults =
+ result.getListOfResults();
+
+ {
+ LogLineResult resultOne = listOfLineResults.get(0);
+ // verify that all fields in this class are parsed as expected
+ assertEquals("Cluster name not parsed properly",
+ "clusterone", resultOne.getClusterName());
+ assertEquals("Method Name not parsed properly",
+ "chooseUnderReplicatedBlocks", resultOne.getLogMethod());
+ assertEquals("Log Level not parsed properly",
+ "INFO", resultOne.getLogLevel());
+ assertEquals("event_count not parsed properly",
+ "1", resultOne.getEventCount());
+ assertEquals("ip address not parsed properly",
+ "192.168.1.1", resultOne.getIpAddress());
+ assertEquals("component type not parsed properly",
+ "hdfs_namenode", resultOne.getComponentType());
+ assertEquals("sequence number not parsed properly",
+ "10584", resultOne.getSequenceNumber());
+ assertEquals("log file path not parsed properly",
+ "/var/log/hadoop/hdfs/hadoop-hdfs-namenode-c6401.ambari.apache.org.log", resultOne.getLogFilePath());
+ assertEquals("log src file name not parsed properly",
+ "UnderReplicatedBlocks.java", resultOne.getSourceFile());
+ assertEquals("log src line number not parsed properly",
+ "394", resultOne.getSourceFileLineNumber());
+ assertEquals("host name not parsed properly",
+ "c6401.ambari.apache.org", resultOne.getHostName());
+ assertEquals("log message not parsed properly",
+ "chooseUnderReplicatedBlocks selected 2 blocks at priority level 0; Total=2 Reset bookmarks? false", resultOne.getLogMessage());
+ assertEquals("logger name not parsed properly",
+ "BlockStateChange", resultOne.getLoggerName());
+ assertEquals("id not parsed properly",
+ "9c5562fb-123f-47c8-aaf5-b5e407326c08", resultOne.getId());
+ assertEquals("message MD5 not parsed properly",
+ "-3892769501348410581", resultOne.getMessageMD5());
+ assertEquals("log time not parsed properly",
+ "1458148749036", resultOne.getLogTime());
+ assertEquals("event MD5 not parsed properly",
+ "1458148749036-2417481968206345035", resultOne.getEventMD5());
+ assertEquals("logfile line number not parsed properly",
+ "2084", resultOne.getLogFileLineNumber());
+ assertEquals("ttl not parsed properly",
+ "+7DAYS", resultOne.getTtl());
+ assertEquals("expire at not parsed properly",
+ "1458753550322", resultOne.getExpirationTime());
+ assertEquals("version not parsed properly",
+ "1528979784023932928", resultOne.getVersion());
+ }
+
+ {
+ LogLineResult resultTwo = listOfLineResults.get(1);
+ // verify second log line record's data is parsed correctly
+ assertEquals("Cluster name not parsed properly",
+ "clusterone", resultTwo.getClusterName());
+ assertEquals("Method Name not parsed properly",
+ "putMetrics", resultTwo.getLogMethod());
+ assertEquals("Log Level not parsed properly",
+ "WARN", resultTwo.getLogLevel());
+ assertEquals("event_count not parsed properly",
+ "1", resultTwo.getEventCount());
+ assertEquals("ip address not parsed properly",
+ "192.168.1.1", resultTwo.getIpAddress());
+ assertEquals("component type not parsed properly",
+ "yarn_resourcemanager", resultTwo.getComponentType());
+ assertEquals("sequence number not parsed properly",
+ "10583", resultTwo.getSequenceNumber());
+ assertEquals("log file path not parsed properly",
+ "/var/log/hadoop-yarn/yarn/yarn-yarn-resourcemanager-c6401.ambari.apache.org.log", resultTwo.getLogFilePath());
+ assertEquals("log src file name not parsed properly",
+ "HadoopTimelineMetricsSink.java", resultTwo.getSourceFile());
+ assertEquals("log src line number not parsed properly",
+ "262", resultTwo.getSourceFileLineNumber());
+ assertEquals("host name not parsed properly",
+ "c6401.ambari.apache.org", resultTwo.getHostName());
+ assertEquals("log message not parsed properly",
+ "Unable to send metrics to collector by address:http://c6401.ambari.apache.org:6188/ws/v1/timeline/metrics", resultTwo.getLogMessage());
+ assertEquals("logger name not parsed properly",
+ "timeline.HadoopTimelineMetricsSink", resultTwo.getLoggerName());
+ assertEquals("id not parsed properly",
+ "8361c5a9-5b1c-4f44-bc8f-4c6f07d94228", resultTwo.getId());
+ assertEquals("message MD5 not parsed properly",
+ "5942185045779825717", resultTwo.getMessageMD5());
+ assertEquals("log time not parsed properly",
+ "1458148746937", resultTwo.getLogTime());
+ assertEquals("event MD5 not parsed properly",
+ "14581487469371427138486123628676", resultTwo.getEventMD5());
+ assertEquals("logfile line number not parsed properly",
+ "549", resultTwo.getLogFileLineNumber());
+ assertEquals("ttl not parsed properly",
+ "+7DAYS", resultTwo.getTtl());
+ assertEquals("expire at not parsed properly",
+ "1458753550322", resultTwo.getExpirationTime());
+ assertEquals("version not parsed properly",
+ "1528979784022884357", resultTwo.getVersion());
+ }
+
+ mockSupport.verifyAll();
+ }
+
+
+ /**
+ * Convenience method for asserting on the values of NameValuePair instances
+ *
+ * @param expectedName the expected name
+ * @param expectedValue the expected value
+ * @param nameValuePair the NameValuePair instance to test
+ */
+ static void assertNameValuePair(String expectedName, String expectedValue, NameValuePair nameValuePair) {
+ assertEquals("Unexpected name found in this pair",
+ expectedName, nameValuePair.getName());
+ assertEquals("Unexpected value found in this pair",
+ expectedValue, nameValuePair.getValue());
+ }
+
+}