Adding Header To A SOAP Call (Java equavalent in C#) - java

The SOAP API I am intending to use has given a working example in Java. In every request to the API one should add three values to the header (I just guess they are a domain, a password and api key). To this aim we override the org.apache.axis.client.Stub like this:
public class SeveraApiStubBase extends org.apache.axis.client.Stub {
#Override
public org.apache.axis.client.Call _createCall() throws ServiceException {
org.apache.axis.client.Call _call = super._createCall();
_call.addHeader(new org.apache.axis.message.SOAPHeaderElement(
"http://something.somethingelse.com/", "WebServicePassword", "API_KEY"));
return _call;
}
}
And then we run the method with the provided header.
I was wondering what the equivalent is in C#.
Update: The use of the IClientMessageInspector class
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
{
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER]))
{
httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent;
}
}
else
{
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}

Normally you would use WCF if using C# rather than Axis. It is a little different of an approach than Axis.
Assuming you're making a client consuming an existing WSDL, you would start by using svcutil to generate your service contract code. Here is a link that describes this part. The example they give is a service with primitive inputs to all operations, so it doesn't show generation of complex type classes.
You can then use message inspectors to intercept the outgoing request and add a header. The IClientMessageInspector interface has the method BeforeSendRequest that passes a Message class as an argument. The Message class has a Headers collection where you can add whatever headers you need.

Related

Getting the actual class of a message body

I'm passing a List of different objects to a camel route. I would like the route to split the body into one object per message and put the class of the body in a header (using a processor).
from("direct:in")
.split(body())
.process(new JmsTypeHeaderProcessor(body().getClass().getName()))
.to("mock:out");
I'm trying it like this...
#Produce(uri = "direct:in") private ProducerTemplate template;
#EndpointInject(uri = "mock:out") private MockEndpoint endpoint;
#Test
public void testRoute() throws Exception {
List<Object> list = new ArrayList<>();
list.add("String");
list.add(Integer.valueOf(1));
list.add(Boolean.FALSE);
template.sendBody(list);
for (Exchange ex : endpoint.getExchanges()) {
System.out.println("JMSType=" + ex.getIn().getHeader("JMSType"));
}
}
When I run that I find I actually have the headers
JMSType=org.apache.camel.builder.ValueBuilder
JMSType=org.apache.camel.builder.ValueBuilder
JMSType=org.apache.camel.builder.ValueBuilder
whereas I expected, and would like
JMSType=java.lang.String
JMSType=java.lang.Integer
JMSType=java.lang.Boolean
What is needed to get the class of the actual body?
BTW. I can see that log("body.class") returns what I want but I have not been able to follow how it works or adapt it for my needs.
The Camel routes are designed in the route builder and the code is run once, to setup the routes.
So this code
.process(new JmsTypeHeaderProcessor(body().getClass().getName()))
Is invoked once, and body().getClass() returns the ValueBuilder as that is what is used at design time in the DSL to specify body etc.
If you want to access the runtime message body, then get that from the Exchange from the process method of your processor. That is the runtime message and then you can get the body.

What are the methods testing POST to prevent status=405?

I was trying to build a RESTful web service using Jersey.
In my server side code, there is a path with name "domain" which I use to display content. The content of the page the "domain" refers to is accessible only correct username and password are input.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Path("domain")
public ArrayList<String> domainList(#Context HttpServletRequest req) throws Exception{
Environments environments = new DefaultConfigurationBuilder().build();
final ALMProfile profile = new ALMProfile();
profile.setUrl(environments.getAutomation().getAlmProfile().getUrl());
profile.setUsername((String) req.getSession().getAttribute("username"));
//Set username from input, HTML form
profile.setPassword((String) req.getSession().getAttribute("password"));
//Set password from input, HTML form
try (ALMConnection connection = new ALMConnection(profile);) {
if (connection.getOtaConnector().connected()) {
Multimap<String, String> domain = connection.getDomains();
ArrayList<String> domain_names = new ArrayList<String>();
for(String key : domain.keys()){
if(domain_names.contains(key)) domain_names.add(key);
}
return domain_names; //return the content
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return null;
}
When I attempted to test if correct content was returned, I got an error (status=405, reason=Method Not Allowed). Below is my client side test.
public static void main(String[] args){
Environments environments = new DefaultConfigurationBuilder().build();
final ALMProfile profile = new ALMProfile();
profile.setUrl(environments.getAutomation().getAlmProfile().getUrl());
profile.setUsername("username"); //Creating a profile with username and password
profile.setPassword("password");
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target(getBaseURI());
String response = target.path("domain").request().accept
(MediaType.APPLICATION_JSON).get(Response.class).toString();
//Above is the GET method I see from an example,
//probably is the reason why 405 error comes from.
System.out.println(response);
}
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost:8080/qa-automation-console").build();
}
The servlet configuration is good. We have other paths succesfully running.
I suspect the reason might come from I used a GET method to do the job that is supposed to be POST.
But I am not familiar to Jersey methods I can use.
Does anyone know any methods that I can use to test the functionality?
See 405 Status Code
405 Method Not Allowed
The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. The response MUST include an Allow header containing a list of valid methods for the requested resource.
Your endpoint is for a #POST request. In your client you are trying to get().
See the Client API documentation for information on how to make a POST request. If it is supposed to be a GET request, then simply change the method annotation to #GET.
Also note, for your #POST resource methods, you should always put a #Consumes annotation with the media types the method supports. If the client send a media type not supported, then they will get a 415 not supported as expected. I would have posted an example of the client post, but I have no idea what type are you are expecting because of the missing annotation, also you don't even have a post object as a method parameter so I am not even sure if your method is really even supposed to be for POST.
See Also:
How to send json object from REST client using javax.ws.rs.client.WebTarget

How can I pass data back from a SOAP handler to a webservice client?

(Following up on this question: Getting raw XML response from Java web service client)
I've got a SOAP message handler that is able to get the raw XML of a web service response. I need to get this XML into the webservice client so I can perform some XSL transformations on the response before sending it on its way. I'm having trouble figuring out a good way to get data from a SOAP handler that catches incoming messages, and makes the raw XML available to a generated (from a WSDL) web service client. Any ideas if this is even feasible?
I've come up with something like this:
public class CustomSOAPHandler implements javax.xml.ws.handler.soap.SOAPHandler<javax.xml.ws.handler.soap.SOAPMessageContext>
{
private String myXML;
public String getMyXML()
{
return myXML;
}
...
public boolean handleMessage(SOAPMessageContext context)
{
...
myXML = this.getRawXML(context.getMessage());
}
//elsewhere in the application:
...
myService.doSomething(someRequest);
for (Handler h: ((BindingProvider)myService).getBinding().getHandlerChain())
{
if (h instanceof CustomSOAPHandler )
{
System.out.println("HandlerResult: "+ ((CustomSOAPHandler )h).getMyXML());
}
}
In very simple tests, this seems to work. But this solution feels somewhat like a cheap hack. I don't like setting the raw XML as a member of the chain handler, and I have a gut feeling this violates many other best practices. Does anyone have a more elegant way of doing this?
The two choices that seemed to work for me are both documented here. I didn't receive a response yet about whether using a ThreadLocal was fine or not, but I don't see why it shouldn't be.
My secoond method which was added to the original question was to go the route of the handler. While debugging the WS callout, I noticed that the invocationProperties map had the SOAP response as part of an internal packet structure within the responseContext object, but there appeared to be no way of getting to it. The ResponseContext was a set of name value pairs. However, when I read the source code for ResponseContext at this location, I saw that the code for the get method had a comment about returning null if it could not find an Application Scoped property, otherwise, it would read it from the packet invocationProperties, which seemed to be what I wanted. So I seached on how to set the scope on the key/value pair (Google: setting application-scope property for jaxws) that the context was introducing it low-and-behold, it was in the jax-ws spec that I referenced in the other thread.
I also did some reading about the Packet, https://jax-ws.java.net/nonav/jax-ws-20-fcs/arch/com/sun/xml/ws/api/message/Packet.html.
I hope this makes some sense for you. I was concerned that three wouldn't be anything to use JAXB against if the web service call resulted in a Soap FAULT, and I really wanted to log this packet, since it was being returned from a Payment Gateway which to this day has a number of undocumented results.
Good luck.
The solution was to use JAXB to convert the objects back to XML. I didn't really want to do this because it seems redundant to have the webservice client receive XML, convert it to a POJO, only to have that POJO converted back to XML, but it works.
Example of handler that passes out request / response message bodies:
public class MsgLogger implements SOAPHandler<SOAPMessageContext> {
public static String REQEST_BODY = "com.evil.request";
public static String RESPONSE_BODY = "com.evil.response";
#Override
public Set<QName> getHeaders() {
return null;
}
#Override
public boolean handleMessage(SOAPMessageContext context) {
SOAPMessage msg = context.getMessage();
Boolean beforeRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(32_000);
context.getMessage().writeTo(baos);
String key = beforeRequest ? REQEST_BODY : RESPONSE_BODY;
context.put(key, baos.toString("UTF-8"));
context.setScope(key, MessageContext.Scope.APPLICATION);
} catch (SOAPException | IOException e) { }
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) { }
}
To register handler and use preserved properties:
BindingProvider provider = (BindingProvider) port;
List<Handler> handlerChain = bindingProvider.getBinding().getHandlerChain();
handlerChain.add(new MsgLogger());
bindingProvider.getBinding().setHandlerChain(handlerChain);
Req req = ...;
Rsp rsp = port.serviceCall(req); // call WS Port
// Access saved message bodies:
Map<String, Object> responseContext = provider.getResponseContext();
String reqBody = (String) responseContext.get(MsgLogger.REQEST_BODY);
String rspBody = (String) responseContext.get(MsgLogger.RESPONSE_BODY);
TL;DR
Metro JAX WS RI docs says about MessageContext.Scope.APPLICATION property:
The message context object can also hold properties set by the client or provider. For instance, port proxy and dispatch objects both extend BindingProvider. A message context object can be obtained from both to represent the request or response context. Properties set in the request context can be read by the handlers, and the handlers may set properties on the message context objects passed to them. If these properties are set with the scope MessageContext.Scope.APPLICATION then they will be available in the response context to the client. On the server end, a context object is passed into the invoke method of a Provider.
metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/api/message/Packet.java contains property:
/**
* Lazily created set of handler-scope property names.
*
* <p>
* We expect that this is only used when handlers are present
* and they explicitly set some handler-scope values.
*
* #see #getHandlerScopePropertyNames(boolean)
*/
private Set<String> handlerScopePropertyNames;
On other hand metro-jax-ws/jaxws-ri/rt/src/main/java/com/sun/xml/ws/client/ResponseContext.java is an implementation of Map with:
public boolean containsKey(Object key) {
if(packet.supports(key))
return packet.containsKey(key); // strongly typed
if(packet.invocationProperties.containsKey(key))
// if handler-scope, hide it
return !packet.getHandlerScopePropertyNames(true).contains(key);
return false;
}
In SOAPHandler we can mark property as APPLICATION instead of default MessageContext.Scope.HANDLER:
/**
* Property scope. Properties scoped as <code>APPLICATION</code> are
* visible to handlers,
* client applications and service endpoints; properties scoped as
* <code>HANDLER</code>
* are only normally visible to handlers.
*/
public enum Scope {APPLICATION, HANDLER};
by:
/**
* Sets the scope of a property.
*
* #param name Name of the property associated with the
* <code>MessageContext</code>
* #param scope Desired scope of the property
* #throws java.lang.IllegalArgumentException if an illegal
* property name is specified
*/
public void setScope(String name, Scope scope);
As an alternative, instead of putting request/response details into the soap context, (in my case it did not work), you can put it into the ThreadLocal. So you need SOAPHandler, that #gavenkoa described (ty), but add it to the ThreadLocal instance, instead of the soap context.

How can I override the decisions made during JAX-RS Content Negotiation?

I'm using RESTEasy 2.2.1.GA as my JAX-RS implementation to create a client to connect to a third party service provider. (Education.com's REST API if it matters)
To make sure I haven't missed an important implementation detail here are code samples:
Service Interface
#Path("/")
public interface SchoolSearch {
#GET
#Produces({MediaType.APPLICATION_XML})
Collection<SchoolType> getSchoolsByZipCode(#QueryParam("postalcode") int postalCode);
}
Calling Class
public class SimpleSchoolSearch {
public static final String SITE_URL = "http://api.education.com/service/service.php?f=schoolSearch&key=****&sn=sf&v=4";
SchoolSearch service = ProxyFactory.create(SchoolSearch.class, SITE_URL);
public Collection<SchoolType> getSchools() throws Exception {
Collection<SchoolType> schools = new ArrayList<SchoolType>();
Collection<SchoolType> response = service.getSchoolsByZipCode(35803);
schools.addAll(response);
return schools;
}
}
After setting up tests to make this call, I execute and see the following exception being thrown.
org.jboss.resteasy.plugins.providers.jaxb.JAXBUnmarshalException: Unable to find JAXBContext for media type: text/html;charset="UTF-8"
From reading the RESTEasy/JAX-RS documentation, as I understand it, when the response is returned to the client, prior to the unmarshaling of the data, a determination is made (Content Negotiation??) about which mechanism to use for unmarshalling. (I think we're talking about a MessageBodyReader here but I'm unsure.) From looking at the body of the response, I see that what is returned is properly formatted XML, but the content negotiation (via HTTP header content-type is indeed text/html;charset ="UTF-8") is not allowing the text to be parsed by JAXB.
I think that the implementation is behaving correctly, and it is the service that is in error, however, I don't control the service, but would still like to consume it.
So that being said:
Am I correct in my understanding of why the exception is thrown?
How do I work around it?
Is there a simple one line annotation that can force JAXB to unmarshal the data, or will I need to implement a custom MessageBodyReader? (If that is even the correct class to implement).
Thanks!
Follow Up:
I just wanted to post the few changes I made to Eiden's answer. I created a ClientExecutionInterceptor using his code and the information available at Resteasy ClientExecutionInterceptor documentation. My final class looks like
#Provider
#ClientInterceptor
public class SimpleInterceptor implements ClientExecutionInterceptor {
#Override
public ClientResponse execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse response = ctx.proceed();
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
The big difference is the addition of the #Provider and #ClientExecutionInterceptor annotations. This should insure that the interceptor is properly registered.
Also, just for completeness, I registered the Interceptor slightly differently for my tests. I used:
providerFactory.registerProvider(SimpleInterceptor.class);
I'm sure there are several solutions to this problem, but I can only think of one.
Try so set the content-type using a ClientExecutionInterceptor:
public class Interceptor implements ClientExecutionInterceptor {
#Override
public ClientResponse<?> execute(ClientExecutionContext ctx) throws Exception {
final ClientResponse<?> response = ctx.proceed();
response
.getHeaders()
.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML);
return response;
}
}
public void getSchools() throws Exception {
ResteasyProviderFactory.getInstance()
.getClientExecutionInterceptorRegistry()
.register( new Interceptor() );
SchoolSearch service =
ProxyFactory.create(SchoolSearch.class, SITE_URL);
}
I dont know about any such annotation, others might do, but a workaround is to create a local proxy. Create a controller, that passes all parameters to education.com using a
java.Net.URL.get()
return the answer that you received, but modify the header. Then connect your client to the local proxy controller.

override http status with cxf

I'm using CXF for web services.
Because of some client restrictions, I need all web faults to return code 200 instead of 500.
I tried to use interceptors, depends on the phase I was able to either override the status and then the response is empty or the response is full with the fault but then the status is not overridden.
Any ideas how to do that?
Using interceptors, what would be the right phase?
I registered the interceptor like this:
#org.apache.cxf.interceptor.OutFaultInterceptors(interceptors = { "com.my.prod.core.service.itercept.HttpStatusInterceptor" })
and this is the interceptor:
public class HttpStatusInterceptor extends AbstractSoapInterceptor {
public HttpStatusInterceptor(){
super(Phase.POST_STREAM_ENDING);
}
#Override public void handleMessage(org.apache.cxf.binding.soap.SoapMessage msg) throws org.apache.cxf.interceptor.Fault{
msg.put(SoapMessage.RESPONSE_CODE, "200");
}}
Can you try
msg.put(SoapMessage.RESPONSE_CODE, 200);
so it ends up as and Integer object instead of a String. I think it's expecting the integer.

Categories

Resources