Unmarshal a REST response without using #XMLRootElement annotation - web-services

Ok, I've searched everywhere for an answer to this. It is driving me nuts.
All I need to do is unmarshal a very simple webservice response. The only problem is, I am using a generated source file without the #XmlRootElement annotation. I am unable to edit this generated source file to add #XmlRootElement, either. I need to use it "as is".
This is the current code that I have, but it is resulting in an error shown at the bottom of this post. I have tried to use a JAXBElement wrapper but to no avail. Could somebody please give me the code I need? I have no idea how to use "QName"s etc.
This code below works great with classes that have #XmlRootElement:
MyGeneratedClass response = restTemplate.getForObject("url to webservice!"),
MyGeneratedClass.class);
return response
Sadly, it is producing this error in this case. Please help me to unmarshal the REST response!
Could not extract response: no suitable HttpMessageConverter found for response
type [MyGeneratedClass] and content type [application/xml;version=1]

I forgot about posting this many months ago and I should probably follow it up with the solution.
This solution also adds a cookie to the request headers, but you can ignore that.
In the case that a generated source file does not have #XmlRootElement annotation, you can unmarshal as follows:
// Cookie setting
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Cookie", "myCookie=value");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);
HttpEntity<String> response = restTemplate.exchange("web service url"),
HttpMethod.GET, requestEntity, String.class);
// Unmarshalling
JAXBElement<MyGeneratedClass> result =
(JAXBElement<MyGeneratedClass>) unmarshaller.unmarshal(
new StreamSource(new ByteArrayInputStream(response.getBody().getBytes())));
return result.getValue();

Spring's RestTemplate relies on HttpMessageConverter to unmarshal an object to XML. More specifically the Jaxb2RootElementHttpMessageConverter.canWrite method is responsible for the error you are seeing. Even if you were to override the canWrite method to not care whether the XmlRootElement annotation was present, JAXB would be unable to unmarshal the object.
One way around this limitation is to override Jaxb2RootElementHttpMessageConverter.canWrite to not check for the presence of the XmlRootElement annotation AND use EclipseLink's Moxy JAXB implementation with a mapping file. In the mapping file you can specify the equivalent of the XmlRootElement annotation, allowing you to use JAXB without using annotation's in your Java class.

Spring's RestTemplate is typically used together with org.springframework.oxm.jaxb.Jaxb2Marshaller. Unfortunately, that class has a property supportJaxbElementClass set to false by default :(
Spring nowhere documents this property, and numerous questions about RestTemplate/ JAXBELement on the Spring Forums have been unanswered :((((
Fortunately, you can configure Jaxb2Marshaller setting its property supportJaxbElementClass to true!
The following example configuration of RestTemplate will correctly marshall and unmarshall objects of type JAXBElement
<beans>
<bean id="httpClient" class="org.apache.http.impl.client.DefaultHttpClient">
<constructor-arg>
<bean class="org.apache.http.impl.conn.PoolingClientConnectionManager" />
</constructor-arg>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg ref="httpClient" />
</bean>
</constructor-arg>
<!-- Configure the Rest template to translate between XML and JAXB -->
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="jaxbMarshaller" />
<property name="unmarshaller" ref="jaxbMarshaller" />
</bean>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="supportJaxbElementClass" value = "true"/>
<property name="packagesToScan">
<list>
<value>com.myorg.path.to.JAXB.classes</value>
</list>
</property>
</bean>
</beans>

Related

Hybris Custom WSDTO on hybris product data

Hi I had created eProductForm bean in the commerecefacades-beans.xml I added custom attribute of ProductData.
<bean class="de.hybris.platform.commercefacades.product.data.ProductData">
<property name="eProductForm" type="String"/>
</bean>
then in commercewebservice-beans.xml, I added the custom attribute of ProductWsDTO
<bean class="de.hybris.platform.commercewebservicescommons.dto.product.ProductWsDTO">
<property name="eProductForm" type="String"/></bean>
from SearchResultProductPopulator populated productdata of eProductForm from my search result.
target.setEProductForm(this.<String> getValue(source, "E_PRODUCT_FORM"));
PFB
dto mapping
<bean parent="fieldSetLevelMapping" id="productWsDTOFieldSetLevelMapping">
<property name="dtoClass" value="de.hybris.platform.commercewebservicescommons.dto.product.ProductWsDTO"/>
<property name="levelMapping">
<map>
<entry key="BASIC"
value="purchasable,stock,name,baseProduct,availableForPickup,code,url,price"/>
<entry key="DEFAULT"
value="summary,averageRating,purchasable,stock(DEFAULT),description,variantMatrix(DEFAULT),name,baseOptions(DEFAULT),baseProduct,availableForPickup,variantOptions(DEFAULT),code,url,price(DEFAULT),numberOfReviews,manufacturer,categories(BASIC),priceRange,multidimensional,configuratorType,configurable,tags"/>
<entry key="FULL"
value="summary,productReferences(FULL),classifications(FULL),averageRating,purchasable,volumePrices(FULL),variantType,stock(FULL),description,variantMatrix(FULL),name,baseOptions(FULL),baseProduct,availableForPickup,variantOptions(FULL),reviews(FULL),code,url,price(FULL),numberOfReviews,manufacturer,volumePricesFlag,futureStocks(FULL),images(FULL),categories(FULL),potentialPromotions(FULL),priceRange,multidimensional,configuratorType,configurable,tags,eProductForm,ePickledGroup"/>
</map>
</property>
</bean>
Below is the code I am calling Mapper.. While I debugged my code sourceresult is having product data of that custom attreibute. But I am not getting the eproductform in the WSDTO response.
final ProductSearchPageData<SearchStateData, ProductData> sourceResult = searchProducts(query, currentPage, pageSize, sort);
if (sourceResult instanceof ProductCategorySearchPageData)
{
return getDataMapper().map(sourceResult, ProductCategorySearchPageWsDTO.class, fields);
}
But in logs I see:
[EL Warning]: 2019-02-20 18:31:27.341--Ignoring attribute
[eProductForm] on class
[de.hybris.platform.commercewebservicescommons.dto.product.ProductWsDTO]
as no Property was generated for it.
As #Farrukh Chishti commented, the URL that you used probably used the DEFAULT level, which doesn't contain the attribute you added. For testing purposes, you can try to add the attribute to BASIC, DEFAULT, and FULL.
In the URL, you can specify the level, something like this:
https://localhost:9002/rest/v2/custom_site/stores?&fields=FULL

WSO2 ESB. Accessing Secure Vault programmaticaly

I am implementing handler for REST API in Java (org.apache.synapse.rest.Handler interface). And there is a case, when I need to access Secure Vault and get a value.
I know that you are able to achieve this by expression="wso2:vault-lookup('YOUR.KEY.HERE')" in sequence, but can't find api to do this in handler. I believe that org.apache.synapse.MessageContext can help, but not sure how.
You can use below code segment in the custom handler.
public String getSecretPassword(String alias, MessageContext messageContext){
RegistrySecretRepository regRepo = new RegistrySecretRepository();
regRepo.setSynCtx(messageContext);
return regRepo.getSecret(alias);
}
Dependency for pom.xml, the version needs to be changed according to your product version.
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.mediation.security</artifactId>
<version>4.2.0</version>
</dependency
Please refer - http://malantech.blogspot.com/2016/10/basic-authentication-handler-with.html
Thanks
I believe you will not be able to get the value of the security vault directly from your handler so I advise you to recover the password and put it in a property and inside your handler to retrieve the property.
<property name="passwordvault"
expression="wso2:vault-lookup('YOUR.KEY.HERE')"
scope="default"/>
And use the MessageContext to get the propertie like this:
context.getProperty("passwordvault");
That's just a workaround which is not advisable , i believe you can try below code as i have used similar earlier as well and it worked
<property expression="wso2:vault-lookup('ei.training.userid')" name="UserID" scope="default" type="STRING"/>
<log>
<property expression="wso2:vault-lookup('ei.training.userid')" name="UID"/>
</log>
And I will answer my own question.
I've created a dummy sequence and placed it into Registry
<sequence name="SecureVaultSeq" trace="disable" xmlns="http://ws.apache.org/ns/synapse">
<property expression="wso2:vault-lookup('MY.PASS')" name="NAME"
scope="default" type="STRING"
xmlns:ns="http://org.apache.synapse/xsd" xmlns:ns3="http://org.apache.synapse/xsd"/>
</sequence>
Then in my handler i retrieved it like this:
messageContext.getConfiguration().getSequence("conf:Resources/sequences/SecureVaultSeq.xml").mediate(messageContext);
key = (String) messageContext.getProperty("NAME");
Hope this will help someone.

WSO2 ESB Collout address from Property or XPath-expression

I have the config with development and production sections. This sections contain the URLs of backends. In my inSequence i need it to Callout to these backends several times per request.
<config>
<env>prod</env>
<backend env="prod">http://localhost:1234/</backend>
<backend env="dev">http://localhost:2345/</backend>
</config>
I read this config from Local Entry (as XML) and want to set Callout's URL as an Property.
I don't want to hardcode these backends inside my code with "Switch" statement, because it's possible to use more than two environments.
Could you please show me an example?
Thank you.
You can read xml file in registry. Simply define property of OM type like this:
<property name="test" expression="get-property('registry','conf:/test.xml')" scope="default" type="OM" />
Then you can see the value by logging like this:
<log level="custom"> <property name="test.b" expression="$ctx:test//b" /> </log>
And in the xml file that you have put in the root of registry, you would fill it like:
<a>Hello<b>WSO2</b></a>
I have learned it from this link.
I found the answer. According to source of Callout mediator:
CalloutMediator.java
It uses "To" header if URL is not specified.

Event builder XML mapping in WSO2CEP 3.0.0

Lets say, a stream named inputStream is defined with attributes name:string, surname:string, address:string. For this stream, if an event builder is defined like the following,
<property>
<from xpath="xpathForSurname"/>
<to default="NULL" name="surname" type="string"/>
</property>
<property>
<from xpath="xpathForName"/>
<to default="NULL" name="name" type="string"/>
</property>
<property>
<from xpath="xpathForAddress"/>
<to default="NULL" name="address" type="string"/>
</property>
When I send an input like ('John', 'Lennon', 'Liverpool') I expect inputStream to be ['John', 'Lennon', 'Liverpool'], but the result stream is ['Lennon', 'John', 'Liverpool']. The reason is that values of the attributes are added to stream following the mapping sequence in builder definition.
Therefore, <to> tags in definition becomes pointless (the value upon xpathForSurname evaluation is not mapped to surname but name). Is this a bug or is it done on purpose?
Yes, this seems to be a bug in CEP 3.0.0 and will be fixed in a future release. I have created a JIRA with the information you provided in CEP-640.
For now, the workaround would be to let the input stream come directly as it is via the event builder without reordering the attributes and doing any manipulations to the ordering at the level of execution plans. Hope this workaround will work for you.

How to pass parameters to XSLT from JBoss Actions Pipeline

Say I have a given action:
<service category="MyService" name="MyFirstService">
<actions mep="RequestResponse">
<action class="actions.CXFListenerAction" name="CXFServiceListener"/>
<action class="org.jboss.soa.esb.actions.transformation.xslt.XsltAction" name="Transform XML">
<property name="templateFile" value="/stylesheets/transform_response.xslt"/>
<property name="failOnWarning" value="true"/>
</action>
</actions>
I am trying to figure out how to add a property name or parameter that I could then access from within the XSLT. I've tried add additional property names,
<property name="param1" value="Hey!"/>
but I'm not 100% sure if this is correct for adding parameters accessible by the XSLT.
Thanks.
The properties defined for the XsltAction class are properties specific to that action class and are not related to parameters in the template file.
So in short, it's not possible to pass parameters to the xslt from the JBoss ESB action pipeline. However, it would be possible to create a custom action that decorates your ESB message with data you define as a property in your jboss-esb.xml file and insert that before your XsltAction. That may be what you're looking for.