Accessing S3 objects in Salesforce.com - amazon-s3

I am trying to access S3 bucket/object details from Salesforce. I have gone through AWS document and try to generate V4 Signature. It seems like I am missing some parameter and hence getting below error:
The request signature we calculated does not match the signature you provided. Check your key and signing method.
Below is the code snippet:
public class AWSIntegrationS31{
private String accessKeyID = 'SampleAccessKey';
private String secretAccessKey = 'SECRET Key';
private String region = 'ap-south-1';
private String service = 's3';
String host = 's3.ap-south-1.amazonaws.com';
private String httpMethodName;
private String endpoint = 'https://s3.ap-south-1.amazonaws.com'; //test123sfdc1.s3.ap-south-1.amazonaws.com
String content_type = 'application/json'; //'application/x-amz-json-1.0';
private String currentDate;
String expires = '86400'; //24 hrs
String signed_headers;
String S3_signed_headers;
String range = 'bytes=0-9';
String TimeStamp;
String t;
String request_parameters;
String canonical_headers;
String amz_date;
String date_stamp;
public AWSIntegrationS31(){
Datetime now = Datetime.now();
TimeStamp = now.formatGmt('yyyyMMdd')+'T'+now.formatGmt('HHmmss')+'Z';
t = EncodingUtil.urlEncode(TimeStamp, 'UTF-8');
currentDate = now.formatGmt('yyyyMMdd');
amz_date = TimeStamp;
date_stamp = currentDate;
request_parameters = '';
}
public HttpResponse connectAWS(){
// ******************************* 1. Cannoncial Request *******************************
/* Step 1.1 Start with the HTTP request method (GET, PUT, POST, etc.), followed by a newline character. */
httpMethodName = 'GET';
/* Step 1.2 Add the canonical URI parameter, followed by a newline character. */
String canonical_uri = '/';
/* Step 1.3 Add the canonical query string, followed by a newline character. */
String canonical_querystring = request_parameters;
String x_amz_content_sha256= 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
/* Step 1.4 Add the canonical headers, followed by a newline character. */
canonical_headers = 'host:' + host + '\n' + 'x-amz-content-sha256:' + x_amz_content_sha256 + '\n' + 'x-amz-date:' + amz_date + '\n';
/* Step 1.5 Add the signed headers, followed by a newline character. */
signed_headers = 'host;x-amz-content-sha256;x-amz-date'; //;x-amz-target';
//signed_headers = 'host;x-amz-content-sha256;x-amz-date';
/* Step 1.6 Create payload hash. */
String payload_hash = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; //'UNSIGNED-PAYLOAD'; // //EncodingUtil.convertToHex(Crypto.generateDigest('SHA256', Blob.valueOf(''))).toLowerCase();
System.debug('payload_hash....'+payload_hash);
/* Step 1.7: Combine elements to create canonical request. */
String canonical_request = httpMethodName + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
System.debug('canonical_request...'+canonical_request);
// ******************************* 2. Create string to Sign *******************************
String algorithm = 'AWS4-HMAC-SHA256';
String credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request';
String hashRequest = EncodingUtil.convertToHex(Crypto.generateDigest('SHA256', Blob.valueOf(canonical_request))).toLowerCase();
System.debug('hashRequest...'+hashRequest);
String string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashRequest;
System.debug('Request Parameter....'+request_parameters);
System.debug('StringTosign....'+string_to_sign);
// ******************************* 3. Calculate Signature *******************************
/* Step 3.1. Generate signing key */
Blob signatureKey = Crypto.generateMac('hmacSHA256', Blob.valueOf('aws4_request'),
Crypto.generateMac('hmacSHA256', Blob.valueOf(service),
Crypto.generateMac('hmacSHA256', Blob.valueOf(region),
Crypto.generateMac('hmacSHA256', Blob.valueOf(date_stamp), Blob.valueOf('AWS4'+secretAccessKey))
)
)
);
System.debug('signatureKey..'+signatureKey);
System.debug('signatureKey..2...'+EncodingUtil.base64Encode(signatureKey));
Blob mac = Crypto.generateMac('hmacSHA256', Blob.valueof(string_to_sign),signatureKey);
System.debug('mac...'+mac);
/*Step 3.2. Encode signature (byte[]) to Hex */
String macUrl = EncodingUtil.convertToHex(mac);
System.debug('macURL...1...'+EncodingUtil.convertToHex(mac));
// ******************************* 4. Add Signing Information to the Request *******************************
String authorization_header = algorithm + ' ' + 'Credential=' + accessKeyID + '/' + credential_scope + ',' + 'SignedHeaders=' + signed_headers + ',' + 'Signature=' + macUrl;
System.debug('authorization_header ..'+authorization_header );
// ******************************* 5. Send Request to S3 *******************************
HttpRequest request = new HttpRequest();
request.setMethod('GET');
request.setEndpoint(endpoint);
//request.setBody(request_parameters);
request.setHeader('Content-Type',content_type);
request.setHeader('X-Amz-Date',amz_date);
request.setHeader('x-amz-content-sha256', 'UNSIGNED-PAYLOAD');
request.setHeader('Authorization', authorization_header);
Http http = new Http();
HttpResponse res = http.send(request);
System.debug(res);
System.debug(res.getBody());
return res;
}
}

Related

Invalid AWS Access Key when calling S3 from Apex

I am attempting to access s3 from Apex using credentials returned from AssumeRole. However, I am receiving the following error:
<Message>The AWS Access Key Id you provided does not exist in our records.</Message>
<AWSAccessKeyId>ASIA********</AWSAccessKeyId>
I am able to successfully call GetObject on this s3 bucket from the CLI using the credentials returned from AssumeRole, so I can be reasonably sure that my bucket permissions have been set up fine. I have the following code in Apex:
Http http = new http();
Profile p = [SELECT Id FROM Profile WHERE Profile.Name = 'S3 Test User' LIMIT 1];
S3_Settings__c s3 = S3_Settings__c.getInstance(p.Id);
String exp = String.valueOf(Cache.Session.get('expiration'));
String sessionToken = String.valueOf(Cache.Session.get('token'));
if(exp == null || exp == '' || (DateTime) JSON.deserialize('"' + exp + '"', DateTime.class) < System.now()) {
requestSessionToken();
}
sessionToken = String.valueOf(Cache.Session.get('token'));
DateTime expires = (DateTime) JSON.deserialize('"' + String.valueOf(Cache.Session.get('expiration')) + '"', DateTime.class);
String accessKeyId = String.valueOf(Cache.Session.get('accessKeyId'));
String accessSecret = String.valueOf(Cache.Session.get('secret'));
String bucketname = s3.Recording_Bucket__c;
String host = 's3.amazonaws.com';
String formattedDateString = Datetime.now().formatGMT('EEE, dd MMM yyyy HH:mm:ss z');
String method = 'GET';
String filePath = 'https://' + bucketname + '.' + host + '/' + filename;
HttpRequest req = new HttpRequest();
req.setMethod(method);
req.setEndpoint(filePath);
req.setHeader('Host', bucketname + '.' + host);
req.setHeader('Connection', 'keep-alive');
String stringToSign = 'GET\n\n' + 'x-amz-security-token=' + sessionToken + '&expiration=' + expires + '\n' + formattedDateString + '\n/' + '/' + bucketname + '/' + filename;
System.debug('SIGN ' + stringToSign);
String encodedStringToSign = EncodingUtil.urlEncode(stringToSign, 'UTF-8');
Blob mac = Crypto.generateMac('HMACSHA1', blob.valueof(stringToSign),blob.valueof(accessSecret));
String signedKey = EncodingUtil.base64Encode(mac);
String authHeader = 'AWS' + ' ' + accessKeyId + ':' + signedKey;
req.setHeader('Date', formattedDateString);
//req.setHeader('x-amz-security-token', sessionToken); //AWS returns 'invalid signature' if this is set
req.setHeader('Authorization',authHeader);
HttpResponse resp = http.send(req);
It seems as if the AWS is reading in the AccessKeyId/Secret, but not the session token. I've also tried setting x-amz-security-token as a header, but this throws a 403 Error -- Signature mismatch. Am I missing something within my headers or signature that would enable this request to return successfully?
Turns out I was placing the x-amz-security-token header in the wrong location. It needs to occur in the canonical AMZ Headers section immediately after the formatted date, with a comma to separate the name and value:
String stringToSign = 'GET\n\n\n' + formattedDateString + '\n' + 'x-amz-security-token:' + sessionToken + '\n' + '/' + bucketname + '/' + filename;
Additionally, the following line needed to be uncommented:
req.setHeader('x-amz-security-token', sessionToken);
As a last note, be sure that neither the header nor the canonicalized AMZ Header are capitalized.

VTD-XML element fragment incorrect

When parsing a XML document (in UTF-8) containing a special character like © using VTD-XML I now encounter an issue that the returned element fragment (getElementFragment) is not correct.
Example code:
VTDGen vg = new VTDGen();
String xmlDocument =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<Root>\r\n" +
" <!-- © -->\r\n" +
" <SomeElement/>\r\n" +
"</Root>";
// For some reason with US_ASCII it does work, although the file is UTF-8.
vg.setDoc(xmlDocument.getBytes(StandardCharsets.UTF_8));
// True or false doesn't matter here, some result.
vg.parse(false);
// Find the element and its fragment.
VTDNav nv = vg.getNav();
AutoPilot ap = new AutoPilot(nv);
ap.selectXPath("//SomeElement");
while ((ap.evalXPath()) != -1) {
long elementOffset = nv.getElementFragment();
int contentStartIndex = (int)elementOffset;
int contentEndIndex = contentStartIndex + (int)(elementOffset>>32);
System.out.println("Returned fragment: " + contentStartIndex + ":" + contentEndIndex + ":\n'" + xmlDocument.substring(contentStartIndex, contentEndIndex) + "'");
}
This returns:
Returned fragment: 65:79:
'SomeElement/>
'
While when changing the StandardCharsets.UTF_8 into StandardCharsets.US_ASCII it does work:
Returned fragment: 64:78:
'<SomeElement/>'
When the input file is a UTF-8 file, this leads to incorrect behaviour. Can this be a bug in VTD-XML, or am I doing something wrong here?
The "©" is a two-word unicode char which causes the starting/ending unicode offset to drift from the starting/ending byte offset by 1. This is not a bug... below is the fix
while ((ap.evalXPath()) != -1) {
long elementOffset = nv.getElementFragment();
int contentStartIndex = (int)elementOffset;
int contentEndIndex = contentStartIndex + (int)(elementOffset>>32);
System.out.println("Returned fragment: " + contentStartIndex + ":" + contentEndIndex + ":\n'"
+ nv.toString(contentStartIndex,(int)(elementOffset>>32)));
//+ xmlDocument.substring(contentStartIndex, contentEndIndex) + "'");
}

getting "415:Media not supported" Error when passing pdf to IBM watson in Salesforce

I am planning to integrate the IBM Watson Document Conversion service
with Salesforce.
From there I am unable to send my pdf file directly to Watson and I'm getting Media Type not supported.
I am also getting this error:
{
"code" : 500 ,
"error" : "Server Error" ,
"description" : "2017-07-18T06:02:19-04:00, Error WATSNGWERR-0x0113001c occurred when accessing https://gateway.watsonplatform.net/document-conversion/api/v1/convert_document?version=2015-12-15&config="{"conversion_target":"answer_units"}", Tran-Id: gateway-dp02-1967135880 - Watson Gateway Error"
}
Here is the code I'm using:
public class Resume {
String boundary = '----------------------------741e90d31eff';
public string id{get;set;}
public string content{get;set;}
Transient public Attachment att{set;get;}
public Resume(ApexPages.StandardController controller) {
id=ApexPages.currentPage().getParameters().get('id');
att=new Attachment();
att=[Select Id,ParentId, Name,body,ContentType From Attachment where ParentId=:id limit 1];
content=String.valueOf(att.body);
System.debug('---->' + content);
String header = '--' + boundary + '\nContent-Disposition: form-data; name="att"; filename="'+att.name+'";\nContent-Type: application/pdf';
String footer = '--' + boundary + '--';
String headerEncoded =
EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
String bodyEncoded = EncodingUtil.base64Encode(att.body);
Blob bodyBlob = null;
String last4Bytes =
bodyEncoded.substring(bodyEncoded.length() - 4, bodyEncoded.length());
while (headerEncoded.endsWith('=')){
header+=' ';
headerEncoded = EncodingUtil.base64Encode(Blob.valueOf(header+'\r\n\r\n'));
}
if (last4Bytes.endsWith('==')) {
last4Bytes = last4Bytes.substring(0,2) + '0K';
bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
} else if (last4Bytes.endsWith('=')) {
last4Bytes = last4Bytes.substring(0,3) + 'N';
bodyEncoded = bodyEncoded.substring(0,bodyEncoded.length()-4) + last4Bytes;
footer = '\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
} else {
footer = '\r\n' + footer;
String footerEncoded = EncodingUtil.base64Encode(Blob.valueOf(footer));
bodyBlob = EncodingUtil.base64Decode(headerEncoded + bodyEncoded + footerEncoded);
}
String configAsString ='\"conversion_target:answer_units\"';
Http h = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');
request.setHeader('Content-Type','multipart/form-data; boundary=' + boundary);
String username= 'DOCUMENT-CONVERSION-USERNAME';
String password= 'DOCUMENT-CONVERSION-PASSWORD';
request.setHeader('Authorization', 'Basic ' + EncodingUtil.base64Encode(blob.valueOf(username + ':' + password)));
request.setEndpoint('https://gateway.watsonplatform.net/document-conversion/api/v1/convert_document?version=2015-12-15&config='+configAsString);
request.setBodyAsBlob(bodyBlob);
request.setCompressed(true);
HttpResponse response = h.send(request);
System.debug(response.getBody());
}
}
You are sending the config as a query parameter, but it should be in the body.
Here is the curl command that makes what you are trying to do:
curl -X POST \
-u "{username}":"{password}" \
-F config="{\"conversion_target\":\"answer_units\"}" \
-F "file=#sample.pdf;type=application/pdf" \
"https://gateway.watsonplatform.net/document-conversion/api/v1/convert_document?version=2015-12-15"
I also think there is an error in the way you are creating the body. My team built an SDK to use the Watson APIs in the Salesforce environment. I would suggest you take a look.
If you can't deploy the SDK to your Salesforce organization (It's a lot of code), copy the code from the IBMWatsonMultipartBody.cls class. It will help you encode an Attachment as base64 so that you can end it to Watson.
UPDATE: The Document Conversion service was deprecated but the features from that service were enhanced and migrated to the Discovery service

Signature did not match. String to sign used was r

Trying to Construct a Shared Access Signature URI for a Blob access in a container
BlobHelper BlobHelper = new BlobHelper(StorageAccount, StorageKey);
string signature = "";
string signedstart = DateTime.UtcNow.AddMinutes(-1).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'");
string signedexpiry = DateTime.UtcNow.AddMinutes(2).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'");
//// SET CONTAINER LEVEL ACCESS POLICY
string accessPolicyXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<SignedIdentifiers>\n" +
" <SignedIdentifier>\n" +
" <Id>twominutepolicy</Id>\n" +
" <AccessPolicy>\n" +
" <Start>" + signedstart + "</Start>\n" +
" <Expiry>" + signedexpiry + "</Expiry>\n" +
" <Permission>r</Permission>\n" +
" </AccessPolicy>\n" +
" </SignedIdentifier>\n" +
"</SignedIdentifiers>\n";
BlobHelper.SetContainerAccessPolicy("xxxxxxx", "container", accessPolicyXml));
string canonicalizedresource = "/xxxxxxx/501362787";
string StringToSign = String.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n{9}\n{10}",
"r",
signedstart,
signedexpiry,
canonicalizedresource,
"twominutepolicy",
"2013-08-15",
"rscc",
"rscd",
"rsce",
"rscl",
"rsct"
);
using (HMACSHA256 hmacSha256 = new HMACSHA256(Convert.FromBase64String(StorageKey)))
{
Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(StringToSign);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
StringBuilder sasToken = new StringBuilder();
sasToken.Append(BlobHelper.DecodeFrom64(e.Item.ToolTip).ToString().Replace("http","https") + "?");
//signedversion
sasToken.Append("sv=2013-08-15&");
sasToken.Append("sr=b&");
//
sasToken.Append("si=twominutepolicy&");
sasToken.Append("sig=" + signature + "&");
//
sasToken.Append("st=" + HttpUtility.UrlEncode(signedstart).ToUpper() + "&");
//
sasToken.Append("se=" + HttpUtility.UrlEncode(signedexpiry).ToUpper() + "&");
//
sasToken.Append("sp=r");
string url = sasToken.ToString();
I am getting the following exception below
<Error>
<Code>AuthenticationFailed</Code>
<Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:e424e1ac-fd96-4557-866a-992fc8c41841 Time:2014-05-22T18:46:15.3436786Z</Message>
<AuthenticationErrorDetail>Signature did not match. String to sign used was r 2014-05-22T18:45:06Z 2014-05-22T18:48:06Z /xxxxxxx/501362787/State.SearchResults.pdf twominutepolicy 2013-08-15 </AuthenticationErrorDetail>
</Error>
rscc, rscd, rsce, rscl, rsct are placeholders for overridden response headers. Your sasToken variable does not seem to override response headers, so you should just use empty strings with a new-line character when signing them. Moreover, it looks like your canonicalized resource also does not match the server's resource.
By the way, did you look at Azure Storage Client Library to create Shared Access Signature tokens? It provides lots of features and is the official SDK to access Microsoft Azure Storage.

Embedded Signing api docusign

I'm using DocuSign to add eSignature to my requests and everything's working well. Right now I send my signature requests,by using Embedded method- to initiate my workflows immediately by navigating to a URL.
After login,and execute the bellow code,i get the (Embedded View: https://demo.docusign.net/Member/StartInSession.aspx?StartConsole=1&t=32598057-5a59-4d0b-bad8-a8ff8f2407f6&DocuEnvelope=168bc155-e013-4ffd-abb4-7608b56647f8&send=1), but whene i paste the url to try signing document in navigate, but will redirect me to an other url is(http://www.docusign.com/?event=Send&envelopeId=168bc155-e013-4ffd-abb4-7608b56647f8),
how can'i start wotkflow process to sign my enveloppe ?? i can't see my enveloppe to sign it.
// STEP 2 - Create an envelope with one recipient, document, and tab and send
//
String jsonBody = "{\"emailBlurb\":\"partail\"," +
"\"emailSubject\":\"API Call for adding signature request to document and sending\"," +
"\"documents\":[{" +
"\"documentId\":\"1\"," +
"\"name\":\"test.pdf\"}]," +
"\"recipients\":{" +
"\"signers\":[{" +
"\"email\":\"" + EmailRecipients + "\"," +
"\"name\":\"name\"," +
"\"recipientId\":\"1\"," +
"\"routingOrder\":\"1\","+
"\"clientUserId\":\"1000\","+
"\"tabs\":{" +
"\"signHereTabs\":[{" +
"\"xPosition\":\"300\"," +
"\"yPosition\":\"600\"," +
"\"documentId\":\"1\"," +
"\"pageNumber\":\"1\"" + "}]}}]}," +
"\"status\":\"sent\"}";
//DemandeSign.getenvelope();
File file = new File("D:/test.pdf");
InputStream inputStream = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
inputStream.read(bytes);
inputStream.close();
String requestBody = "\r\n\r\n--BOUNDARY\r\n" +
"Content-Type: application/json\r\n" +
"Content-Disposition: form-data\r\n" +
"\r\n" +
jsonBody + "\r\n\r\n--BOUNDARY\r\n" + // our json formatted request body
"Content-Type: application/pdf\r\n" +
"Content-Disposition: file; filename=\"test.pdf\"; documentId=1\r\n" +
"\r\n";
// we break this up into two string since the PDF doc bytes go here and are not in string format.
// see further below where we write to the outputstream...
String reqBody2 = "\r\n" + "--BOUNDARY--\r\n\r\n";
// append "/envelopes" to the baseUrl and use in the request
conn = (HttpURLConnection)new URL(baseURL + "/envelopes").openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("X-DocuSign-Authentication", authenticateStr);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=BOUNDARY");
conn.setRequestProperty("Content-Length", Integer.toString(requestBody.toString().length()));
conn.setRequestProperty("Accept", "application/xml");
// write the body of the request...
DataOutputStream dos = new DataOutputStream( conn.getOutputStream() );
dos.writeBytes(requestBody.toString());
dos.write(bytes);
dos.writeBytes(reqBody2.toString());
dos.flush(); dos.close();
status = conn.getResponseCode(); // triggers the request
if( status != 201 ) // 201 = Created
{
System.out.println("Error calling webservice, status is: " + status);
InputStreamReader isr = new InputStreamReader( conn.getErrorStream() );
br = new BufferedReader(isr);
StringBuilder error_response = new StringBuilder();
while ( (line = br.readLine()) != null)
error_response.append(line);
System.out.println("Error response is " + error_response.toString() );
System.exit(-1);
}
// Read the response
InputStreamReader isr = new InputStreamReader( conn.getInputStream() );
br = new BufferedReader(isr);
StringBuilder response2 = new StringBuilder();
while ( (line = br.readLine()) != null)
response2.append(line);
//token1 = "//*[1]/*[local-name()='envelopeId']";
//String envelopeId = xPath.evaluate(token1, new InputSource(new StringReader(response2.toString())));
//--- display results
//System.out.println("Document sent! envelopeId is " + envelopeId );//envelopeId is e4c0659a-9d01-4ac3-a45f-02a80fd6bd96 at 04/07/2013 17:24
token1 = "//*[1]/*[local-name()='uri']";
String uri = xPath.evaluate(token1, new InputSource(new StringReader(response2.toString())));
//--- display results
System.out.println("uri = " + uri );
/// Step3
// construct another outgoing XML request body
String reqBody = "<returnUrlRequest xmlns=\"http://www.docusign.com/restapi\">" +
"<authenticationMethod>email</authenticationMethod>" +
"<email>***test#gmail.com***</email>" +
"<returnUrl>http://www.docusign.com</returnUrl>" +
"<userName>name</userName>" +
"<clientUserId>1000</clientUserId>" +
"</returnUrlRequest>";
// append uri + "/views/sender" to the baseUrl and use in the request
conn = (HttpURLConnection)new URL(baseURL + uri + "/views/sender").openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("X-DocuSign-Authentication", authenticateStr);
conn.setRequestProperty("Content-Type", "application/xml");
conn.setRequestProperty("Content-Length", Integer.toString(reqBody.length()));
conn.setRequestProperty("Accept", "application/xml");
// write the body of the request...
dos = new DataOutputStream( conn.getOutputStream() );
dos.writeBytes(reqBody); dos.flush(); dos.close();
status = conn.getResponseCode(); // triggers the request
if( status != 201 ) // 201 = Created
{
System.out.println("Error calling webservice, status is: " + status);
System.exit(-1);
}
// Read the response
isr = new InputStreamReader( conn.getInputStream() );
br = new BufferedReader(isr);
StringBuilder response3 = new StringBuilder();
while ( (line = br.readLine()) != null)
response3.append(line);
token1 = "//*[1]/*[local-name()='url']";
//--- display results
System.out.println("Embedded View: " + xPath.evaluate(token1, new InputSource(new StringReader(response3.toString()))));`
Are you trying to access the URL immediately or are you waiting at all? Once you generate a URL token to access a given envelope it has a TTL (time to life) of 5 mins, meaning it expires after 5 minutes and you then need to generate a new one.
If that's not it, your problem might be related to how you are identifying your recipient. A recipient in DocuSign is identified by the unique combination of their name, email, recipientId, and in the case of embedding, the clientUserId. You seem to be setting all of those, however whatever the combination is when you first create the envelope, you need to refer to the same combination when you are requesting the Embedded URL token.
When you create your envelope I see you are setting the name literally to "name" and that you are setting the email through a variable called "EmailRecipients". But when you request the URL token you are using email "test#gmail.com", that might be causing your issue too.