Trying to consume a webservice using spring integration ws, On the webservice end i get a null pointer as it seems the object passed isnt been marshalled or not mapped in the xml, Below is the snippet of the client calling invoking the service .
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext("springws.xml");
MessageChannel channel = context.getBean("request", MessageChannel.class);
String body = "<getPojo xmlns=\"http://johnson4u.com/\"><pojo>\n"
+ " <pojoId>23</pojoId>\n"
+ " <pojoName>dubic</pojoName>\n"
+ "</pojo></getPojo>";
MessagingTemplate messagingTemplate = new MessagingTemplate();
Message<?> message = messagingTemplate.sendAndReceive(
channel, MessageBuilder.withPayload(body).build());
System.out.println(message.getPayload());
}
The WSDL is genrated by the JAxWS endpoint class
package com.johnson4u;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
#WebService(serviceName = "SpringService")
public class SpringService {
#WebMethod(operationName = "getPojo" )
public Pojo getPojo(#WebParam(Pojo pjRequest){
//Null Pointer occurs here as pjRequest might not be mapped to xml
System.out.println("Pojo name is "+pjRequest.getPojoName());
return new Pojo(234,"IM new Pojo");
}
And the POJO
package com.johnson4u;
public class Pojo {
private int pojoId;
private String pojoName;
public Pojo(int pojoId, String pojoName) {
this.pojoId = pojoId;
this.pojoName = pojoName;
}
public int getPojoId() {
return pojoId;
}
public void setPojoId(int pojoId) {
this.pojoId = pojoId;
}
public String getPojoName() {
return pojoName;
}
public void setPojoName(String pojoName) {
this.pojoName = pojoName;
}
Unfortunately,stackoverflow cant format the wsdl properly, but the namespace id are based on the package name com.johnson4u, below is spring-ws-context.xml
<int:channel id="request" />
<int:channel id="response" />
<ws:outbound-gateway id="gateway"
request-channel="request"
uri="http://localhost:20151/SpringWs/SpringService?wsdl"/>
i believe the string body should be
String body = "<ns:getPojo xmlns:ns=\"http://johnson4u.com/\"><pojo>\n"
+ " <pojoId>23</pojoId>\n"
+ " <pojoName>dubic</pojoName>\n"
+ "</pojo><ns:/getPojo>";
the namespace notation 'ns' was not included
I believe for the object to be un-marshalled you need to specify the element in the object class.
public class Pojo {
#XmlElement(name="pojoId", required=true, namespace=YOUR_NAME_SPACE)
private int pojoId;
#XmlElement(name="pojoName", required=true, namespace=YOUR_NAME_SPACE)
private String pojoName;
// Getters and Setters ......
}
I changed web param value to
#WebMethod(operationName = "getPojo" )
public Pojo getPojo(#WebParam(name = "pojo") Pojo pjRequest){
System.out.println("Pojo name is "+pjRequest.getPojoName());
return new Pojo(234,"IM new Pojo");
}
}
and xml request to
String body = "<ns0:getPojo xmlns:ns0=\"http://johnson4u.com/\">\n" +
" <pojo>"
+ "<pojoId>456</pojoId>"
+"<pojoName>Johnson</pojoName>"
+ "</pojo>\n" +
" </ns0:getPojo>";
Related
I Have a GET request with some parameters which I handle as an object on the controller, consider it could be several parameters.
The problem is that the values for the properties on the dto are being filled using url encoding which I dont want because it messes up queries to a database later on, ie.: name gets populated with "some%20name" instead of "some name" as I would expect.
How can I avoid this encoding problem?
Bellow is a small scenario that represents my issue:
public class SomeDto {
private String name;
private String hex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHex() {
return hex;
}
public void setHex(String hex) {
this.hex = hex;
}
}
#RestController
#RequestMapping("example")
public class RestController {
#GetMapping
public void example(final SomeDto someDto) {
System.out.println(someDto.getName());
System.out.println(someDto.getHex());
}
}
public class ClientApi {
private RestTemplate restTemplate;
private String hostUri;
public ClientApi(RestTemplate restTemplate, String hostUri) {
this.restTemplate = restTemplate;
this.hostUri = hostUri;
}
public void test(SomeDto someDto) {
var uri = UriComponentsBuilder.fromUriString(hostUri + "/example");
if(someDto != null) {
uri.queryParam("name", someDto.getName())
.queryParam("hex", someDto.getHex());
}
restTemplate.exchange(uri.toUriString(), HttpMethod.GET, null, Void.class);
}
}
#SpringBootTest(
classes = DemoApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
class ClientApiTest {
#LocalServerPort
private String port;
private ClientApi clientApi;
#BeforeEach
void before() {
clientApi = new ClientApi(new RestTemplate(), "http://localhost:" + port);
}
#Test
void testMethod() {
SomeDto someDto = new SomeDto();
someDto.setName("some name");
someDto.setHex("#ffffff");
clientApi.test(someDto);
}
}
UPDATE:
I was able to partially fix it by decoding the URL, however it only fixes name "some name" to reach the controller correctly, hex "#ffffff" on the other hand reaches as null.
var decodedUri = URLDecoder.decode(uri.toUriString(), Charset.defaultCharset());
Spring uses some symbols as service symbols.
E.g. you cannot parse param value if it contains a comma.
?someParam=some,value
Would be parsed as two params: some and value. But if receive type is not array or collection then the second value will be ignored. Hence, you'll get someParam=some.
The simplest way to avoid it is URL params base64 encoding.
For me, the convenient way was to encode params as json in Base64.
{
"name": "some name",
"hex": "fffffff"
}
Why json? Because there are many ready-made solutions for parsing JSON into an object.
So, your controller will receive Base64 value which is eyJuYW1lIjoic29tZSBuYW1lIiwgImhleCI6ImZmZmZmZmYifQ==
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Base64;
import java.util.Objects;
#RestController
public class RestController {
#GetMapping("/example")
public void example(String params) {
String decoded = decodeBase64(params);
SomeDto dto = parseTo(decodedFilters, SomeDto.class);
}
public String decodeBase64(String encoded) {
if (Objects.nonNull(encoded)) {
return new String(Base64.getDecoder().decode(encoded));
}
return "";
}
public <T> T parseTo(String jsonAsString, Class<T> classType) {
String toParse = Objects.nonNull(jsonAsString) ? jsonAsString : "{}";
try {
return new ObjectMapper().readValue(toParse, classType);
} catch (IOException e) {
throw new ValidationException(e.getMessage());
}
}
}
I am writing Rest Application with Spring boot. To expose my service code in as REST services.
I am able to expose my services in GET method when I am writing Post method with below code of Controller and Pojo class I am getting 405: Method Not Allowed error.
Not able to understand why ?
I have refered this link. and others related too but i could not figure out what is the problem.
Below is my controller and Pojo with jackson Json annotated code.
When i am calling using Advanced REST client - Chrome Web Store - Google and using as attached image i am getting below error.
In the same class i have some GET method that is working fine.
Error :
URL : http://localhost:8085/DBService/application/saveApplicationAnswer
{
"timestamp": 1470313096237
"status": 405
"error": "Method Not Allowed"
"exception": "org.springframework.web.HttpRequestMethodNotSupportedException"
"message": "Request method 'POST' not supported"
"path": "/DBService/application/saveApplicationAnswer"
}
DBService is my Context Name
As I have set server.context-path=/DBService in application.properties
import java.io.Serializable;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.drd.hotel.db.service.IApplicationDBService;
import com.drd.hotel.db.service.dto.application.CustomerDTO;
import com.drd.hotel.db.service.dto.application.ApplicationAnswerDTO;
import com.drd.hotel.db.service.dto.application.ApplicationQuestionsDTO;
import com.drd.hotel.db.service.dto.application.ApplicationRecommendationDTO;
import com.drd.hotel.db.service.util.ServicesConstants;
#RestController
#RequestMapping("/application")
public class ApplicationDBController<T, I extends Serializable> {
#Autowired
private IApplicationDBService applicationDBService;
#RequestMapping(value = "/saveApplicationAnswer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public int saveApplicationAnswer(#ModelAttribute(ServicesConstants.SURVERY_ANSWER_FL) ApplicationAnswerDTO applicationAnswer) {
LOG.info("ApplicationDBController fn saveApplicationAnswer BookingId {} {} {}",applicationAnswer.getBookingId(), ServicesConstants.CUSTOMER_ID_FL, applicationAnswer.getCustomerId());
return applicationDBService.saveapplicationAnswer(applicationAnswer);
}
}
My JSON:
{"answerId":1,"applicationQuestionId":1,"recommendId":1,"bookingId":123001,"customerId":19501,"reasonForCancelation":"I dont konw ","feedbackText":"I dont know what is this too bad design","applicationDate":"2016-08-04","funnelPageName":"I dont know what is the use of this.","applicationReferenceSource":"I dont knwo what is this field for","languageId":1}
MY Pojo annotated with JSON :
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion;
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
#JsonSerialize(include =Inclusion.NON_NULL)
public class ApplicationAnswerDTO {
private int answerId;
private int applicationQuestionId;
private int recommendId;
private int bookingId;
private int customerId;
private String reasonForCancelation;
private String feedbackText;
private Date applicationDate;
private String funnelPageName;
private String applicationReferenceSource;
private int languageId;
public int getAnswerId() {
return answerId;
}
public void setAnswerId(int answerId) {
this.answerId = answerId;
}
public int getApplicationQuestionId() {
return applicationQuestionId;
}
public void setApplicationQuestionId(int applicationQuestionId) {
this.applicationQuestionId = applicationQuestionId;
}
public int getRecommendId() {
return recommendId;
}
public void setRecommendId(int recommendId) {
this.recommendId = recommendId;
}
public int getBookingId() {
return bookingId;
}
public void setBookingId(int bookingId) {
this.bookingId = bookingId;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getFeedbackText() {
return feedbackText;
}
public void setFeedbackText(String feedbackText) {
this.feedbackText = feedbackText;
}
public Date getApplicationDate() {
return applicationDate;
}
public void setApplicationDate(Date applicationDate) {
this.applicationDate = applicationDate;
}
public String getFunnelPageName() {
return funnelPageName;
}
public void setFunnelPageName(String funnelPageName) {
this.funnelPageName = funnelPageName;
}
public String getApplicationReferenceSource() {
return applicationReferenceSource;
}
public void setApplicationReferenceSource(String applicationReferenceSource) {
this.applicationReferenceSource = applicationReferenceSource;
}
public int getLanguageId() {
return languageId;
}
public void setLanguageId(int languageId) {
this.languageId = languageId;
}
public String getReasonForCancelation() {
return reasonForCancelation;
}
public void setReasonForCancelation(String reasonForCancelation) {
this.reasonForCancelation = reasonForCancelation;
}
#Override
public String toString() {
return "ApplicationAnswerDTO [answerId=" + answerId + ", applicationQuestionId="
+ applicationQuestionId + ", recommendId=" + recommendId
+ ", bookingId=" + bookingId + ", customerId=" + customerId
+ ", reasonForCancelation="
+ reasonForCancelation + ", feedbackText="
+ feedbackText + ", applicationDate=" + applicationDate
+ ", funnelPageName=" + funnelPageName
+ ", applicationReferenceSource=" + applicationReferenceSource
+ ", languageId=" + languageId + "]";
}
}
Thanks in advance for any kind of info and suggestion.
Can you check the method type which your are requesting.
In the screen shot which your shared it is displaying only get and head method are allowed.
I have tried your code in my Soap ui. It is displaying the below response.
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 04 Aug 2016 13:03:11 GMT
1999999999
It is displaying the response which you shared when i try to call the same service using Get method.Below is the response.
{
"timestamp": 1470315684018,
"status": 405,
"error": "Method Not Allowed",
"exception": "org.springframework.web.HttpRequestMethodNotSupportedException",
"message": "Request method 'GET' not supported",
"path": "/saveApplicationAnswer"
}
and code I used is
{
#RequestMapping(value = "/saveApplicationAnswer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public int saveApplicationAnswer(#ModelAttribute("hello") ApplicationAnswerDTO applicationAnswer) {
System.out.println(applicationAnswer);
return 1999999999;
}
Please try with different tools preferably soap ui.
#RequestMapping(value = "/saveApplicationAnswer", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public int saveApplicationAnswer(#RequestBody(ServicesConstants.SURVERY_ANSWER_FL) ApplicationAnswerDTO applicationAnswer) {
LOG.info("ApplicationDBController fn saveApplicationAnswer BookingId {} {} {}",applicationAnswer.getBookingId(), ServicesConstants.CUSTOMER_ID_FL, applicationAnswer.getCustomerId());
return applicationDBService.saveapplicationAnswer(applicationAnswer);
}
I have changed my parameter annotation refering this post.
From
#ModelAttribute(ServicesConstants.SURVERY_ANSWER_FL) ApplicationAnswerDTO applicationAnswer
To
#RequestBody(ApplicationAnswerDTO applicationAnswer
It worked for me. seems #RequestBody is correct. But i dont know the different between #RequestBody and #ModelAttribute. If any one knows the different please share here. That will be helpful for some one.
When i am pitting #RequestBody(ApplicationAnswerDTO applicationAnswer) It worked for me.
Any way Thanks for every one for your help and suggestion.
I use Netbeans 7.4 for my Java development. I try to use web service client/server application.
I am hiving a strange problem, let me describe it in details.
At server side, suppose i have DeviceInfo and LocationInfo classes like the following:
#XmlRootElement(name="DeviceInfo")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "DeviceInfo")
public class DeviceInfo {
#XmlElement(name = "Name")
private String name;
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
LocationInfo class has a member of DeviceInfo type:
#XmlRootElement(name="LocationInfo")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "LocationInfo")
public class LocationInfo {
#XmlElement(name = "Name")
private String name;
#XmlElement(name = "DeviceInfo", type=DeviceInfo.class)
private DeviceInfo deviceInfo;
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public DeviceInfo getDeviceInfo() {
return deviceInfo;
}
public void setDeviceInfo(DeviceInfo deviceInfo) {
this.deviceInfo = deviceInfo;
}
}
And my web service class is like:
#WebService
public class DbAccess {
public List<LocationInfo> getListLocation()
{
List<LocationInfo> listLocation = new ArrayList<>();
LocationInfo location1 = new LocationInfo();
location1.setName("Location 1");
DeviceInfo device1_1 = new DeviceInfo();
device1_1.setName("Device1_1");
location1.setDeviceInfo(device1_1);
listLocation.add(location1);
LocationInfo location2 = new LocationInfo();
location2.setName("Location 2");
DeviceInfo device2_1 = new DeviceInfo();
device2_1.setName("Device2_1");
location2.setDeviceInfo(device2_1);
listLocation.add(location2);
return listLocation;
}
}
The web service simply returns list of LocationInfo.
At web client side, I simply added a web service client from Netbeans(New/Web Service Client). I just gave the web service WSDL address to IDE and it imported the web service types. So i can consume the web service at client side. The problem is that my web client gets null deviceInfo from getDeviceInfo() function of LocationInfo.
The code that i use in web client:
DbAccessService srv = new DbAccessService();
DbAccess db = srv.getDbAccessPort();
List<LocationInfo> list = db.getListLocation();
for(int i=0; i<list.size(); i++)
{
String str = "-" + list.get(i).getName();
if(list.get(i).getDeviceInfo() == null)
str += "\r\n---NULL";
else
str += "\r\n---" + list.get(i).getDeviceInfo().getName();
System.out.println(str);
}
This produces the following output:
-Location 1
---NULL
-Location 2
---NULL
Why do i get null insead of actual DeviceInfo?
edit* let me post as an answer for better formatting
No <root> was never used in your case, I just put that for an example. In your instance, since LocationInfo holds a DevinceInfo you want LocationInfo to be the root element. Defining DeviceInfo as a root element also confused the marshaller. Your Xml will look like
<LocationInfo>
<name>
Location 1
</name>
<DeviceInfo>
<name>
Device_1
</name>
</DeviceInfo>
</LocationInfo>
see how LocationInfo is the root element, defined by #XmlRootElement
I have created a RestfulWeb Service in Java that is working well with GET requests. However I cannot find some good resource on how to make it accept POST requests.
This is how I run GET
#Path("/hello")
public class Hello {
#GET
#Path(value = "/ids/{id}")
public String ids(#PathParam(value = "id") final String id) throws JSONException {
return getResults("select * from " + id);
}
In order to access this method from web I just go to mywebservice.com/hello/thisismyID and the method receives the "ID".
How would this look if it was to be done with a POST.
Thanks in advance,
-D
Example
#Path("/hello")
public class Hello {
#POST
#Path(value = "/ids")
public String ids(#HeaderParam("id") final String id) throws JSONException {
return getResults("select * from " + id);
}
}
#Path("/hello")
public class Hello {
#POST
#Path("/ids/{id}")
public String ids(#PathParam("id") final String id) throws JSONException {
return getResults("select * from " + id);
}
}
an exhaustive tutorial can be found here: Vogella_REST
How I can print 'null' as field value, when marshalling the string?
Example: error and error_code are Strings, and i want to use 'null' as a value indicating that there is no value/errors happened on the server side.
{
"error_code": null,
"error": null
}
Today, I have to use EMPTY values, so that "error_code" or "error" these fields generally fall into json, and if they were not explicitly initialized as this.errorCode = StringUtils.EMPTY;
So today, I have next json:
{
"error_code": "",
"error": ""
}
This is how that looks in a code:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.FIELD)
public class Response
{
#SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(Response.class);
public static final String ERROR_FIELD_NAME = "error";
public static final String ERROR_CODE_FIELD_NAME = "error_code";
// #XmlJavaTypeAdapter(CafsResponse.EmptyStringAdapter.class)
#XmlElement(name = Response.ERROR_CODE_FIELD_NAME)
private String errorCode;
// #XmlJavaTypeAdapter(CafsResponse.EmptyStringAdapter.class)
#XmlElement(name = Response.ERROR_FIELD_NAME)
private String errorMessage;
// Empty Constructor
public Response()
{
this.errorCode = StringUtils.EMPTY; // explicit initialization, otherwise error_code will not appear as part of json, how to fix this this ?
this.errorMessage = StringUtils.EMPTY;
}
etc...
// Empty Constructor
public Response()
{
this.errorCode = null; // this variant dosn't work either, and error_code again didn't get to json
this.errorMessage = null;
}
See, #XmlJavaTypeAdapter, i thought that this potentially could help me - but no :)
Instead of null value, i'm getting "null" as string.
if (StringUtils.isEmpty(str))
{
return null;
}
return str;
{
"error_code": "null", // this is not whta i wanted to get.
"error": "null"
}
Any help on this? - ask me if something is not clear.
full list:
/**
* Empty string Adapter specifying how we want to represent empty strings
* (if string is empty - treat it as null during marhsaling)
*
*/
#SuppressWarnings("unused")
private static class EmptyStringAdapter extends XmlAdapter<String, String>
{
#Override
public String unmarshal(String str) throws Exception
{
return str;
}
#Override
public String marshal(String str) throws Exception
{
if (StringUtils.isEmpty(str))
{
return null;
}
return str;
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You could use MOXy as your JSON provider to support this use case. Below is an example:
Response
MOXy will marshal properties marked with #XmlElement(nillable=true) to the representation you are looking for
(see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).
package forum11319741;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
public static final String ERROR_FIELD_NAME = "error";
public static final String ERROR_CODE_FIELD_NAME = "error_code";
#XmlElement(name = Response.ERROR_CODE_FIELD_NAME, nillable = true)
private String errorCode;
#XmlElement(name = Response.ERROR_FIELD_NAME, nillable = true)
private String errorMessage;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum11319741;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty("eclipselink.json.include-root", false);
Response response = new Response();
marshaller.marshal(response, System.out);
}
}
Output
{
"error_code" : null,
"error" : null
}
MOXy and JAX-RS
You can use the MOXyJsonProvider class to enable MOXy as your JSON provider in your JAX-RS application (see: http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html).
package org.example;
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class CustomerApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(CustomerService.class);
return set;
}
}
For More Information
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html