CXF: Multiple parts in soap header - java

I need a soap header like this:
<MyHeader xmlns:foo="http://foourl" xmlns:bar="http://barurl" bar:someParam="true", otherParam="hereiam">WORLD</MyHeader>
I tried to create it like:
new SoapHeader(new QName("http://foourl", "MyHeader", "foo"),
"WORLD", new JAXBDataBinding(String.class)));
But I just get
<MyHeader xmlns:foo="http://foourl">WORLD</MyHeader>
How can I set the other parts of the header (xmlns:bar, bar:someParam, otherParam)? TIA!

I found a solution for myself:
https://manoj29.wordpress.com/2011/12/29/apache-cxf-adding-custom-soap-headers-to-a-soap-message/

Example:
private void addHeader(String localPart, String valueElement, QName attributeQname, String attributeValue, List<Header> headers) {
var qname = new QName("http://www.w3.org/2005/08/addressing", localPart, "wsa");
var soapElement = createSoapElement(qname, attributeQname, attributeValue, valueElement);
headers.add(new Header(qname, soapElement));
}
private SOAPElement createSoapElement(QName elementQname, QName attributeQname, String attributeValue, String valueElement) {
SOAPElement element = null;
try {
SOAPFactory factory = SOAPFactory.newInstance();
element = factory.createElement(elementQname);
if (Objects.nonNull(attributeQname) && Objects.nonNull(attributeValue))
element.addAttribute(attributeQname, attributeValue);
if (Objects.nonNull(valueElement))
element.addTextNode(valueElement);
} catch (SOAPException e) {
log.error("Can not create new element", e);
} finally {
return element;
}}

Related

Bitfinex API Error Message "Key symbol was not present."

I'm working with the Bitfinex API and the version of the API is 1.
But I have a problem that can not be solved.
When I use '/v1/order/new', the server sends to the message "Key symbol was not present."
I can not find which point is the problem.
The parameter settings are as belows.
Please advise.
========== ========== ========== ==========
/**
Create Header, Param
*/
JSONObject json = new JSONObject();
json.put("request", targetURL);
json.put("nonce", Long.toString(getNonce()));
String payload = json.toString();
String payload_base64 = Base64.getEncoder().encodeToString(payload.getBytes());
String payload_sha384hmac = hmacDigest(payload_base64, apiKeySecret, ALGORITHM_HMACSHA384);
HttpTask http = new HttpTask(URL, Method.POST);
http.addHeader("X-BFX-APIKEY", apiKey);
http.addHeader("X-BFX-PAYLOAD", payload_base64);
http.addHeader("X-BFX-SIGNATURE", payload_sha384hmac);
http.setContentType("x-www-urlencoded");
http.setAcceptType("application/xml");
http.addParam("symbol", "btcusd");
http.addParam("amount", "0.01");
http.addParam("price", "0.01");
http.addParam("side", "buy");
http.addParam("type", "exchange market");
http.addParam("is_hidden", "false");
http.addParam("is_postonly", "true");
http.addParam("use_all_available", "0");
http.addParam("exchange", "bitfinex");
http.addParam("ocoorder", "false");
http.addParam("buy_price_oco", "0");
/**
Parsing Param
*/
StringBuilder sb = new StringBuilder();
Set<String> key = m_params.keySet();
int totalCount = key.size();
if (totalCount > 0) {
int index = 0;
for (Iterator<String> iterator = key.iterator(); iterator.hasNext();) {
String keyValue = (String) iterator.next();
String valueValue = (String) m_params.get(keyValue);
sb.append(String.format("%s=%s", keyValue, valueValue));
if (index < totalCount - 1) {
sb.append("&");
}
index++;
}
query = sb.toString();
}
/**
send Param
*/
if (!query.isEmpty()) {
DataOutputStream wr;
try {
wr = new DataOutputStream(m_connection.getOutputStream());
wr.writeBytes(query);
wr.flush();
wr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
You need to put all params in payload object.
Here is my example on JAVASCRIPT:
auth_v1_request(path, params){
return new Promise((resolve, reject) => {
// console.log(this.account);
const apiKey = this.account.api_key;
const apiSecret = this.account.api_secret;
const apiPath = '/' + path;
const nonce = (Date.now() * 1000).toString();
const completeURL = `${ CONFIG.BITFINEX.API_URL }${apiPath}`;
params.nonce = nonce;
params.request = apiPath;
const payload = new Buffer(JSON.stringify(params))
.toString('base64');
const signature = crypto
.createHmac('sha384', apiSecret)
.update(payload)
.digest('hex');
const options = {
url: completeURL,
headers: {
'X-BFX-APIKEY': apiKey,
'X-BFX-PAYLOAD': payload,
'X-BFX-SIGNATURE': signature
},
body: JSON.stringify(params),
json: true
};
request.post(options, (error, response, res_body) => {
console.log(error);
console.log(res_body);
if(error) {
reject(error);
}
else {
let parsed;
try {
parsed = res_body;
if(parsed.message){
reject(parsed);
}
else {
resolve(parsed);
}
}
catch(err) {
reject(err);
}
}
})
});
}

SOAPExceptionImpl Bad response: 404Not Found if i don't do soapMessage.writeTo(System.out);

I'm creating a Soap client in java and I'm getting a strange error.
Abstract client
public abstract class AbstractSoapClient {
private ServerContext context;
private String path;
private static final String WSSE = "";
private static final String CURL = "";
private static final String CURL_PASSWORD = "";
private static final String SECURITY_NODE = "";
private static final String USERNAME_TOKEN = "";
private static final String USERNAME_NODE = "";
private static final String PASSWORD_NODE = "";
public AbstractSoapClient(ServerContext context) {
this.context = context;
}
protected SOAPMessage createRequest(String path) throws SOAPException {
this.path = assembleEndpoint(path);
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection soapConnection = soapConnectionFactory.createConnection();
SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), this.path);
soapConnection.close();
return soapResponse;
}
protected void setCredentials(SOAPEnvelope envelope) throws SOAPException {
SOAPHeader tHeader = envelope.getHeader();
Name tWsseHeaderName = envelope.createName(SECURITY_NODE, WSSE, CURL);
SOAPHeaderElement tSecurityElement = tHeader.addHeaderElement(tWsseHeaderName);
tSecurityElement.setMustUnderstand(false);
Name tUserTokenElementName = envelope.createName(USERNAME_TOKEN, WSSE, CURL);
SOAPElement tUserTokenElement = tSecurityElement.addChildElement(tUserTokenElementName);
tUserTokenElement.removeNamespaceDeclaration(WSSE);
tUserTokenElement.addNamespaceDeclaration("wsu", CURL);
// user name child
Name tUsernameElementName = envelope.createName(USERNAME_NODE, WSSE, CURL);
SOAPElement tUsernameElement = tUserTokenElement.addChildElement(tUsernameElementName);
tUsernameElement.removeNamespaceDeclaration(WSSE);
tUsernameElement.addTextNode(context.getUsername());
// password child
Name tPasswordElementName = envelope.createName(PASSWORD_NODE, WSSE, CURL);
SOAPElement tPasswordElement = tUserTokenElement.addChildElement(tPasswordElementName);
tPasswordElement.removeNamespaceDeclaration(WSSE);
tPasswordElement.setAttribute("Type", CURL_PASSWORD);
tPasswordElement.addTextNode(context.getPassword());
}
private String assembleEndpoint(String path) {
return context.getUrl().concat(path);
}
protected abstract SOAPMessage createSOAPRequest() throws SOAPException;
public ServerContext getContext() {
return context;
}
public String getPath() {
return path;
}
}
Soap Client implementation
public class SoapClient extends AbstractSoapClient {
public SoapClient(ServerContext context) {
super(context);
}
#Override
public SOAPMessage createSOAPRequest() throws SOAPException {
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
setCredentials(envelope);
buildBody(envelope);
soapMessage.saveChanges();
try {
soapMessage.writeTo(System.out);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return soapMessage;
}
private void buildBody(SOAPEnvelope envelope) throws SOAPException {
envelope.addNamespaceDeclaration("sch", "------");
SOAPBody soapBody = envelope.getBody();
SOAPElement soapBodyElem = soapBody.addChildElement("sampleData", "sampleData");
SOAPElement soapBodyElem1 = soapBodyElem.addChildElement("sampleData");
soapBodyElem1.addTextNode("sampleData");
SOAPElement soapBodyElem2 = soapBodyElem.addChildElement("sampleData");
soapBodyElem2.addTextNode("sampleData");
SOAPElement soapBodyElem3 = soapBodyElem.addChildElement("sampleData");
soapBodyElem3.addTextNode("Y");
SOAPElement soapBodyElem4 = soapBodyElem.addChildElement("sampleData");
soapBodyElem4.addTextNode("sampleData");
SOAPElement soapBodyElem5 = soapBodyElem.addChildElement("sampleData");
soapBodyElem5.addTextNode("sampleData");
SOAPElement soapBodyElem6 = soapBodyElem.addChildElement("sampleData");
soapBodyElem6.addTextNode("sampleData");
}
public static void main(String[] args) throws SOAPException, IOException {
SoapClient client = new SoapClient(
new ServerContext("url", "user", "password"));
SOAPMessage response = client.createRequest("endpoint");
response.writeTo(System.out);
}
}
The strange point is in this part of the code:
try {
soapMessage.writeTo(System.out);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
If I comment this code that only print the request before to send it I get the following exception:
ago 12, 2016 12:58:17 PM com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection post
GRAVE: SAAJ0008: respuesta errĂ³nea; Not Found
Exception in thread "main" com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Bad response: (404Not Found
at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.call(HttpSOAPConnection.java:149)
at
AbstractSoapClient.createRequest(AbstractSoapClient.java:44)
at SoapClient.main(SoapClient.java:67)
Caused by: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Bad response: (404Not Found
at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.post(HttpSOAPConnection.java:264)
at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.call(HttpSOAPConnection.java:145)
... 2 more
CAUSE:
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Bad response: (404Not Found
at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.post(HttpSOAPConnection.java:264)
at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.call(HttpSOAPConnection.java:145)
at AbstractSoapClient.createRequest(AbstractSoapClient.java:44)
at SoapClient.main(SoapClient.java:67)
But if I don't comment this line I can get the response correctly.
This doesn't make sense: why is it sending a 404Not Found if I don't write the request in the console before sending it?
If you check the writeTo implementation you will see that they add a SOAPAction header.
Try the following:
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
soapMessage.getMimeHeaders().addHeader("SOAPAction", "\"\"");
By default, SOAPMessage interface is implemented by SoapMessageImpl.
This implementation has the side effect of adding a SOAPAction header if it's not present.
After calling to writeTo, you can remove it by calling:
soapMessage.getMimeHeaders().removeHeader("SOAPAction");
Having said that, instead of adding extra code only to log the call and response, i suggest to use a proxy instead.
If you're using Eclipse, take a look at the TCP/Monitor View
Identify the correct "SOAPAction" and then post the request. This will resolve the 404 FileNotFoud Error.
eg:
String SOAPAction = "/Service/Service.serviceagent/ServicesEndpoint1/ACTIONNAMEFROMSERVERSIDE";
httpConn.setRequestProperty("Content-Length", String.valueOf(b.length));
httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
httpConn.setRequestProperty("SOAPAction", SOAPAction);

Verify OAuth1a signed request using Twitter joauth with RSA-SHA1?

I have a use case to authenticate OAuth1 request which is signed using RSA Private Key and verified at server end with RSA public key.
I found this library from Twitter which helps us authenticate/verify the Oauth signed requests. https://github.com/twitter/joauth
I want to leverage this library for verifying the request from Jersey or Spring MVC action method. The request from client would have been signed using private key. At my end I would use the public key of the client to verify the request. which means RSA-SHA1 algo.
Twitter joauth seem to be useful but I am missing the code that would transform HttpServletRequest to OAuthRequest
The library read-me file suggests this as facility but I could not find a code that does javax.servlet.http.HttpServletRequest --> com.twitter.joauth.OAuthRequest transformation.
The request verification happens in verify method which has following signature.
public VerifierResult verify(UnpackedRequest.OAuth1Request request, String tokenSecret, String consumerSecret);
Secondly I also want to know which is the most appropriate way to use/read RSA public key with twitter joauth when verify method takes String parameter ?
I have never used any library to authenticate users via Twitter. But I have just looked in the UnpackedRequest.OAuth1Request. You can create an instance of this class by filling all parameters. I have written Twitter OAuth Header creator, so you can just use it to fill those parameters or send POST requests directly without a library.
Here all classes what you need:
Signature - to generate an OAuth Signature.
public class Signature {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
public static String calculateRFC2104HMAC(String data, String key)
throws java.security.SignatureException
{
String result;
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(data.getBytes());
result = new String(Base64.encodeBase64(rawHmac));
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
return result;
}
}
NvpComparator - to sort parameters you need in the header.
public class NvpComparator implements Comparator<NameValuePair> {
#Override
public int compare(NameValuePair arg0, NameValuePair arg1) {
String name0 = arg0.getName();
String name1 = arg1.getName();
return name0.compareTo(name1);
}
}
OAuth - for URL encode.
class OAuth{
...
public static String percentEncode(String s) {
return URLEncoder.encode(s, "UTF-8")
.replace("+", "%20").replace("*", "%2A")
.replace("%7E", "~");
}
...
}
HeaderCreator - to create all needed parameters and generate an OAuth header param.
public class HeaderCreator {
private String authorization = "OAuth ";
private String oAuthSignature;
private String oAuthNonce;
private String oAuthTimestamp;
private String oAuthConsumerSecret;
private String oAuthTokenSecret;
public String getAuthorization() {
return authorization;
}
public String getoAuthSignature() {
return oAuthSignature;
}
public String getoAuthNonce() {
return oAuthNonce;
}
public String getoAuthTimestamp() {
return oAuthTimestamp;
}
public HeaderCreator(){}
public HeaderCreator(String oAuthConsumerSecret){
this.oAuthConsumerSecret = oAuthConsumerSecret;
}
public HeaderCreator(String oAuthConsumerSecret, String oAuthTokenSecret){
this(oAuthConsumerSecret);
this.oAuthTokenSecret = oAuthTokenSecret;
}
public String getTwitterServerTime() throws IOException, ParseException {
HttpsURLConnection con = (HttpsURLConnection)
new URL("https://api.twitter.com/oauth/request_token").openConnection();
con.setRequestMethod("HEAD");
con.getResponseCode();
String twitterDate= con.getHeaderField("Date");
DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
Date date = formatter.parse(twitterDate);
return String.valueOf(date.getTime() / 1000L);
}
public String generatedSignature(String url, String method, List<NameValuePair> allParams,
boolean withToken) throws SignatureException {
oAuthNonce = String.valueOf(System.currentTimeMillis());
allParams.add(new BasicNameValuePair("oauth_nonce", oAuthNonce));
try {
oAuthTimestamp = getTwitterServerTime();
allParams.add(new BasicNameValuePair("oauth_timestamp", oAuthTimestamp));
}catch (Exception ex){
//TODO: Log!!
}
Collections.sort(allParams, new NvpComparator());
StringBuffer params = new StringBuffer();
for(int i=0;i<allParams.size();i++)
{
NameValuePair nvp = allParams.get(i);
if (i>0) {
params.append("&");
}
params.append(nvp.getName() + "=" + OAuth.percentEncode(nvp.getValue()));
}
String signatureBaseStringTemplate = "%s&%s&%s";
String signatureBaseString = String.format(signatureBaseStringTemplate,
OAuth.percentEncode(method),
OAuth.percentEncode(url),
OAuth.percentEncode(params.toString()));
String compositeKey = OAuth.percentEncode(oAuthConsumerSecret)+"&";
if(withToken) compositeKey+=OAuth.percentEncode(oAuthTokenSecret);
oAuthSignature = Signature.calculateRFC2104HMAC(signatureBaseString, compositeKey);
return oAuthSignature;
}
public String generatedAuthorization(List<NameValuePair> allParams){
authorization = "OAuth ";
Collections.sort(allParams, new NvpComparator());
for(NameValuePair nvm : allParams){
authorization+=nvm.getName()+"="+OAuth.percentEncode(nvm.getValue())+", ";
}
authorization=authorization.substring(0,authorization.length()-2);
return authorization;
}
}
Explain:
1. getTwitterServerTime
In oAuthTimestamp you need not your time of server but the time of a Twitter server. You can optimize it saving this param if you always send requests in the certain Twitter server.
2. HeaderCreator.generatedSignature(...)
url - logically url to twitter API
method - GET or POST. You must use always "POST"
allParams - Parameters which you know to generate signature ("param_name", "param_value");
withToken - if you know oAuthTokenSecret put true. Otherwise false.
3. HeaderCreator.generatedAuthorization(...)
Use this method after generatedSignature(...) to generate an OAuth header string.
allParams - it is parameters which you have used in generatedSignature(...) plus: nonce, signature, timestamp. Always use:
allParams.add(new BasicNameValuePair("oauth_nonce", headerCreator.getoAuthNonce()));
allParams.add(new BasicNameValuePair("oauth_signature", headerCreator.getoAuthSignature()));
allParams.add(new BasicNameValuePair("oauth_timestamp", headerCreator.getoAuthTimestamp()));
Now you can use it to fill UnpackedRequest.OAuth1Request in your library. Also here an example to authenticate user in SpringMVC without the library:
Requests - to send post requests.
public class Requests {
public static String sendPost(String url, String urlParameters, Map<String, String> prop) throws Exception {
URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
con.setRequestMethod("POST");
if(prop!=null) {
for (Map.Entry<String, String> entry : prop.entrySet()) {
con.setRequestProperty(entry.getKey(), entry.getValue());
}
}
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
BufferedReader in;
if(responseCode==200) {
in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
}else{
in = new BufferedReader(
new InputStreamReader(con.getErrorStream()));
}
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
}
}
twAuth(...) - put it in your controller. Execute it when an user want to authenticate in your site via Twitter.
#RequestMapping(value = "/twauth", method = RequestMethod.GET)
#ResponseBody
public String twAuth(HttpServletResponse response) throws Exception{
try {
String url = "https://api.twitter.com/oauth/request_token";
List<NameValuePair> allParams = new ArrayList<NameValuePair>();
allParams.add(new BasicNameValuePair("oauth_callback", "http://127.0.0.1:8080/twlogin"));
allParams.add(new BasicNameValuePair("oauth_consumer_key", "2YhNLyum1VY10UrWBMqBnatiT"));
allParams.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
allParams.add(new BasicNameValuePair("oauth_version", "1.0"));
HeaderCreator headerCreator = new HeaderCreator("RUesRE56vVWzN9VFcfA0jCBz9VkvkAmidXj8d1h2tS5EZDipSL");
headerCreator.generatedSignature(url,"POST",allParams,false);
allParams.add(new BasicNameValuePair("oauth_nonce", headerCreator.getoAuthNonce()));
allParams.add(new BasicNameValuePair("oauth_signature", headerCreator.getoAuthSignature()));
allParams.add(new BasicNameValuePair("oauth_timestamp", headerCreator.getoAuthTimestamp()));
Map<String, String> props = new HashMap<String, String>();
props.put("Authorization", headerCreator.generatedAuthorization(allParams));
String twitterResponse = Requests.sendPost(url,"",props);
Integer indOAuthToken = twitterResponse.indexOf("oauth_token");
String oAuthToken = twitterResponse.substring(indOAuthToken, twitterResponse.indexOf("&",indOAuthToken));
response.sendRedirect("https://api.twitter.com/oauth/authenticate?" + oAuthToken);
}catch (Exception ex){
//TODO: Log
throw new Exception();
}
return "main";
}
twLogin(...) - put it in your controller. It is callback from Twitter.
#RequestMapping(value = "/twlogin", method = RequestMethod.GET)
public String twLogin(#RequestParam("oauth_token") String oauthToken,
#RequestParam("oauth_verifier") String oauthVerifier,
Model model, HttpServletRequest request){
try {
if(oauthToken==null || oauthToken.equals("") ||
oauthVerifier==null || oauthVerifier.equals(""))
return "main";
String url = "https://api.twitter.com/oauth/access_token";
List<NameValuePair> allParams = new ArrayList<NameValuePair>();
allParams.add(new BasicNameValuePair("oauth_consumer_key", "2YhNLyum1VY10UrWBMqBnatiT"));
allParams.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
allParams.add(new BasicNameValuePair("oauth_token", oauthToken));
allParams.add(new BasicNameValuePair("oauth_version", "1.0"));
NameValuePair oAuthVerifier = new BasicNameValuePair("oauth_verifier", oauthVerifier);
allParams.add(oAuthVerifier);
HeaderCreator headerCreator = new HeaderCreator("RUesRE56vVWzN9VFcfA0jCBz9VkvkAmidXj8d1h2tS5EZDipSL");
headerCreator.generatedSignature(url,"POST",allParams,false);
allParams.add(new BasicNameValuePair("oauth_nonce", headerCreator.getoAuthNonce()));
allParams.add(new BasicNameValuePair("oauth_signature", headerCreator.getoAuthSignature()));
allParams.add(new BasicNameValuePair("oauth_timestamp", headerCreator.getoAuthTimestamp()));
allParams.remove(oAuthVerifier);
Map<String, String> props = new HashMap<String, String>();
props.put("Authorization", headerCreator.generatedAuthorization(allParams));
String twitterResponse = Requests.sendPost(url,"oauth_verifier="+oauthVerifier,props);
//Get user id
Integer startIndexTmp = twitterResponse.indexOf("user_id")+8;
Integer endIndexTmp = twitterResponse.indexOf("&",startIndexTmp);
if(endIndexTmp<=0) endIndexTmp = twitterResponse.length()-1;
Long userId = Long.parseLong(twitterResponse.substring(startIndexTmp, endIndexTmp));
//Do what do you want...
}catch (Exception ex){
//TODO: Log
throw new Exception();
}
}

commons httpclient - Adding query string parameters to GET/POST request

I am using commons HttpClient to make an http call to a Spring servlet. I need to add a few parameters in the query string. So I do the following:
HttpRequestBase request = new HttpGet(url);
HttpParams params = new BasicHttpParams();
params.setParameter("key1", "value1");
params.setParameter("key2", "value2");
params.setParameter("key3", "value3");
request.setParams(params);
HttpClient httpClient = new DefaultHttpClient();
httpClient.execute(request);
However when i try to read the parameter in the servlet using
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getParameter("key");
it returns null. In fact the parameterMap is completely empty. When I manually append the parameters to the url before creating the HttpGet request, the parameters are available in the servlet. Same when I hit the servlet from the browser using the URL with queryString appended.
What's the error here? In httpclient 3.x, GetMethod had a setQueryString() method to append the querystring. What's the equivalent in 4.x?
Here is how you would add query string parameters using HttpClient 4.2 and later:
URIBuilder builder = new URIBuilder("http://example.com/");
builder.setParameter("parts", "all").setParameter("action", "finish");
HttpPost post = new HttpPost(builder.build());
The resulting URI would look like:
http://example.com/?parts=all&action=finish
If you want to add a query parameter after you have created the request, try casting the HttpRequest to a HttpBaseRequest. Then you can change the URI of the casted request:
HttpGet someHttpGet = new HttpGet("http://google.de");
URI uri = new URIBuilder(someHttpGet.getURI()).addParameter("q",
"That was easy!").build();
((HttpRequestBase) someHttpGet).setURI(uri);
The HttpParams interface isn't there for specifying query string parameters, it's for specifying runtime behaviour of the HttpClient object.
If you want to pass query string parameters, you need to assemble them on the URL yourself, e.g.
new HttpGet(url + "key1=" + value1 + ...);
Remember to encode the values first (using URLEncoder).
I am using httpclient 4.4.
For solr query I used the following way and it worked.
NameValuePair nv2 = new BasicNameValuePair("fq","(active:true) AND (category:Fruit OR category1:Vegetable)");
nvPairList.add(nv2);
NameValuePair nv3 = new BasicNameValuePair("wt","json");
nvPairList.add(nv3);
NameValuePair nv4 = new BasicNameValuePair("start","0");
nvPairList.add(nv4);
NameValuePair nv5 = new BasicNameValuePair("rows","10");
nvPairList.add(nv5);
HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(url);
URI uri = new URIBuilder(request.getURI()).addParameters(nvPairList).build();
request.setURI(uri);
HttpResponse response = client.execute(request);
if (response.getStatusLine().getStatusCode() != 200) {
}
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String output;
System.out.println("Output .... ");
String respStr = "";
while ((output = br.readLine()) != null) {
respStr = respStr + output;
System.out.println(output);
}
This approach is ok but will not work for when you get params dynamically , sometimes 1, 2, 3 or more, just like a SOLR search query (for example)
Here is a more flexible solution. Crude but can be refined.
public static void main(String[] args) {
String host = "localhost";
String port = "9093";
String param = "/10-2014.01?description=cars&verbose=true&hl=true&hl.simple.pre=<b>&hl.simple.post=</b>";
String[] wholeString = param.split("\\?");
String theQueryString = wholeString.length > 1 ? wholeString[1] : "";
String SolrUrl = "http://" + host + ":" + port + "/mypublish-services/carclassifications/" + "loc";
GetMethod method = new GetMethod(SolrUrl );
if (theQueryString.equalsIgnoreCase("")) {
method.setQueryString(new NameValuePair[]{
});
} else {
String[] paramKeyValuesArray = theQueryString.split("&");
List<String> list = Arrays.asList(paramKeyValuesArray);
List<NameValuePair> nvPairList = new ArrayList<NameValuePair>();
for (String s : list) {
String[] nvPair = s.split("=");
String theKey = nvPair[0];
String theValue = nvPair[1];
NameValuePair nameValuePair = new NameValuePair(theKey, theValue);
nvPairList.add(nameValuePair);
}
NameValuePair[] nvPairArray = new NameValuePair[nvPairList.size()];
nvPairList.toArray(nvPairArray);
method.setQueryString(nvPairArray); // Encoding is taken care of here by setQueryString
}
}
This is how I implemented my URL builder.
I have created one Service class to provide the params for the URL
public interface ParamsProvider {
String queryProvider(List<BasicNameValuePair> params);
String bodyProvider(List<BasicNameValuePair> params);
}
The Implementation of methods are below
#Component
public class ParamsProviderImp implements ParamsProvider {
#Override
public String queryProvider(List<BasicNameValuePair> params) {
StringBuilder query = new StringBuilder();
AtomicBoolean first = new AtomicBoolean(true);
params.forEach(basicNameValuePair -> {
if (first.get()) {
query.append("?");
query.append(basicNameValuePair.toString());
first.set(false);
} else {
query.append("&");
query.append(basicNameValuePair.toString());
}
});
return query.toString();
}
#Override
public String bodyProvider(List<BasicNameValuePair> params) {
StringBuilder body = new StringBuilder();
AtomicBoolean first = new AtomicBoolean(true);
params.forEach(basicNameValuePair -> {
if (first.get()) {
body.append(basicNameValuePair.toString());
first.set(false);
} else {
body.append("&");
body.append(basicNameValuePair.toString());
}
});
return body.toString();
}
}
When we need the query params for our URL, I simply call the service and build it.
Example for that is below.
Class Mock{
#Autowired
ParamsProvider paramsProvider;
String url ="http://www.google.lk";
// For the query params price,type
List<BasicNameValuePair> queryParameters = new ArrayList<>();
queryParameters.add(new BasicNameValuePair("price", 100));
queryParameters.add(new BasicNameValuePair("type", "L"));
url = url+paramsProvider.queryProvider(queryParameters);
// You can use it in similar way to send the body params using the bodyProvider
}
Im using Java 8 and apache httpclient 4.5.13
HashMap<String, String> customParams = new HashMap<>();
customParams.put("param1", "ABC");
customParams.put("param2", "123");
URIBuilder uriBuilder = new URIBuilder(baseURL);
for (String paramKey : customParams.keySet()) {
uriBuilder.addParameter(paramKey, customParams.get(paramKey));
}
System.out.println(uriBuilder.build().toASCIIString()); // ENCODED URL
System.out.println(uriBuilder.build().toString); // NORMAL URL
Full example with DTO
public class HttpResponseDTO {
private Integer statusCode;
private String body;
private String errorMessage;
public Integer getStatusCode() {
return statusCode;
}
public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
/**
*
* #param destinationURL
* #param params
* #param headers
* #return HttpResponseDTO
*/
public static HttpResponseDTO get(String baseURL, Boolean encodeURL, HashMap<String, String> params, HashMap<String, String> headers) {
final HttpResponseDTO httpResponseDTO = new HttpResponseDTO();
// ADD PARAMS IF
if (params != null && Boolean.FALSE.equals(params.isEmpty())) {
URIBuilder uriBuilder;
try {
uriBuilder = new URIBuilder(baseURL);
for (String paramKey : params.keySet()) {
uriBuilder.addParameter(paramKey, params.get(paramKey));
}
// CODIFICAR URL ?
if (Boolean.TRUE.equals(encodeURL)) {
baseURL = uriBuilder.build().toASCIIString();
} else {
baseURL = uriBuilder.build().toString();
}
} catch (URISyntaxException e) {
httpResponseDTO.setStatusCode(500);
httpResponseDTO.setErrorMessage("ERROR AL CODIFICAR URL: " + e.getMessage());
return httpResponseDTO;
}
}
// HACER PETICION HTTP
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
final HttpGet get = new HttpGet(baseURL);
// ADD HEADERS
if (headers != null && Boolean.FALSE.equals(headers.isEmpty())) {
for (String headerKey : headers.keySet()) {
get.setHeader(headerKey, headers.get(headerKey));
}
}
try (CloseableHttpResponse response = httpClient.execute(get);) {
HttpEntity httpEntity = response.getEntity();
if (httpEntity != null) {
httpResponseDTO.setBody(EntityUtils.toString(httpEntity));
httpResponseDTO.setStatusCode(response.getStatusLine().getStatusCode());
}
} catch(Exception e) {
httpResponseDTO.setStatusCode(500);
httpResponseDTO.setErrorMessage(e.getMessage());
return httpResponseDTO;
}
} catch(Exception e) {
httpResponseDTO.setStatusCode(500);
httpResponseDTO.setErrorMessage(e.getMessage());
return httpResponseDTO;
}
return httpResponseDTO;
}

Add SoapHeader to org.springframework.ws.WebServiceMessage

How can I add an object into the soap header of a org.springframework.ws.WebServiceMessage
This is the structure I'm looking to end up with:
<soap:Header>
<credentials xmlns="http://example.com/auth">
<username>username</username>
<password>password</password>
</credentials>
</soap:Header>
Basically, you need to use a WebServiceMessageCallback in your client to modify the message after its creation but before it is sent. To rest of the code has been described pretty accurately by #skaffman so the whole stuff might look like this:
public void marshalWithSoapActionHeader(MyObject o) {
webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
try {
SoapMessage soapMessage = (SoapMessage)message;
SoapHeader header = soapMessage.getSoapHeader();
StringSource headerSource = new StringSource("<credentials xmlns=\"http://example.com/auth\">\n +
<username>"+username+"</username>\n +
<password>"+password"+</password>\n +
</credentials>");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
} catch (Exception e) {
// exception handling
}
}
});
}
Personally, I find that Spring-WS sucks hard for such a basic need, they should fix SWS-479.
You can do as below:
public class SoapRequestHeaderModifier implements WebServiceMessageCallback {
private final String userName = "user";
private final String passWd = "passwd";
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
if (message instanceof SaajSoapMessage) {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
MimeHeaders mimeHeader = soapMessage.getSaajMessage().getMimeHeaders();
mimeHeader.setHeader("Authorization", getB64Auth(userName, passWd));
}
}
private String getB64Auth(String login, String pass) {
String source = login + ":" + pass;
String retunVal = "Basic " + Base64.getUrlEncoder().encodeToString(source.getBytes());
return retunVal;
}
}
Then
Object response = getWebServiceTemplate().marshalSendAndReceive(request, new SoapRequestHeaderModifier());
You need to cast the WebServiceMessage to SoapMessage, which has a getSoapHeader() method you can use to modify the header. In turn, SoapHeader has various methods for adding elements, including getResult() (which can be used as the output of a Transformer.transform() operation).
I tried many options and finally below one worked for me if you have to send soap header with authentication(Provided authentication object created by wsimport) and also need to set soapaction.
public Response callWebService(String url, Object request)
{
Response res = null;
log.info("The request object is " + request.toString());
try {
res = (Response) getWebServiceTemplate().marshalSendAndReceive(url, request,new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) {
try {
// get the header from the SOAP message
SoapHeader soapHeader = ((SoapMessage) message).getSoapHeader();
// create the header element
ObjectFactory factory = new ObjectFactory();
Authentication auth =
factory.createAuthentication();
auth.setUser("****");
auth.setPassword("******");
((SoapMessage) message).setSoapAction(
"soapAction");
JAXBElement<Authentication> headers =
factory.createAuthentication(auth);
// create a marshaller
JAXBContext context = JAXBContext.newInstance(Authentication.class);
Marshaller marshaller = context.createMarshaller();
// marshal the headers into the specified result
marshaller.marshal(headers, soapHeader.getResult());
} catch (Exception e) {
log.error("error during marshalling of the SOAP headers", e);
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
You can achieve it by creating key-value map of child elements as well:
final Map<String, String> elements = new HashMap<>();
elements.put("username", "username");
elements.put("password", "password");
Set up namespace and prefix of your child element within the soap header:
final String LOCAL_NAME = "credentials";
final String PREFIX = "";
final String NAMESPACE = "http://example.com/auth";
Then, you can invoke WebServiceTemplate's method marshalSendAndReceive where you override WebServiceMessageCallback's method doWithMessage as follows:
Object response = getWebServiceTemplate().marshalSendAndReceive(request, (message) -> {
if (message instanceof SaajSoapMessage) {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
if (Objects.nonNull(elements)) {
try {
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPHeader soapHeader = soapEnvelope.getHeader();
Name headerElementName = soapEnvelope.createName(
LOCAL_NAME,
PREFIX,
NAMESPACE
);
SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(headerElementName);
elements.forEach((key, value) -> {
try {
SOAPElement element = soapHeaderElement.addChildElement(key, PREFIX);
element.addTextNode(value);
} catch (SOAPException e) {
// error handling
}
});
soapMessage.saveChanges();
} catch (SOAPException e) {
// error handling
}
}
}
});
The above steps result in:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header>
<credentials xmlns="http://example.com/auth">
<password>password</password>
<username>username</username>
</credentials>
</env:Header>
<env:Body>
<!-- your payload -->
</env:Body>
</env:Envelope>
Response response = (Response)getWebServiceTemplate() .marshalSendAndReceive(request, new HeaderModifier());
Create class HeaderModifier and override doWithMessage
public class HeaderModifier implements WebServiceMessageCallback {
private static PrintStream out = System.out;
#Override
public void doWithMessage(WebServiceMessage message) throws IOException {
SaajSoapMessage soapMessage = (SaajSoapMessage) message;
SoapEnvelope soapEnvelope = soapMessage.getEnvelope();
SoapHeader soapHeader = soapEnvelope.getHeader();
//Initialize QName for Action and To
QName action = new QName("{uri}","Action","{actionname}");
QName to = new QName("{uri}","To","{actionname}");
soapHeader.addNamespaceDeclaration("{actionname}", "{uri}");
SoapHeaderElement soapHeaderElementAction = soapHeader.addHeaderElement(action);
SoapHeaderElement soapHeaderElementTo = soapHeader.addHeaderElement(to);
soapHeaderElementAction.setText("{text inside the tags}");
soapHeaderElementTo.setText("{text inside the tags}");
soapMessage.setSoapAction("{add soap action uri}");
soapMessage.writeTo(out);
}
}

Categories