I am trying to interact with a third party web service, who requires me to send a security token as a part of each request. The token is a node by itself, and I acquire it from the response of an initial call.
The web service endpoint is dotNet, and I have a Java client.
Apparently, the server side expects me to send the security token exactly like it was provided to me: literally the same string: so it won't do if its content has a different size, order, etc.
So, in SoapUI, everything works fine. There is a token in the response of the initial 'startSession' call, which I copy into the request of a next call.
But in Java (I tried JAX-WS and CXF generated code, both rely on JAXB) it doesn't work. I receive the token as an object after it is unmarshalled, and I use this object in the next call.
When marshalled and send, it is missing a namespace attribute in a subnode. The server side says it won't continue because the token is incorrect.
So, by using JAXB outbound logical handler functionality, I am able to add the missing namespace without any problems in the DOM source (I was also able to achieve this with a CXF interceptor).
The problem now is, that the attributes, when marshalled, are ordered in such a way that the result still not matches the provided token as it was before it was unmarshalled. Alhough it should not matter, the order of these attributes is crucial.
I have no idea how to solve this, unless it is possible to actually modify the output XML string. I even tried a dirty hack by removing all attributes from the subnode and replacing them with one attribute that visually looks the same; but then the outer two double quotes become single quotes...
I hope anyone has an idea. Because I have none.
Cheers.
UPDATE:
I should have mentioned that the attributes in question are namespace(d) attributes. The node should look like this:
<HawanedoSessionInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c">
However, after using outbound JAXB handler to add the missing xmlns="...", my result looks like this:
<HawanedoSessionInfo xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
In the HawanedoSessionInfo class, I used XmlType.proporder and #XmlAttribute like so:
#XmlType(name = "HawanedoSessionInfo", propOrder = {
"xsd",
"xsi",
"xmlns",
and some other non-attribute sub-elements..
private String xsd;
private String xsi;
private String xmlns;
#XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsd() {
return xsd;
}
public void setXsd(final String xsd) {
this.xsd = xsd;
}
#XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsi() {
return xsi;
}
public void setXsi(final String xsi) {
this.xsi = xsi;
}
#XmlAttribute
public String getXmlns() {
return xmlns;
}
public void setXmlns(final String xmlns) {
this.xmlns = xmlns;
}
So apparently the proporder option does not help in this case?
UPDATE 2:
Like I wrote in my answer, it now works. Based on this LINK,
in the HawanedoSessionInfo class I added:
#XmlCustomizer(HawanedoSessionInfoCustomizer.class)
I created the customizer class exactly as described in the linked page, and I added the jaxb.properties.
So I did two things:
1) I added my attributes to (the top of the already existing) propOrder attribute. I added the attributes as instance variables and created the getters/setters. I annotated the getters with XmlAttribute.
2) I implemented the XmlCustomizer solution.
Now comes the strange part. According to Fiddler, the order of the attributes is still not changed! But I must stress that this is now working, ONLY after implementing the Customizer. What is happening here? :)
So in principle you cannot control order of attributes in a standard way, but ....
Depending on jaxb /java version the order can be determined by alphabetical order of the names, the order of declaration.
You could try in your code if a) moving the fields around changes anything, b) renaming the fields (the XMLAttribute than have to map to original name).
If you are lucky, it will work. But of course it is a hack and will work till next jaxb/java update.
The JAXB providers (the actuall implementation can have extra features), that can be used to customized the marshalling process). For example I found that: https://community.oracle.com/thread/977397 abut eclipselink.
I am sure there was a way of intercepting the soap body before it is send or governing the data serialization before it is send. I can think how it was called but try to google the jaxws client customization. If you capture the whole soap message simple xslt transforamation could fix the attributes order.
I feel your pain. The whole point of using xml, jaxws and such is to make our life easier and then someone providers decide not to follow standards and you end up with a mess that you were trying to clean for few days. Good luck and maybe try to contact xml gurus from Eclipse Moxy
I am so happy right now, because I got it working and it only cost me a full week to do so...:) With help of #Zielu, I was pointed to this link with the EclipseLink XMLCustomizer solution as suggested by Blaise Doughan: XMLCustomizer solution
I took the code in my original question (underneath 'UPDATE') and added the exact solution as suggested. Not sure if it is all necessary, but it works. Thanks guys.
It's possible you can control the order by using,
#XmlType (propOrder={"prop1","prop2",..."propN"})
Related
I have the following method that accepts xml and I do some data feeding with the content.
I am supposed to return a SOAP message as well, something along these lines:
<ow-e:Envelope revision='2.0' xmlns:ow-e='http://www.url.com/test-envelope'>
<ow-e:Header>
<ow-e:Properties>
<ow-e:SentAt>2004-12-14T13:54:36</ow-e:SentAt>
<ow-e:Topic>SOME_STRING</ow-e:Topic>
</ow-e:Properties>
</ow-e:Header>
</ow-e:Envelope>
So right now what I am doing is the following:
String some_string = "qwe";
String response = "";
response = "<ow-e:Envelope revision='2.0' xmlns:ow-e='http://www.url.com/test-envelope'><ow-e:Header><ow-e:Properties><ow-e:SentAt>2004-12-14T13:54:36</ow-e:SentAt><ow-e:Topic>" + some_string + "</ow-e:Topic></ow-e:Properties></ow-e:Header></ow-e:Envelope>";
return response;
Which is absolutely terrible. Any idea how I can actually make it more bearable? Using a framework is not an option at the moment.
This is the first time I am dealing with SOAP messages/responses and it feels like hell coming from REST. I probably need to create some kind of hierarchy to populate the values correctly, but I am not sure how it can be done just by using Java without any frameworks.
You mentioned using frameworks is not an option, but something more lightweight may be available in your platform:
JAXB. JAXB allows you to map Java classes to XML representations using annotations. It's far better than doing marshaling and unmarshaling by hand or by concatenating or parsing strings. With properly structured and annotated POJOs, JAXB can handle things for you. You might even be able to cheat and use xjc with your WSDL file to create annotated classes with the -wsdl option (experimental though).
SAAJ. Bluntly put, SAAJ is just like a specific builder and parser for SOAP messages. It will handle the structure and namespaces for you. Speaking of which...
... the example you are showing isn't really valid SOAP message. SOAP is a protocol. You need to properly format it and use the right namespaces otherwise you are just returning some XML messages that look like SOAP, but aren't.
I'm trying to set up my website to allow location additions to the urls.
EG: mysite.com/us/ca/sanfrancisco/home
While also still allowing mysite.com/home and everything in between.
Spring boot parent so you know what version of spring I'm using:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
If there's another piece of versioning you need, let me know.
I get that I can add in regex variables to the request mapping, but how would I go about persisting those urls across more requests?
So right now for an example, the testing error page I have:
#RequestMapping({"/error/501", "/{state:[a-z]{2}}/error/501", "/{state:[a-z]{2}}/{city:[a-z]+}/error/501"})
public ModelAndView testingLocations(ModelMap model, #PathVariable(value="state", required = false) String state,
#PathVariable(value="city", required=false) String city){
getLogger().info("State: {}", state);
model.addAttribute("stateTest",state+":"+city);
model.addAttribute("view", "error");
return new ModelAndView("error/501", model);
}
But when I'm on my testing page, and I click the home button, it takes me back to mysite.com/home
So My Questions
Is there a way for me to persist it so that if they're currently on a location based url, it will apply that base to the future navigations? (unless they manually enter the url to not include them)
Then as a follow-up, is there a way for me to globally apply these request variables without requiring me to add the #PathVariable to every request mapping method? I get that I can just add the request mapping variable strings themselves to the controller class, so that I don't need those on every method. But is there a way for me to utilize those without needing the #PathVariable annotations?
Finally, is there a way for me to make this not as hardcoded, like a way for me to say /{*location}/error to cover as deep as the locations will allow? While still having the verification on the location formatting, so verifying that 1 we support the locations given, 2 the format is correct (/ca/sanfrancisco vs /anything/anything
The last one I can live with, if I need to have the /state/city/municipality/actualtarget
As far as verifying that we support the locations given, I understand that's on my end, which I'll probably just have a small database to keep track of where we do and do not support for the given variables.
Is there a best practice for building this system? I tried to find something on this, but googling "spring boot location url" is not the best at giving me what I need, since "location" can apply to a pretty wide range of topics. I've gotten to where I am from searching, but I can't seem to pin down these last few steps.
Any help/advice/suggestions is appreciated. If upgrading versions is required, I'm not sure how viable that is at the moment, I'd have to look into it. Preferably I'd like the solution to be able to be done on the current spring version I'm running.
The best way here is:
#RequestMapping("/some/{foo}/{baz}")
public String hi(CompositeObject compositeObject) {
return "hi";
}
#Data
public class CompositeObject {
private String foo;
private String baz;
}
Spring provides functionality for request path and request parameters to collect it into a composite object. It doesn' work either with body or headers.
If you have something optional like state, then just keep it null at the controller and handle later
I am creating Client API in Java using :+ Apache Jena FrameWork+ Hydra(for Hypermedia driven) + my private vocab similar to Markus Lanther Event-API Vocab instead of schema.org(for Ontology/Vocabulary part)
Section 1 :
After looking this Markus Lanther EventDemo repo and hydra-java.I found that they are creating classes for each hydra:Class that can break client in future .For example :
A Person class (Person.java)
public class Person
{
String name;
};
But in future requirement name is also a class eg:
public class Name
{
String firstName;
String LastName;
};
So to fulfill this requirement I have to update Person class like this:
public class Person
{
Name name;
};
Question 1:
Is my understanding correct or not of this Section? If yes then what is the way to deal with this part ?
Section 2:
To avoid above problem I created a GenericResource class(GenericResource.java)
public class GenericResource
{
private Model model;
public void addProperty(String propertyName,Object propertyValue)
{
propertyName = "myvocab:"+propertyName;
//Because he will pass propertyName only eg: "name" and I will map it to "myvocab:name"
//Some logic to add propertyName and propertyValue to model
}
public GenericResource retriveProperty(String propertyName)
{
propertyName = "myvocab:"+propertyName;
//Some logic to query and retrieve propertyName data from this Object add it to new GenericResource Object and return
}
public GenericResouce performAction(String actionName,String postData)
{
//Some logic to make http call give response in return
}
}
But again I stuck in lots of problem :
Problem 1: It is not necessary that every propertyName is mapped to myvocab:propertyName. Some may be mapped to some other vocab eg: hydra:propertyName, schema:propertyName, rdfs:propertyName, newVocab:propertyName, etc.
Problem 2: How to validate whether this propertyName belongs to this class ?
Suggestion: Put type field/variable in GenericResource class.And then check supportedProperty in vocab corresponding to that class.To more clarity assume above Person class which is also defined in vocab and having supportedProperty : [name,age,etc] .So my GenericResource have type "Person" and at time of addProperty or some other operation , I will query through vocab for that property is in supportedProperty list or in supportedOperation list in case of performAction().
Is it correct way ? Any other suggestion will be most welcomed?
Question 1: Is my understanding correct or not of this Section? If yes
then what is the way to deal with this part ?
Yes, that seems to be correct. Just because hydra-java decided to creates classes doesn't mean you have to do the same in your implementation though. I would rather write a mapper and annotate an internal class that can then stay stable (you need to update the mapping instead). Your GenericResource approach also looks good btw.
Problem 1: It is not necessary that every propertyName is mapped to
myvocab:propertyName. Some may be mapped to some other vocab eg:
hydra:propertyName, schema:propertyName, rdfs:propertyName,
newVocab:propertyName, etc.
Why don't you store and access the properties with full URLs, i.e., including the vocab? You can of course implement some convenience methods to simplify the work with your vocab.
Problem 2: How to validate whether this propertyName belongs to this
class
Suggestion: Put type field/variable in GenericResource class
JSON-LD's #type in node objects (not in #value objects) corresponds to rdf:type. So simply add it as every other property.
And then check supportedProperty in vocab corresponding to that class.
Please keep in mind that supportedProperty only tells you which properties are known to be supported. It doesn't tell you which aren't. In other words, it is valid to have properties other than the ones listed as supportedProperty on an object/resource.
Ad Q1:
For the flexibility you want, the client has to be prepared for semantic and structural changes.
In HTML that is possible. The server can change the structure of an html form in the way outlined by you, by having a firstName and lastName field rather than just a name field. The client does not break, rather it adjusts its UI, following the new semantics. The trick is that the UI is generated, not fixed.
A client which tries to unmarshal the incoming message into a fixed representation, such as a Java bean, is out of luck, and I do not think there is any solution how you could deserialize into a Java bean and survive a change like yours.
If you do not try to deserialize, but stick to reading and processing the incoming message into a more flexible representation, then you can achieve the kind of evolvability you're after. The client must be able to handle the flexible representation accordingly. It could generate UIs rather than binding data to fixed markup, which means, it makes no assumptions about the semantics and structure of the data. If the client absolutely has to know what a data element means, then the server cannot change the related semantics, it can only add new items with the new semantics while keeping the old ones around.
If there were a way how a server could hand out a new structure with a code-on-demand adapter for existing clients, then the server would gain a lot of evolvability. But I am not aware of any such solutions yet.
Ad Q2:
If your goal is to read an incoming json-ld response into a Jena Model on the client side, please see https://jena.apache.org/documentation/io/rdf-input.html
Model model = ModelFactory.createDefaultModel() ;
String base = null;
model.read(inputStream, base, "JSON-LD");
Thus your client will not break in the sense that it cannot read the incoming response. I think that is what your GenericResource achieves, too. But you could use Jena directly on the client side. Basically, you would avoid unmarshalling into a fixed type.
I'm having a problem where my C# client can't parse the data from my webservice in Glassfish.
I have a WSDL and XSD for my webservices as follows:
http://www.consorciovivedigital.com:8080/ServicioInterventoria/ServicioInterventoria?WSDL
http://www.consorciovivedigital.com:8080/ServicioInterventoria/ServicioInterventoria?xsd=1
And I'm using the next C# client to test this webservice:
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using ServicioInterventoria;
public partial class _Default : System.Web.UI.Page
{
ServicioInterventoria.ServicioInterventoria proxy;
protected void Page_Load(object sender, EventArgs e)
{
proxy = new ServicioInterventoria.ServicioInterventoria();
ResultadoMensualIC[] res = proxy.ObtenerResultadosMensuales("Intv12", "2014-07-07T08:08:08");
System.Diagnostics.Debug.WriteLine(res.Length);
System.Diagnostics.Debug.WriteLine(res[0].FechaCorte);
}
}
The problem is that when I execute this code, the res array has the amount of objects that should have, but each value of each object has the default value instead of the correct value. I used Fiddler to check the traffic and it receives the correct SOAP response with the correct data, but it seems that my C# client doesn't know how to parse the data.
I checked with a Java client, and I can get the correct data without any problems, and seems that my C# it's the only one giving problems with this.
Maybe there is a problem with the targetNamespace in the SOAP response, but I don't understand why works correctly in Java but in C# just puts default values.
Anyone have any idea what could be the problem?
If someone needs more information about it, let me know
Thanks beforehand
I solved last week. The problem was that the SOAP response didn't put the namespace for each attribute, then, the C# client doesn't know how to match this (seems like a limitation of C#). And, the other problem was the order of the SOAP response, because, the C# client was expecting each object as was defined in the WSDL, but the response it's ordered alphabetically, in this way, the C# client, doesn't match correctly each attribute.
I did some modifications to the client. First, in each model, for each attribute I added the namespace, something like this:
#XmlElement(name = "IdInterventor", namespace = "http://ws.bigdatasolutions.co/")
public String getIdInterventor() {
return IdInterventor;
}
With this, the SOAP response always puts the namespace for each attribute, which was neccesary for the C# client.
After that, at the beginning of each model class I add this tag, to define the order as defined in the WSDL and expected for the C# client.
#XmlRootElement(name = "AspectosFinancieros")
#XmlType(propOrder={"idInterventor", "numeroContrato", "ano", "valorContratoOperador", "fechaFirmaContrato",
"valorAdicion", "fechaProrrogaAdicion", "valorDesembolso", "fechaPagoDesembolso",
"valorAnticipo", "fechaAnticipo", "valorUtilizacion", "numeroActaAprobacion",
"fechaUtilizacion", "valorRendimiento", "fechaRendimiento", "numeroComprobanteRendimiento",
"valorComision", "fechaComision", "valorGastosAdministrativos", "fechaGastosAdministrativos",
"nombreFiducia", "numeroContratoFiducia", "fechaContratoFiducia", "fechaProrrogaAdicionFiducia",
"marcaTiempo"})
public class AspectosFinancieros {
I checked the expected order in the auto generated class in the C# client.
I hope someone find this useful.
I'm very new at XML and I'm having a problem I'm not able to solve and after looking around for hours I've decided to post my problem.
I'm using #XmlIDREF to just have the XmlID of some java classes on the XML doc.
All works fine, but when JAXB looks for the ID depeen on the tree hierarchy it seems that it couldn't find it and marhsalls the whole object again.
I'll show you the (simplified) model, it's all about optical routers, java classes represent the router and some components:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class OpticalRouter {
// #XmlID inherited from upper class
private List<FiberConnection> fiberConnections = new ArrayList<FiberConnection>();
}
That's a fiber connection:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class FiberConnection {
#XmlID
private String instanceID;
#XmlIDREF
Card card;
#XmlIDREF
Port port;
#XmlIDREF
Channel channel;
}
And finally:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({
DropCard.class,
AddCard.class
})
public class Card{
// #XmlID inherited from supper class
}
All works fine when I marshall ports and channels due they don't have subclasses.... but when it's time to marshall the cards if it's an AddCard or a DropCard it marshalls it another time instead of using the IDREF.
It seems that JAXB doesn't find them on the hierarchy...
If I change the original "Card card;" of FiberConnection for an "AddCard" for example it works too (JAXB finds the IdREF and doesn't marshall it again).
Hope I have explained that clearly.
Ask if not, I'll be glad to answer :)
Thanks in advance!
EDITED
Ok, I've come back with new info and results to explain myself better.
Due it's a huge class model and I don't want to make the post to much difficult to read I have created a kind of UML class diagram to make it much easier to read with some important info that should help (XML annotations and parameters). I have also included #XmlElement tags as Blaise advised me (thanks again).
Here you can find the yEd UML archive: yEd file
And there an UML jpg if you don't have/want to download graph
editor: jpg file
I also include (to finish) a part of the XML to better see what's happening.
Here I have a fiberConnection as the above mentioned.
I have a scCard and a fiberChannelPlan that have already been marshalled before on the XML doc (checked) but they are being marshalled again...
In exchange, srcPort, srcChannel and fiberChannels, also marshalled before, have only their ID's.
<OpticalRouter>
<fiberConnections>
<instanceID>FiberConnection#29e83b01</instanceID>
<srcCard xsi:type="DropCard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<instanceID>DropCard#6b02b23d</instanceID>
<type>11</type>
<subtype>1</subtype>
<chasis>0</chasis>
<fiberChannelPlan xsi:type="ChannelPlan">
<instanceID>ChannelPlan#7e246b6d</instanceID>
<firstChannel>0</firstChannel>
<lastChannel>0</lastChannel>
<maxFreq>196.1</maxFreq>
<minFreq>191.6</minFreq>
<fiberChannels>Channel/360</fiberChannels>
<fiberChannels>Channel/368</fiberChannels>
<fiberChannels>Channel/376</fiberChannels>
<fiberChannels>Channel/384</fiberChannels>
</fiberChannelPlan>
<cardExpressPort>Port#4f781d1d</cardExpressPort>
<carCommonPort>Port#56bf83ad</carCommonPort>
</srcCard>
<srcPort>Port#56bf83ad</srcPort>
<srcChannel>Channel/184</srcChannel>
</fiberConnections>
</OpticalRouter>
I think that the problem is something related to that post I found (even if I'm using linux and java 1.6) or other posts I have seen here on StarckOverflow:
java.net/jira/browse/JAXB-870
Thanks in advance!
P.S: all code I'm using is opensource and can be downloaded from the main source at a git repository if someone thinks it should be easier for him.
You need to make sure that each object in your graph is referenced through a containment/nesting relationship (such as #XmlElement), this forms the XML. I don't see where this requirement is met in your model. Then you can use #XmlID/#XmlIDREF to have key based relationships within the tree to turn it into a graph.
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html