I'm using CXF/JAXB to autogenerate java classes from wsdl.
All of the generated requests extend BaseRequest, which can take the user credentials required for the soap auth:
CityRequest req = new CityRequest();
req.setUsername("admin");
req.setPassword("test");
req.setPostalCode("1234");
new WsService().getCityPort().getTown(req);
Now I'd like to somehow "intercept" the outgoing Request, and automatically add the required credentials. So that in my client implementation I don't have to care about setting the authentication stuff. Is it possible with CXF?
Then credentials are not provided as a header, but just as normal xml fields.
You can use a CXF Interceptor for that, as long as it executes before the object is marshalled in the MARSHAL phase. You should be able to choose any phase before that one. See the complete list in the CXF Interceptors documentation.
In the Interceptor, you can find your outgoing object with something like the following code:
#Override
public void handleMessage(Message message) throws Fault {
List mcl = message.getContent(List.class);
if (mcl != null) {
for (Object o : mcl) {
if (o instanceof BaseRequest) {
BaseRequest baseRequest = (BaseRequest) o;
}
}
}
}
Related
i am working on android app , I often get HTTP error 500 when i try to access the url due to bad connectivity which causes my app to fail . Response returned from URL is in JSON format so in order to Parse this json i used jackson api
JsonNode monthlyUrlNode = objectMapper.readValue(url, JsonNode.class);
In case of failure i want to reconnect to url with a delay of 30 seconds
i referred this Retry a connection on timeout in Java , but it is not of much use to me
Have you thought of using a Proxy object? Proxy let's you wrap an interface in a way that you can perform the same intervention independent of the method being called. Let's assume you've already created a client object for accessing interface SomeService. Then you can create a proxy class with a 30-second retry built in:
public class ServiceProxy implements InvocationHandler {
private final SomeService realClient;
public ServiceProxy(SomeService realClientObject) {
this.realClient = realClientObject;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(realClient, args);
if (result instanceof URL) {
JsonNode urlNode = objectMapper.readValue(url, JsonNode.class);
if (some condition on urlNode) {
// Wait and retry
Thread.sleep(30000);
result = method.invoke(realClient, args);
}
}
return result;
}
}
You create the proxy object by passing it the original interface, like:
public class ProxyFactory {
public static SomeService get(SomeService originalClient) {
return (SomeService)Proxy.newProxyInstance(SomeService.class.getClassLoader(),
new Class[]{SomeService.class},
new ServiceProxy(originalClient));
}
}
If you need this sort of fine control, do not pass a URL to Jackson. Use an appropriate HTTP library to read the content from the URL into memory, retrying as needed, and then feed it to Jackson.
I have a SessionListener on a CometD server. I want to pass data from a client to the server when the listener's sessionAdded() method is called.
The sessionAdded() method receives a ServerSession and ServerMessage object. ServerSession has an Attribute map that always seems to have nothing in it.
I would like to get some unique client data to the server. This data should be accessed by the server when the sessionAdded() method is invoked.
The documentation talks about basic use of a SessionListener, but says nothing about attributes. All the javadocs for client and server say about it is to describe how setAttribute() sets an attribute and how getAttribute() gets it.
Is there a way to do this? Can the ServerSession's attribute map be used to transfer attributes from the client to the server, and if so, how?
Someone please advise...
The ServerSession attributes map is a map that lives on the server.
It is an opaque (from the CometD point of view) map that applications can populate with whatever they need.
If you want to send data from a client to the server, you can just put this additional data into the handshake message, and then retrieve it from the message when BayeuxServer.SessionListener.sessionAdded() is called.
The client looks like this:
BayeuxClient client = ...;
Map<String, Object> extraFields = new HashMap<>();
Map<String, Object> ext = new HashMap<>();
extraFields.put(Message.EXT_FIELD, ext);
Map<String, Object> extraData = new HashMap<>();
ext.put("com.acme", extraData);
client.handshake(extraFields);
extraData.put("token", "foobar");
This creates an extra data structure that in JSON looks like this:
{
"ext": {
"com.acme": {
"token": "foobar"
}
}
}
It is always a very good practice to put your data under a namespace such as com.acme, so that you don't mess up with CometD fields, nor with other extensions that you may use.
Put your fields inside extraData, like for example field token in the example above.
Then, on the server:
public class MySessionListener implements BayeuxServer.SessionListener {
#Override
public void sessionAdded(ServerSession session, ServerMessage message) {
Map<String, Object> ext = message.getExt();
if (ext != null) {
Map<String, Object> extra = (Map<String, Object>)ext.get("com.acme");
if (extra != null) {
String token = (String)extra.get("token");
session.setAttribute("token", token);
}
}
}
#Override
public void sessionRemoved(ServerSession session, boolean timedout) {
}
}
This listener puts into the session attributes data that has been sent by the client, in the example above the token field.
Then, elsewhere in the application, you can access the session attributes and use that data.
I call a web-service which can periodically add some element in their contract.
Example: the SOAP response body contains:
<picture>
<active>true</active>
<code>172</code>
<label>Nikon D3200 Black</label>
<downloadEnabled>true</downloadEnabled>
</picture>
<picture>
<active>false</active>
<code>177</code>
<label>Nikon D5200 Red</label>
<downloadEnabled>true</downloadEnabled>
<extraField>none</extraField>
</picture>
and my CXF generated JAXB Bean looks like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "pictureListJAXB", propOrder = {
"active",
"code",
"label",
"downloadEnabled",
"extraField"
})
public class PictureListJAXB {
protected boolean active;
protected String code;
protected String label;
protected boolean downloadEnabled;
#XmlElement(nillable = true)
protected String extraField
// And Getters / Setters comes here after
}
The JAXB beans are generated with the maven plugin cxf-codegen-plugin version 2.6.2 (from apache.cxf).
Now I want to know if there is a solution to make my Bean to be fault tolerant in case a new element appears in the SOAP response:
<picture>
<active>true</active>
<code>172</code>
<label>Nikon D3200 Black</label>
<downloadEnabled>true</downloadEnabled>
<newUnmappedElement>anything irrelevant</newUnmappedElement>
</picture>
For now, when I recieve such response, I got an Unmarshalling Error because this new element.
My JAXB contains the minimal fields I want to manage but I want the bean to be able to cope with new element and just ignore them.
Is there any way to do it without regenerating the JAXB Bean?
(As now I have to regenerate the Beans and release my project)
I checked the CXF options (and xjc) and found nothing in the doc (and google). The unmarshalling operation is automatically done in the ReferentialService also generated by CXF, then an option to modify the generation of this part would be sufficient.
Here is the code to call the web-service using the CXF generated classes:
public ReferentialService getReferentialService(String resource, String auth) throws RuntimeException {
// These two classes are generated by CXF
Referential referential;
ReferentialService referentialService;
// Get the resource URL in form http://myserver:port/remote-backend/resource?wsdl
referential = new Referential(new URL(MyConfigUtil.getWSDL(resource)));
referentialService = referential.getReferentialServicePort();
BindingProvider bp = (BindingProvider) referentialService;
// Get the endpoint URL in form http://myserver:port/remote-backend/resource
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, MyConfigUtil.getWebServiceEndPoint(resource));
// Add SOAP credentials
String username = HttpBasicAuthHelper.getUsername(auth);
String password = HttpBasicAuthHelper.getPassword(auth);
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
return referentialService;
}
and the call:
// Throws Exception just for the sample code
public InputStream listPictures(QueryDTO request, String resource, String auth) throws Exception {
InputStream is = null;
if (request != null) {
// This is the WS Call which failed with unmarshal error
List<PictureListJAXB> result = getReferentialService(resource, auth).getPictures(request);
// Some code to convert JAXB into a XML stream
is = convertObjectToXmlStream(result);
}
return is;
}
UPDATE:
I saw this post and my feeling was the same: JAXB will just ignore the unmapped elements if used alone without CXF. By using the cxf-codegen-plugin, it is not the case.
I finally found the answer by looking to this another post but as I do not use the declarative way like in the post, it makes me guessed I should be able to add some properties on the binding provider.
My modification to my code is to add these properties in the BindingProvider like this in the getReferentialService method:
bp.getRequestContext().put("schema-validation-enabled", "true");
bp.getRequestContext().put("jaxb-validation-event-handler", new ValidatorHandler());
and for the test, I just created an inner class ValidatorHandler:
private class ValidatorHandler extends DefaultValidationEventHandler {
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == ValidationEvent.WARNING) {
return super.handleEvent(event);
} else if (event.getSeverity() == ValidationEvent.ERROR
&& event.getMessage().startsWith("unexpected element")) {
return true;
} else {
throw new RuntimeException(event.getMessage()
+ " [line:" + event.getLocator().getLineNumber() + "]");
}
}
}
By doing this, I do not need to modify my generated beans (JAX-B) and use them as they were generated by default.
One way to solve this issue is to use Jaxb annotation #XmlAnyElement(lax = true) .This means that for that field if an element is associated with a class via #XmlRootElement or #XmlElementDecl then an instance of the corresponding class will be used to populate the field if not the element will be set as an instance of org.w3c.dom.Element.A sample code
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
..
....
#XmlAnyElement(lax = true)
protected List<Object> any;
Any elements in the input that do not correspond to explicit properties of the class will be swept up into this list.Checkout more enter link description here
Your answer was helpful in my research. Thanks.
I had the same issue of unknown elements in the SOAP response specifically
ValidationEvent.FATAL_ERROR "cvc-complex-type.2.4.a: Invalid content"
I was able to add the following
bp.getRequestContext().put("set-jaxb-validation-event-handler", false);
This will turn off the just JAXB validation and quoting from Danial Kulp CXF commiter "For the most part, that is just things like unknown elements or elements in wrong namespaces and such."
I make a call from my frontend to the userPrivateProfile controller.The route is /api/user/private/:id so let's say I make a call at /api/user/private/65. Before I excecute the controller the request is intecepted by SecurityAuthAction where I make sure that the request headers have the token and if that's the case I want to change the :id to something different.
Controller.java
#With(SecurityAuthAction.class)
public Result userPrivateProfile(Long id) {
//LOGIC
}
SecurityAuthAction.java
public Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
String[] authTokenHeaderValues = ctx.request().headers()
.get(AUTH_TOKEN_HEADER);
if ((authTokenHeaderValues != null) && (authTokenHeaderValues.length == 1) && (authTokenHeaderValues[0] != null)) {
Long userId = sessionService
.findUserByToken(authTokenHeaderValues[0]);
ctx.args.put("id",userId.toString());
return delegate.call(ctx);
}
My problems are
that I cannot retrieve the :id specified from the original call using ctx
Since I cannot find where the request parameter is I cannot change it as well
I tried iterating through the ctx.args Map but I didn't find something there.The output is:
ROUTE_VERB ROUTE_
ACTION_METHOD
ROUTE_CONTROLLER
ROUTE_COMMENTS
ROUTE_PATTERN
GET
userPrivateProfile
controllers.Controller
/api/user/private/$id<[^/]+>
Thanx for your help :)
Unfortunately the Play Framework (certainly in version 2.1) does not give you easy access to URL query parameters when performing action composition. This discussion on the Play Google group may be of interest to you. One workaround mentioned there is to parse the URL in SecurityAuthAction to get the value of the id query parameter. However this is a little messy, and doesn't help you with the next part of your problem, which is changing the id before it gets to the downstream action.
Changing the details of the request as it's being handled by the server seems uncommon and wrong to me. Typically if you wanted to change what a client is requesting, you'd issue a HTTP 303 response redirecting them to the URL you want them to go to. But this doesn't feel like a situation for redirection. What I reckon you should do is just push your call to sessionService down to your main controller class:
SecurityAuthAction.java
public Promise<SimpleResult> call(Http.Context ctx) throws Throwable {
if (authorisation token header is present in request) {
return delegate.call(ctx);
}
return unauthorised();
}
Controller.java
#With(SecurityAuthAction.class)
public Result userPrivateProfile(Long id) {
// We've already established that an auth token header is present in the request
final String authToken = ctx.request().headers().get(AUTH_TOKEN_HEADER)[0];
final Long userId = sessionService.findUserByToken(authToken);
// TODO: Proceed...
}
If userId is something that you need all over your application, then it might be a candidate for inclusion in your application's cookie.
I've searched on this and found a few near misses. I've created a java client to consume a web service using JAX-WS. Is there a way when using JAX to set the HTTP_USER_AGENT value? I would like to have my web service log when specific clients (mine) access it so I wanted a customized value.
I've seen options where you set it in the system properties but this doesn't seem to work. The generated JAX classes don't seem to have a direct reference to the connection object so I don't see how I can manipulate those classes.
Any help would be great.
Thanks
ST
The solution to this kind of problem in JAX-WS is to implement a SoapMessage Handler (Interface: SOAPHandler< SOAPMessageContext >).
Within that handler you insert your HTTP header into maybe already existing headers, then you give control to the next handler in the handler chain.
The concept of this handler chain is kind of nice, you can have small classes for a very specific purpose (Security, Logging etc.).
In your client you configure the handler chain prior to sending any request:
// HandlerChain installieren
Binding binding = ((BindingProvider) port).getBinding();
List hchain = binding.getHandlerChain();
if (hchain == null) {
hchain = new ArrayList();
}
hchain.add(new HTTPUserAgentHandler());
binding.setHandlerChain(hchain);
And here is the code for the HTTPUserAgentHandler:
public class HTTPUserAgentHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
boolean request = ((Boolean) context.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY)).booleanValue();
if (request) {
#SuppressWarnings("unchecked")
Map<String, List<String>> headers = (Map<String, List<String>>) context
.get(MessageContext.HTTP_REQUEST_HEADERS);
if (null == headers) {
headers = new HashMap<String, List<String>>();
}
headers.put("HTTP_USER_AGENT", Collections.singletonList("user_agent"));
context.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
}
return true;
}
#Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
#Override
public void close(MessageContext context) {}
#Override
public Set<QName> getHeaders() {
return null;
}
}
Let me question the idea of having HTTP header first.
A more correct (WS-centric) approach is to set SOAP Header, not HTTP header. Consider this: SOAP messages can be delivered not only by HTTP, but by JMS, SMTP or custom transports. By requiring to have user-agent HTTP Header, you unnecessary tie you code to only one transport, albeit currently prevailing.
This is the reason BTW why JAX-WS have no notion of HTTP headers except in handlers.
And (of course) StackOverlow knows how to create SOAP headers.
not sure if this is the best/most direct way to do it, but i think you could add a custom javax.xml.ws.handler.Handler to the handler chain in the dispatch javax.xml.ws.Binding. in the Handler, you should be able to set a custom map of extra http headers on the outgoing MessageContext using the MessageContext.HTTP_REQUEST_HEADERS property.