summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/onap/dcae/collectors/restconf/common/RestapiCallNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/onap/dcae/collectors/restconf/common/RestapiCallNode.java')
-rwxr-xr-xsrc/main/java/org/onap/dcae/collectors/restconf/common/RestapiCallNode.java600
1 files changed, 600 insertions, 0 deletions
diff --git a/src/main/java/org/onap/dcae/collectors/restconf/common/RestapiCallNode.java b/src/main/java/org/onap/dcae/collectors/restconf/common/RestapiCallNode.java
new file mode 100755
index 0000000..a324e87
--- /dev/null
+++ b/src/main/java/org/onap/dcae/collectors/restconf/common/RestapiCallNode.java
@@ -0,0 +1,600 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dcaegen2.collectors.restconf
+ * ================================================================================
+ * Copyright (C) 2018 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dcae.collectors.restconf.common;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+import com.sun.jersey.api.client.filter.HTTPDigestAuthFilter;
+import com.sun.jersey.client.urlconnection.HTTPSProperties;
+import com.sun.jersey.oauth.client.OAuthClientFilter;
+import com.sun.jersey.oauth.signature.OAuthParameters;
+import com.sun.jersey.oauth.signature.OAuthSecrets;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.SocketException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RestapiCallNode {
+ private static final Logger log = LoggerFactory.getLogger(RestapiCallNode.class);
+
+ public void sendRequest(Map<String, String> paramMap, RestConfContext ctx, Integer retryCount) throws Exception {
+ RetryPolicy retryPolicy = null;
+ HttpResponse r = new HttpResponse();
+ try {
+ Parameters p = getParameters(paramMap);
+ String pp = p.responsePrefix != null ? p.responsePrefix + '.' : "";
+ String req = null;
+ if (p.templateFileName != null) {
+ String reqTemplate = readFile(p.templateFileName);
+ req = buildXmlJsonRequest(ctx, reqTemplate, p.format);
+ } else if (p.requestBody != null) {
+ req = p.requestBody;
+ }
+
+ r = sendHttpRequest(req, p);
+ setResponseStatus(ctx, p.responsePrefix, r);
+
+ if (p.dumpHeaders && r.headers != null) {
+ for (Map.Entry<String, List<String>> a : r.headers.entrySet()) {
+ ctx.setAttribute(pp + "header." + a.getKey(), StringUtils.join(a.getValue(), ","));
+ }
+ }
+
+ if (p.returnRequestPayload && req != null) {
+ ctx.setAttribute(pp + "httpRequest", req);
+ }
+
+ if (r.body != null && r.body.trim().length() > 0) {
+ ctx.setAttribute(pp + "httpResponse", r.body);
+
+ if (p.convertResponse) {
+ Map<String, String> mm = null;
+ if (p.format == Format.XML) {
+ mm = XmlParser.convertToProperties(r.body, p.listNameList);
+ } else if (p.format == Format.JSON) {
+ mm = JsonParser.convertToProperties(r.body);
+ }
+
+ if (mm != null) {
+ for (Map.Entry<String, String> entry : mm.entrySet())
+ ctx.setAttribute(pp + entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ } catch (Exception e) {
+ boolean shouldRetry = false;
+ if (e.getCause().getCause() instanceof SocketException) {
+ shouldRetry = true;
+ }
+
+ log.error("Error sending the request: " + e.getMessage(), e);
+ String prefix = parseParam(paramMap, "responsePrefix", false, null);
+ if (retryPolicy == null || shouldRetry == false) {
+ setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
+ } else {
+ if (retryCount == null) {
+ retryCount = 0;
+ }
+ String retryMessage = retryCount + " attempts were made out of " + retryPolicy.getMaximumRetries() +
+ " maximum retries.";
+ log.debug(retryMessage);
+ try {
+ retryCount = retryCount + 1;
+ if (retryCount < retryPolicy.getMaximumRetries() + 1) {
+ URI uri = new URI(paramMap.get("restapiUrl"));
+ String hostname = uri.getHost();
+ String retryString = retryPolicy.getNextHostName(uri.toString());
+ URI uriTwo = new URI(retryString);
+ URI retryUri = UriBuilder.fromUri(uri).host(uriTwo.getHost()).port(uriTwo.getPort()).scheme(
+ uriTwo.getScheme()).build();
+ paramMap.put("restapiUrl", retryUri.toString());
+ log.debug("URL was set to {}", retryUri.toString());
+ log.debug("Failed to communicate with host {}. Request will be re-attempted using the host {}.",
+ hostname, retryString);
+ log.debug("This is retry attempt {} out of {}", retryCount, retryPolicy.getMaximumRetries());
+ sendRequest(paramMap, ctx, retryCount);
+ } else {
+ log.debug("Maximum retries reached, calling setFailureResponseStatus.");
+ setFailureResponseStatus(ctx, prefix, e.getMessage(), r);
+ }
+ } catch (Exception ex) {
+ log.error("Could not attempt retry.", ex);
+ String retryErrorMessage =
+ "Retry attempt has failed. No further retry shall be attempted, calling " +
+ "setFailureResponseStatus.";
+ setFailureResponseStatus(ctx, prefix, retryErrorMessage, r);
+ }
+ }
+ }
+
+ if (r != null && r.code >= 300) {
+ throw new Exception(String.valueOf(r.code) + ": " + r.message);
+ }
+ }
+
+ protected Parameters getParameters(Map<String, String> paramMap) throws Exception {
+ Parameters p = new Parameters();
+ p.templateFileName = parseParam(paramMap, "templateFileName", false, null);
+ p.requestBody = parseParam(paramMap, "requestBody", false, null);
+ p.restapiUrl = parseParam(paramMap, "restapiUrl", true, null);
+ validateUrl(p.restapiUrl);
+ p.restapiUser = parseParam(paramMap, "restapiUser", false, null);
+ p.restapiPassword = parseParam(paramMap, "restapiPassword", false, null);
+ p.oAuthConsumerKey = parseParam(paramMap, "oAuthConsumerKey", false, null);
+ p.oAuthConsumerSecret = parseParam(paramMap, "oAuthConsumerSecret", false, null);
+ p.oAuthSignatureMethod = parseParam(paramMap, "oAuthSignatureMethod", false, null);
+ p.oAuthVersion = parseParam(paramMap, "oAuthVersion", false, null);
+ p.contentType = parseParam(paramMap, "contentType", false, null);
+ p.format = Format.fromString(parseParam(paramMap, "format", false, "json"));
+ p.authtype = AuthType.fromString(parseParam(paramMap, "authType", false, "unspecified"));
+ p.httpMethod = HttpMethod.fromString(parseParam(paramMap, "httpMethod", false, "post"));
+ p.responsePrefix = parseParam(paramMap, "responsePrefix", false, null);
+ p.listNameList = getListNameList(paramMap);
+ String skipSendingStr = paramMap.get("skipSending");
+ p.skipSending = "true".equalsIgnoreCase(skipSendingStr);
+ p.convertResponse = Boolean.valueOf(parseParam(paramMap, "convertResponse", false, "true"));
+ p.trustStoreFileName = parseParam(paramMap, "trustStoreFileName", false, null);
+ p.trustStorePassword = parseParam(paramMap, "trustStorePassword", false, null);
+ p.keyStoreFileName = parseParam(paramMap, "keyStoreFileName", false, null);
+ p.keyStorePassword = parseParam(paramMap, "keyStorePassword", false, null);
+ p.ssl = p.trustStoreFileName != null && p.trustStorePassword != null && p.keyStoreFileName != null &&
+ p.keyStorePassword != null;
+ p.customHttpHeaders = parseParam(paramMap, "customHttpHeaders", false, null);
+ p.partner = parseParam(paramMap, "partner", false, null);
+ p.dumpHeaders = Boolean.valueOf(parseParam(paramMap, "dumpHeaders", false, null));
+ p.returnRequestPayload = Boolean.valueOf(parseParam(paramMap, "returnRequestPayload", false, null));
+ return p;
+ }
+
+ private void validateUrl(String restapiUrl) throws Exception {
+ try {
+ URI.create(restapiUrl);
+ } catch (IllegalArgumentException e) {
+ throw new Exception("Invalid input of url " + e.getLocalizedMessage(), e);
+ }
+ }
+
+ protected Set<String> getListNameList(Map<String, String> paramMap) {
+ Set<String> ll = new HashSet<>();
+ for (Map.Entry<String, String> entry : paramMap.entrySet())
+ if (entry.getKey().startsWith("listName")) {
+ ll.add(entry.getValue());
+ }
+ return ll;
+ }
+
+ protected String parseParam(Map<String, String> paramMap, String name, boolean required, String def)
+ throws Exception {
+ String s = paramMap.get(name);
+
+ if (s == null || s.trim().length() == 0) {
+ if (!required) {
+ return def;
+ }
+ throw new Exception("Parameter " + name + " is required in RestapiCallNode");
+ }
+
+ s = s.trim();
+ StringBuilder value = new StringBuilder();
+ int i = 0;
+ int i1 = s.indexOf('%');
+ while (i1 >= 0) {
+ int i2 = s.indexOf('%', i1 + 1);
+ if (i2 < 0) {
+ break;
+ }
+
+ String varName = s.substring(i1 + 1, i2);
+ String varValue = System.getenv(varName);
+ if (varValue == null) {
+ varValue = "%" + varName + "%";
+ }
+
+ value.append(s.substring(i, i1));
+ value.append(varValue);
+
+ i = i2 + 1;
+ i1 = s.indexOf('%', i);
+ }
+ value.append(s.substring(i));
+
+ log.info("Parameter {}: [{}]", name, value);
+ return value.toString();
+ }
+
+ protected String buildXmlJsonRequest(RestConfContext ctx, String template, Format format) throws Exception {
+ log.info("Building {} started", format);
+ long t1 = System.currentTimeMillis();
+
+ template = expandRepeats(ctx, template, 1);
+
+ Map<String, String> mm = new HashMap<>();
+ for (String s : ctx.getAttributeKeySet())
+ mm.put(s, ctx.getAttribute(s));
+ StringBuilder ss = new StringBuilder();
+ int i = 0;
+ while (i < template.length()) {
+ int i1 = template.indexOf("${", i);
+ if (i1 < 0) {
+ ss.append(template.substring(i));
+ break;
+ }
+
+ int i2 = template.indexOf('}', i1 + 2);
+ if (i2 < 0) {
+ throw new Exception("Template error: Matching } not found");
+ }
+
+ String var1 = template.substring(i1 + 2, i2);
+ String value1 = format == Format.XML ? XmlJsonUtil.getXml(mm, var1) : XmlJsonUtil.getJson(mm, var1);
+ // log.info(" " + var1 + ": " + value1);
+ if (value1 == null || value1.trim().length() == 0) {
+ // delete the whole element (line)
+ int i3 = template.lastIndexOf('\n', i1);
+ if (i3 < 0) {
+ i3 = 0;
+ }
+ int i4 = template.indexOf('\n', i1);
+ if (i4 < 0) {
+ i4 = template.length();
+ }
+
+ if (i < i3) {
+ ss.append(template.substring(i, i3));
+ }
+ i = i4;
+ } else {
+ ss.append(template.substring(i, i1)).append(value1);
+ i = i2 + 1;
+ }
+ }
+
+ String req = format == Format.XML
+ ? XmlJsonUtil.removeEmptyStructXml(ss.toString()) : XmlJsonUtil.removeEmptyStructJson(ss.toString());
+
+ if (format == Format.JSON) {
+ req = XmlJsonUtil.removeLastCommaJson(req);
+ }
+
+ long t2 = System.currentTimeMillis();
+ log.info("Building {} completed. Time: {}", format, (t2 - t1));
+
+ return req;
+ }
+
+ protected String expandRepeats(RestConfContext ctx, String template, int level) throws Exception {
+ StringBuilder newTemplate = new StringBuilder();
+ int k = 0;
+ while (k < template.length()) {
+ int i1 = template.indexOf("${repeat:", k);
+ if (i1 < 0) {
+ newTemplate.append(template.substring(k));
+ break;
+ }
+
+ int i2 = template.indexOf(':', i1 + 9);
+ if (i2 < 0) {
+ throw new Exception(
+ "Template error: Context variable name followed by : is required after repeat");
+ }
+
+ // Find the closing }, store in i3
+ int nn = 1;
+ int i3 = -1;
+ int i = i2;
+ while (nn > 0 && i < template.length()) {
+ i3 = template.indexOf('}', i);
+ if (i3 < 0) {
+ throw new Exception("Template error: Matching } not found");
+ }
+ int i32 = template.indexOf('{', i);
+ if (i32 >= 0 && i32 < i3) {
+ nn++;
+ i = i32 + 1;
+ } else {
+ nn--;
+ i = i3 + 1;
+ }
+ }
+
+ String var1 = template.substring(i1 + 9, i2);
+ String value1 = ctx.getAttribute(var1);
+ log.info(" {}:{}", var1, value1);
+ int n = 0;
+ try {
+ n = Integer.parseInt(value1);
+ } catch (NumberFormatException e) {
+ log.info("value1 not set or not a number, n will remain set at zero");
+ }
+
+ newTemplate.append(template.substring(k, i1));
+
+ String rpt = template.substring(i2 + 1, i3);
+
+ for (int ii = 0; ii < n; ii++) {
+ String ss = rpt.replaceAll("\\[\\$\\{" + level + "\\}\\]", "[" + ii + "]");
+ if (ii == n - 1 && ss.trim().endsWith(",")) {
+ int i4 = ss.lastIndexOf(',');
+ if (i4 > 0) {
+ ss = ss.substring(0, i4) + ss.substring(i4 + 1);
+ }
+ }
+ newTemplate.append(ss);
+ }
+
+ k = i3 + 1;
+ }
+
+ if (k == 0) {
+ return newTemplate.toString();
+ }
+
+ return expandRepeats(ctx, newTemplate.toString(), level + 1);
+ }
+
+ protected String readFile(String fileName) throws Exception {
+ try {
+ byte[] encoded = Files.readAllBytes(Paths.get(fileName));
+ return new String(encoded, "UTF-8");
+ } catch (IOException | SecurityException e) {
+ throw new Exception("Unable to read file " + fileName + e.getLocalizedMessage(), e);
+ }
+ }
+
+ protected Client addAuthType(Client client, Parameters p) throws Exception {
+ if (p.authtype == AuthType.Unspecified) {
+ if (p.restapiUser != null && p.restapiPassword != null) {
+ client.addFilter(new HTTPBasicAuthFilter(p.restapiUser, p.restapiPassword));
+ } else if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null
+ && p.oAuthSignatureMethod != null) {
+ OAuthParameters params = new OAuthParameters()
+ .signatureMethod(p.oAuthSignatureMethod)
+ .consumerKey(p.oAuthConsumerKey)
+ .version(p.oAuthVersion);
+
+ OAuthSecrets secrets = new OAuthSecrets()
+ .consumerSecret(p.oAuthConsumerSecret);
+ client.addFilter(new OAuthClientFilter(client.getProviders(), params, secrets));
+ }
+ } else {
+ if (p.authtype == AuthType.DIGEST) {
+ if (p.restapiUser != null && p.restapiPassword != null) {
+ client.addFilter(new HTTPDigestAuthFilter(p.restapiUser, p.restapiPassword));
+ } else {
+ throw new Exception("oAUTH authentication type selected but all restapiUser and restapiPassword " +
+ "parameters doesn't exist", new Throwable());
+ }
+ } else if (p.authtype == AuthType.BASIC) {
+ if (p.restapiUser != null && p.restapiPassword != null) {
+ client.addFilter(new HTTPBasicAuthFilter(p.restapiUser, p.restapiPassword));
+ } else {
+ throw new Exception("oAUTH authentication type selected but all restapiUser and restapiPassword " +
+ "parameters doesn't exist", new Throwable());
+ }
+ } else if (p.authtype == AuthType.OAUTH) {
+ if (p.oAuthConsumerKey != null && p.oAuthConsumerSecret != null && p.oAuthSignatureMethod != null) {
+ OAuthParameters params = new OAuthParameters()
+ .signatureMethod(p.oAuthSignatureMethod)
+ .consumerKey(p.oAuthConsumerKey)
+ .version(p.oAuthVersion);
+
+ OAuthSecrets secrets = new OAuthSecrets()
+ .consumerSecret(p.oAuthConsumerSecret);
+ client.addFilter(new OAuthClientFilter(client.getProviders(), params, secrets));
+ } else {
+ throw new Exception("oAUTH authentication type selected but all oAuthConsumerKey, oAuthConsumerSecret " +
+ "and oAuthSignatureMethod parameters doesn't exist", new Throwable());
+ }
+ }
+ }
+ return client;
+ }
+
+ protected HttpResponse sendHttpRequest(String request, Parameters p) throws Exception {
+
+ ClientConfig config = new DefaultClientConfig();
+ SSLContext ssl = null;
+ if (p.ssl && p.restapiUrl.startsWith("https")) {
+ ssl = createSSLContext(p);
+ }
+ if (ssl != null) {
+ HostnameVerifier hostnameVerifier = (hostname, session) -> true;
+
+ config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
+ new HTTPSProperties(hostnameVerifier, ssl));
+ }
+
+ logProperties(config.getProperties());
+
+ Client client = Client.create(config);
+ client.setConnectTimeout(5000);
+ WebResource webResource = addAuthType(client, p).resource(p.restapiUrl);
+
+ log.info("Sending request:");
+ log.info(request);
+ long t1 = System.currentTimeMillis();
+
+ HttpResponse r = new HttpResponse();
+ r.code = 200;
+
+ if (!p.skipSending) {
+ String tt = p.format == Format.XML ? "application/xml" : "application/json";
+ String tt1 = tt + ";charset=UTF-8";
+ if (p.contentType != null) {
+ tt = p.contentType;
+ tt1 = p.contentType;
+ }
+
+ WebResource.Builder webResourceBuilder = webResource.accept(tt).type(tt1);
+ if (p.format == Format.NONE) {
+ webResourceBuilder = webResource.header("", "");
+ }
+
+ if (p.customHttpHeaders != null && p.customHttpHeaders.length() > 0) {
+ String[] keyValuePairs = p.customHttpHeaders.split(",");
+ for (String singlePair : keyValuePairs) {
+ int equalPosition = singlePair.indexOf('=');
+ webResourceBuilder.header(singlePair.substring(0, equalPosition),
+ singlePair.substring(equalPosition + 1, singlePair.length()));
+ }
+ }
+
+ webResourceBuilder.header("X-ECOMP-RequestID", org.slf4j.MDC.get("X-ECOMP-RequestID"));
+
+ ClientResponse response;
+
+ try {
+ response = webResourceBuilder.method(p.httpMethod.toString(), ClientResponse.class, request);
+ } catch (UniformInterfaceException | ClientHandlerException e) {
+ throw new Exception("Exception while sending http request to client "
+ + e.getLocalizedMessage(), e);
+ }
+
+ r.code = response.getStatus();
+ r.headers = response.getHeaders();
+ EntityTag etag = response.getEntityTag();
+ if (etag != null) {
+ r.message = etag.getValue();
+ }
+ if (response.hasEntity() && r.code != 204) {
+ r.body = response.getEntity(String.class);
+ }
+ }
+
+ long t2 = System.currentTimeMillis();
+ log.info("Response received. Time: {}", (t2 - t1));
+ log.info("HTTP response code: {}", r.code);
+ log.info("HTTP response message: {}", r.message);
+ logHeaders(r.headers);
+ log.info("HTTP response: {}", r.body);
+
+ return r;
+ }
+
+ protected void setFailureResponseStatus(RestConfContext ctx, String prefix, String errorMessage,
+ HttpResponse resp) {
+ resp.code = 500;
+ resp.message = errorMessage;
+ String pp = prefix != null ? prefix + '.' : "";
+ ctx.setAttribute(pp + "response-code", String.valueOf(resp.code));
+ ctx.setAttribute(pp + "response-message", resp.message);
+ }
+
+ protected void setResponseStatus(RestConfContext ctx, String prefix, HttpResponse r) {
+ String pp = prefix != null ? prefix + '.' : "";
+ ctx.setAttribute(pp + "response-code", String.valueOf(r.code));
+ ctx.setAttribute(pp + "response-message", r.message);
+ }
+
+ protected SSLContext createSSLContext(Parameters p) {
+ try (FileInputStream in = new FileInputStream(p.keyStoreFileName)) {
+ System.setProperty("jsse.enableSNIExtension", "false");
+ System.setProperty("javax.net.ssl.trustStore", p.trustStoreFileName);
+ System.setProperty("javax.net.ssl.trustStorePassword", p.trustStorePassword);
+
+ HttpsURLConnection.setDefaultHostnameVerifier((string, ssls) -> true);
+
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ char[] pwd = p.keyStorePassword.toCharArray();
+ ks.load(in, pwd);
+ kmf.init(ks, pwd);
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ ctx.init(kmf.getKeyManagers(), null, null);
+ return ctx;
+ } catch (Exception e) {
+ log.error("Error creating SSLContext: {}", e.getMessage(), e);
+ }
+ return null;
+ }
+
+ protected void logProperties(Map<String, Object> mm) {
+ List<String> ll = new ArrayList<>();
+ for (Object o : mm.keySet())
+ ll.add((String) o);
+ Collections.sort(ll);
+
+ log.info("Properties:");
+ for (String name : ll)
+ log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
+ }
+
+ protected void logHeaders(MultivaluedMap<String, String> mm) {
+ log.info("HTTP response headers:");
+
+ if (mm == null) {
+ return;
+ }
+
+ List<String> ll = new ArrayList<>();
+ for (Object o : mm.keySet())
+ ll.add((String) o);
+ Collections.sort(ll);
+
+ for (String name : ll)
+ log.info("--- {}:{}", name, String.valueOf(mm.get(name)));
+ }
+
+ private static class FileParam {
+
+ public String fileName;
+ public String url;
+ public String user;
+ public String password;
+ public HttpMethod httpMethod;
+ public String responsePrefix;
+ public boolean skipSending;
+ public String oAuthConsumerKey;
+ public String oAuthConsumerSecret;
+ public String oAuthSignatureMethod;
+ public String oAuthVersion;
+ public AuthType authtype;
+ }
+
+} \ No newline at end of file