I am logging RequestXML for a webservice client using SoapHandler as follows
public boolean handleMessage(SOAPMessageContext smc) {
logToSystemOut(smc);
return true;
}
private void logToSystemOut(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
out.println("\nOutbound message:");
} else {
out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
message.writeTo(out);
out.println("");
} catch (Exception e) {
out.println("Exception in handler: " + e);
}
}
Got a new requirenment to add this xml to DB along with some extra values(which are not present in the xml). Is there any way I can pass few additional fields to above soap handler (in handleMessage method)?
Please note that changing the xml/WSDL or adding this to SOAP message header is not an option for me as it is owned by other interface. Any other solution?
Thanks!
You can cast your service class to a class of type "BindingProvider". In this form you can use it to assign it objects which you can access later from your SOAPHandler. Another useful usage is that you also can change the endPoint URL this way.
Before calling the service you do:
MySoapServicePortType service = new MySoapService().getMySoapServicePort();
BindingProvider bp = (BindingProvider)service;
MyTransferObject t = new MyTransferObject();
bp.getRequestContext().put("myTransferObject", t);
TypeResponse response = service.doRequest();
SOAPMessage message = t.getRequestMessage(message);
From your logging function you do:
private void logToSystemOut(SOAPMessageContext smc) {
...
MyTransferObject t = (MyTransferObject) messageContext.get("myTransferObject");
if (outboundProperty.booleanValue())
t.setRequestMessage(message);
else
t.setResponseMessage(message);
...
}
Related
I am facing strange issue where response as string not coming back from one service to another service. we have created microservices where one service is calling another service. i can see response printed in logs . after that line immediately i am returning that response but its coming back as null.
I created similar method with same code and it works fine. I have put code for calling service and service method from which i am returning response.
controller:
#RequestMapping(value = "/test/save", method = RequestMethod.POST)
#ResponseBody
public String save(#RequestBody Calculation calculation,
HttpServletRequest request) {
logger.info("In .save");
String result = "false";
try {
result = CalService.save(calculation);
logger.info("Response from service is :" + result);
} catch (Exception e) {
logger.error("Exception occured in save:", e);
}
return result;
}
method call client :
public String saveCal(Calculation calculation) {
String result = null;
try {
logger.info("In save");
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("REMOTE_USER", "test");
HttpEntity<Calculation> request = new HttpEntity<Calculation>(Calculation, headers);
RestTemplate template = new RestTemplate();
template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
result = template.postForObject(url+"/test/save", request, String.class);
} catch (Exception e) {
logger.error("Exception occured in SaveMddMd", e);
result = "fail";
}
logger.info("Save"+result);
return result;
}
result returned is success or failure.
I can see result printed in controller as success but when it comes back to client it prints as null. I created exact same method with different signature which returns result as success. we are using microservices here.
Jordan
I have a method I need to refactor, as F.Promise has been deprecated in Play 2.5. It's pretty readable actually. It sends a request and authenticates via a custom security token and returns true if the response is 200.
public boolean verify(final String xSassToken){
WSRequest request = WS.url(mdVerifyXSassTokenURL)
.setHeader("X-SASS", xSassToken)
.setMethod("GET");
final F.Promise<WSResponse> responsePromise = request.execute();
try {
final WSResponse response = responsePromise.get(10000);
int status = response.getStatus();
if(status == 200 ) { //ok
return true;
}
} catch (Exception e) {
return false;
}
return false;
}
First thing I had to do was change this line:
final F.Promise<WSResponse> responsePromise = request.execute();
To this:
final CompletionStage<WSResponse> responsePromise = request.execute();
However, CompletionStage(T) doesn't have an equivalent get() method so I'm not sure the quickest and easiest way to get a WSResponse that I can verify the status of.
Yes, it does not. At least not directly.
What you are doing is "wrong" in the context of PlayFramework. get is a blocking call and you should avoid blocking as much as possible. That is why WS offers a non blocking API and a way to handle asynchronous results. So, first, you should probably rewrite your verify code to be async:
public CompletionStage<Boolean> verify(final String xSassToken) {
return WS.url(mdVerifyXSassTokenURL)
.setHeader("X-SASS", xSassToken)
.setMethod("GET")
.execute()
.thenApply(response -> response.getStatus() == Http.Status.OK);
}
Notice how I'm using thenApply to return a new a java.util.concurrent.CompletionStage instead of a plain boolean. That means that the code calling verify can also do the same. Per instance, an action at your controller can do something like this:
public class MyController extends Controller {
public CompletionStage<Result> action() {
return verify("whatever").thenApply(success -> {
if (success) return ok("successful request");
else return badRequest("xSassToken was not valid");
});
}
public CompletionStage<Boolean> verify(final String xSassToken) { ... }
}
This way your application will be able to handle a bigger workload without hanging.
Edit:
Since you have to maintain compatibility, this is what I would do to both evolve the design and also to keep code compatible while migrating:
/**
* #param xSassToken the token to be validated
* #return if the token is valid or not
*
* #deprecated Will be removed. Use {#link #verifyToken(String)} instead since it is non blocking.
*/
#Deprecated
public boolean verify(final String xSassToken) {
try {
return verifyToken(xSassToken).toCompletableFuture().get(10, TimeUnit.SECONDS);
} catch (Exception e) {
return false;
}
}
public CompletionStage<Boolean> verifyToken(final String xSassToken) {
return WS.url(mdVerifyXSassTokenURL)
.setHeader("X-SASS", xSassToken)
.setMethod("GET")
.execute()
.thenApply(response -> response.getStatus() == Http.Status.OK);
}
Basically, deprecate the old verify method and suggest users to migrate to new one.
For some reason there is no 'Content-Length' HTTP headers in CXF response.
I've decided to implement interceptor that does it, but unfortunately there is no added header in sent data (intercepted using Wireshark). I can set breakpoint and i can see interceptor is invoked. What's wrong?
/**
* Adds Content-Length header for the outcoming messages
*/
public class AddContentLengthInterceptor extends AbstractLoggingInterceptor {
private static final String CONTENT_LENGTH_ADDED = AddContentLengthInterceptor.class.getName() + ".log-setup";
public AddContentLengthInterceptor(String phase) {
super(phase);
addBefore(StaxOutInterceptor.class.getName());
}
public AddContentLengthInterceptor() {
this(Phase.PRE_PROTOCOL); // before streaming
}
#Override
protected Logger getLogger() {
return null;
}
#Override
public void handleMessage(Message message) throws Fault {
final OutputStream os = message.getContent(OutputStream.class);
final Writer iowriter = message.getContent(Writer.class);
if (os == null && iowriter == null) {
return;
}
// ignore double processing of the message
boolean hasAddedHeader = message.containsKey(CONTENT_LENGTH_ADDED);
if (!hasAddedHeader) {
message.put(CONTENT_LENGTH_ADDED, Boolean.TRUE);
if (os != null) {
// Write the output while caching it for adding header later
final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(os);
message.setContent(OutputStream.class, newOut);
newOut.registerCallback(new LoggingCallback(message, os));
}
}
}
class LoggingCallback implements CachedOutputStreamCallback {
private final Message message;
private final OutputStream origStream;
public LoggingCallback(final Message msg, final OutputStream os) {
this.message = msg;
this.origStream = os;
}
public void onFlush(CachedOutputStream cos) {
}
public void onClose(CachedOutputStream cos) {
long contentLength = cos.size();
Map<String, List<String>> headers = (Map<String, List<String>>) message.get(Message.PROTOCOL_HEADERS);
if (headers == null)
headers = new HashMap<String, List<String>>();
headers.put("Content-Length", Arrays.asList(String.valueOf(contentLength)));
message.put(Message.PROTOCOL_HEADERS, headers);
try {
// empty out the cache
cos.lockOutputStream();
cos.resetOut(null, false);
} catch (Exception ex) {
//ignore
}
message.setContent(OutputStream.class, origStream);
}
}
}
This is now server-side endpoints are created:
mediaService = new MediaService(ip, rtspPort, streamUri);
ProviderImpl provider = new ProviderImpl();
mediaEndpoint = (EndpointImpl) provider.createEndpoint(null, mediaService);
String mediaServiceURL = MessageFormat.format("http://{0}:{1}/onvif/media_service", ip, String.valueOf(port));
mediaEndpoint.publish(mediaServiceURL);
// add "Content-Length" header
mediaEndpoint.getServer().getEndpoint().getOutInterceptors().add(contentLengthInterceptor);
I think you are using Transfer-Encoding as chunked. Which results in no content-length header since it is defined that way in RFC.
Messages MUST NOT include both a Content-Length header field and a
non-identity transfer-coding. If the message does include a
non-identity transfer-coding, the Content-Length MUST be ignored.
I am not exactly sure but, either cxf removes your header since it is not allowed or it sets but sub layers remove it. As far as I know cxf uses HttpUrlConnection to transport your message. And that layer sets Content-length header on normal cases. But since you are using chunked transfer, that layer may override it.
In order to fix it you must change your Transfer-Encoding. And since CXF's itself sets content-length header you are no longer need to write your Interceptor.
By the way, if you use a custom header, you will realise that your interceptor works like a charm.
History:
A WSDL was provided to me with which I generated a service reference.
The object to serialize and send to the web service to consume, was a strongly, complex-typed class.
Instead of sending a strongly, complex-typed class, I wanted to send an XMLDocument instead.
So I modified the service and I am left with this:
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.17929")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name = "B2MML_ProcessProductionPerformance_MaterialConsumption_OBBinding", Namespace="http://company.com/M2D/Manufacturing/ManufacturingExecution/GoodsIssue/pi")]
public partial class B2MML_ProcessProductionPerformance_MaterialConsumption_OBService : System.Web.Services.Protocols.SoapHttpClientProtocol {
private System.Threading.SendOrPostCallback B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted;
public B2MML_ProcessProductionPerformance_MaterialConsumption_OBService()
{
}
public event B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompletedEventHandler B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompleted;
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://sap.com/xi/WebService/soap1.1", OneWay=true, Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
public void B2MML_ProcessProductionPerformance_MaterialConsumption_OB(XmlDocument ProcessProductionPerformance)
{
this.Invoke("B2MML_ProcessProductionPerformance_MaterialConsumption_OB", new object[] {
ProcessProductionPerformance});
}
public System.IAsyncResult BeginB2MML_ProcessProductionPerformance_MaterialConsumption_OB(XmlDocument ProcessProductionPerformance, System.AsyncCallback callback, object asyncState)
{
return this.BeginInvoke("B2MML_ProcessProductionPerformance_MaterialConsumption_OB", new object[] {
ProcessProductionPerformance}, callback, asyncState);
}
public void EndB2MML_ProcessProductionPerformance_MaterialConsumption_OB(System.IAsyncResult asyncResult)
{
this.EndInvoke(asyncResult);
}
public void B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(XmlDocument ProcessProductionPerformance)
{
this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(ProcessProductionPerformance, null);
}
public void B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(XmlDocument ProcessProductionPerformance, object userState)
{
if ((this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted == null))
{
this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted = new System.Threading.SendOrPostCallback(this.OnB2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted);
}
this.InvokeAsync("B2MML_ProcessProductionPerformance_MaterialConsumption_OB", new object[] {
ProcessProductionPerformance}, this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted, userState);
}
private void OnB2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted(object arg)
{
if ((this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompleted != null))
{
System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompleted(this, new System.ComponentModel.AsyncCompletedEventArgs(invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
}
}
public new void CancelAsync(object userState)
{
base.CancelAsync(userState);
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.17929")]
public delegate void B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompletedEventHandler(object sender, System.ComponentModel.AsyncCompletedEventArgs e);
Problem Decription:
I am able to send an XMLDocument successfully, however, if my XMLDocument looks like this:
<ProcessProductionPerformance xmlns="SomeNS">
<ApplicationArea>Some data here</ApplicationArea>
<DataArea>Some data there</DataArea>
</ProcessProductionPerformance>
I am seeing the below in the SOAP Body (seen via Fiddler):
<ProcessProductionPerformance>
<ProcessProductionPerformance xmlns="SomeNS">
<ApplicationArea>Some data here</ApplicationArea>
<DataArea>Some data there</DataArea>
</ProcessProductionPerformance>
</ProcessProductionPerformance>
Information:
1) I do not have access to modify the web service
2) I've tried to pack XMLDocument ApplicationArea and XMLDocument DataArea individually into a class and try to serialize the class, I end up with this in the SOAP Body:
<ProcessProductionPerformance xmlns="SomeNS">
<ApplicationArea>
<ApplicationArea xmlns="SomeNS">Some data here</ApplicationArea>
<ApplicationArea>
<DataArea>
<DataArea xmlns="SomeNS">Some data there</DataArea>
<DataArea>
</ProcessProductionPerformance>
3) I suspect it's to do with the SOAPBindingUse or SOAPBindingStyle perhaps? I did not change this at all because I don't know much about it.
4) I'm just a kid new to C#.. Please have mercy.
EDIT:
Okay it seems that the enums SoapBindingUse.Literal and SoapParameterStyle.Bare have a major role to play in this. But I'm still stuck because my use of this has been correct all along.
https://msdn.microsoft.com/en-us/library/vstudio/2b4bx2t6%28v=vs.100%29.aspx
So what am I missing?
By the way, not sure if you humans care about the client but I'm calling the service like this:
var processProductionPerformance = new XmlDocument();
processProductionPerformance.LoadXml(#xmlText);
var sendPerformanceToSap = new B2MML_ProcessProductionPerformance_MaterialConsumption_OBService
{
//Url = Link here,
//Credentials since
};
sendPerformanceToSap.B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(processProductionPerformance);
Hi I have a webService that is generated from buildServices of Liferay..
the method looks like this
public User getUserTest(long userId) {
User u = null;
try {
Token token = OAuthFactoryUtil.createToken("sasa", "sdad");
} catch (OAuthException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
u = UserLocalServiceUtil.getUser(userId);
System.out.println("xx user " + u.getScreenName());
} catch (Exception e) {
System.out.println(" Exception ************* " + e.toString());
}
return u;
}
the parameters in this ws would be this :
http://localhost:8080/demo-portlet/api/json?serviceClassName=com.sample.portlet.library.service.BookServiceUtil&serviceMethodName=getUserTest&userId=10195&serviceParameters=[userId]
having userId as a parameter..
How would you pass a parameter if you need HttpServletRequest.. my method would look like this
public User getUserTest(HttpServletRequest httpRequest) {
User u = null;
try {
String version = httpRequest.getHeader("X-PHM-APP-VERSION");
Token token = OAuthFactoryUtil.createToken("sasa", "sdad");
} catch (OAuthException e1) {
e1.printStackTrace();
}
try {
String authorization = httpRequest.getHeader("Authorization");
u = UserLocalServiceUtil.getUser(Long.valueOf(authorization));
System.out.println("authorization --> " + authorization);
System.out.println("xx user " + u.getScreenName());
} catch (Exception e) {
System.out.println(" Exception ************* " + e.toString());
}
return u;
}
I need the HttpServletRequest to get the parameters from header, instead of passing through url. Is there a better way to get parameters from header? thanks for your help
I think webservice layer is normally at a later stage where in you would never pass request. Ideally what you would do is pass header parameter to the webservice instead of request
In Liferay, you will get HttpServletRequest from the PortletRequest. Please use com.liferay.portal.util.PortalUtil class.
There are 2 methods in it. getHttpServletRequest() and getOriginalServletRequest(), you will get the both core level http request from these methods.