Rest Easy Client Framework value lost after unmarshaling - java

Yesterday I tried to use the client side of the RestEasy framework. The interface has a method:
#PUT
#Path("document/autoincrement")
#Consumes("application/xml")
BaseClientResponse<String> insertPointOfInterest(PoiDocument poiDocument);
and the call to some (Jersey) rest service looks like:
String restServerServiceUrl = "http://my.jersey.server/rest/serviceFoo/v1/";
NSSClientService client = ProxyFactory.create(NSSClientService.class, restServerServiceUrl);
PoiDocument poiDocument = new PoiDocument("Parkirišče", "90", 390262.85133115170, 42240.33558245482);
BaseClientResponse<String> response = client.insertPointOfInterest(poiDocument);
assert response.getResponseStatus() == Response.Status.OK;
// Expected result
//<?xml version="1.0" encoding="UTF-8" standalone="yes"?><insertedRecord><record>14</record></insertedRecord>
logger.info("Returned: " + response.getEntity());
And the logger prints:
14
Kind of expected.
But I want an object not a string, so I can easely assert the values returned. The interface:
#PUT
#Path("document/autoincrement")
#Consumes("application/xml")
BaseClientResponse<InsertedResponse> insertPointOfInterest(PoiDocument poiDocument);
Instead of String there is now a InsertedResponse class which looks like:
#XmlRootElement(name="insertedRecord")
public class InsertedResponse extends ResponseResult{
String insertedRecord;
public InsertedResponse(int insertedRecord) {
this.insertedRecord = Integer.toString(insertedRecord);
}
public InsertedResponse(){
insertedRecord = "";
}
#XmlElement(name="record")
public String getInsertedRecords(){
return insertedRecord;
}
public void add(int recNo) {
insertedRecord = Integer.toString(recNo);
}
}
...and its superclass:
#XmlRootElement(name = "result")
public abstract class ResponseResult {
protected String getClearString(String string) {
if (string != null) {
return Constants.removeInvalidXMLCharacters(string);
}
return "";
}
}
Now, when I change the client call also to:
BaseClientResponse<InsertedResponse> response = client.insertPointOfInterest(poiDocument);
logger.info("Returned: " + response.getEntity().getInsertedRecords());
I get an empty string instead of some value.
So, the question is - where did the value of go? It should print a number, like 14 in the above example.

One missing JAXB annotation (#XmlSeeAlso)
#XmlRootElement(name = "result")
#XmlSeeAlso( { InsertedResponse.class, OtherChild.class, SomeOtherChild.class })
public abstract class ResponseResult {
...
}
and an added setter method
#XmlRootElement(name="insertedRecord")
public class InsertedResponse extends ResponseResult{
...
public void setInsertedRecords(String insertedRecord) {
this.insertedRecord = insertedRecord;
}
solved the problem.

Related

How to get the body from a POST call. JAX-RS

I make a POST-type call in eclipse / java using JAX-RS
I can not handle the return in the method predictCid
This method sends the textToPredict parameter and receives a return string, how can I get this value and set it to the variable, textPredicted?
#Path("Predicao")
public class PredicaoCIDResource extends BaseResource {
#POST
#Path("predicaoCid")
public RetornoGenerico<PredicaoCidVo> predizerCid(PredicaoCidVo predicaoVo) {
System.out.print("\nentrou no método java");
RetornoGenerico<PredicaoCidVo> retorno = new RetornoGenerico<PredicaoCidVo>();
String nomeMetodo = "predicaoCid";
super.criarRetornoSucesso(nomeMetodo, retorno);
System.out.print("passou pelo super");
try {
System.out.print("\nentrou no try");
PredicaoCidVo predicaoCidVo = new PredicaoCidVo();
Response retornoPred = predictCid(predicaoVo.getTextToPredict());
System.out.print("retornou do método predict");
predicaoCidVo.setTextPredicted(retornoPred.getEntity().toString());
retorno.setRetorno(predicaoCidVo);
} catch (Exception e) {
super.trataExececao(retorno, e, nomeMetodo);
}
return retorno;
}
#POST
#Path("http://127.0.0.1:5000/predict")
#Consumes("application/x-www-form-urlencoded")
private Response predictCid(#FormParam("textToPredict") String predicaoVo) throws IOException {
System.out.print("\nentrou no método predict");
//How get te return ??? String
}
PredicaoVo:
#XmlRootElement
public class PredicaoCidVo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2471424108047814793L;
private String textToPredict;
private String textPredicted;
public String getTextToPredict() {
return textToPredict;
}
public void setTextToPredict(String textToPredict) {
this.textToPredict = textToPredict;
}
public String getTextPredicted() {
return textPredicted;
}
public void setTextPredicted(String textPredicted) {
this.textPredicted = textPredicted;
}
}
The call is made correctly (predictCid), returns with status 200 (OK).
But, I can not return in one of the class variables PredicaoVo.
How do I make this return by filling in, for example, the textPredicted object?
The return, within the method that does the POST, is a simple string
Below, the feedback I have on SOAPUI testing:
<Response xmlns="http://app-homolog/overcare-ws/rest/Profissional/predicaoCid">
<retorno>
<textPredicted>predicao.PredicaoCidVo#3372c9d7</textPredicted>
<textToPredict null="true"/>
</retorno>
<retornoMensagem>
<dsMensagem>predicaoCid.sucesso</dsMensagem>
<dsStackTrace null="true"/>
<dsTitulo>predicaoCid.titulo</dsTitulo>
<idCamada>SUCESSO</idCamada>
</retornoMensagem>
</Response>
Who sends the return to the soapUI is the method predizerCid

JsonGenerationException: Can not write a field name

My problem lies in the fields of JsonSerialization as implemented in Jackson FasterXML Library. I have a series of endpoints through which I exchange content between my back-end and a MVVM front-end framework. This is working, but now I am a little stuck as I got to the point where I want to handle user creation/registration.
This is the model (entity) that represents a group in my application (I omit irrelevant import declarations and JPA annotations):
#JsonRootName(value="userGroup")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Groups extends MetaInfo implements Serializable {
private String groupName;
private Set<Credential> credentials = new HashSet<>();
public Groups() {
super();
}
public Groups(String groupName) {
this();
this.groupName = groupName;
}
public Groups(String createdBy, String groupName) {
this();
setCreatedBy(createdBy);
this.groupName = groupName;
}
#JsonGetter("group_Name")
// #JsonValue
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
updateModified();
}
#JsonIgnore
public Set<Credential> getCredentials() {
return credentials;
}
public void setCredentials(Set<Credential> credentials) {
this.credentials = credentials;
}
public void addCredential(Credential c) {
credentials.add(c);
if (c.getGroup() != this) {
c.setGroup(this);
}
}
#Override
public String toString() {
return "Groups{" + "groupName=" + groupName + '}';
}
}
And this is the method in the endpoint that retrieves (if exists) and returns a serialized version of a Groups to a JavaScript client:
#Path("/groups")
#Produces("application/json")
public class GroupsResourceService extends RimmaRestService{
#Inject
#Production
private GroupsRepository groupsRepository;
...
#GET
#Path("{group}")
public Response getGroup(#PathParam("group") String group){
if(InputValidators.stringNotNullNorEmpty.apply(group)){
//proceed with making call to the repo
Optional<Groups> optGroup = ofNullable(groupsRepository.getByGroupName(group));
if(optGroup.isPresent()){//ultimately success scenario
try {
String output = getMapper().writeValueAsString(optGroup.get());
return Response.ok(output).build();
} catch (JsonProcessingException e) {
logger.error("Serialization error: "+e.getMessage()+
"\n"+e.getClass().getCanonicalName());
throw new InternalServerErrorException("Server error "
+ " serializing the requested group \""+group+"\"");
}
} else{
throw new NotFoundException("Group " + group + " could not be found");
}
}else{//empty space after the slash
throw new BadRequestException("You haven't provided a group parameter",
Response.status(Response.Status.BAD_REQUEST).build());
}
}
}
Trying to test this code like this:
#Test
public void successfullResponse(){
Response success = groupsResourceService.getGroup("admin");
assertTrue(success.getStatus()==200);
}
...cruelly fails:
<< ERROR!
javax.ws.rs.InternalServerErrorException: Server error serializing the requested group "admin"
at com.vgorcinschi.rimmanew.rest.services.GroupsResourceService.getGroup(GroupsResourceService.java:54)
at com.vgorcinschi.rimmanew.services.GroupsResourceServiceTest.successfullResponse(GroupsResourceServiceTest.java:48)
In this case stack trace is of 0 help, though - that's why I am pasting the output of the log that catches the underlying Json exception:
15:05:05.857 [main] ERROR com.vgorcinschi.rimmanew.rest.services.GroupsResourceService - Serialization error: Can not write a field name, expecting a value
com.fasterxml.jackson.core.JsonGenerationException
Having visited and analyzed 13 similar complaints links (of which 3 are from stackoverflow) I came up with a solution which is more a workaround - if you look back at the entity, I have commented #JsonValue. If I uncomment that and comment #JsonGetter("group_Name") then the test passes with the following output:
{"userGroup":"admin"}
This being only a workaround, I decided to recur to asking for help which I hope someone will be able and kind enough to provide.

Transition from old Java to Play, Servlet issue

I have to move from an old Java code to a new one using Play! Framework.
In the old code, I called a Java servlet using Ext-Js (Javascript Framework) using this way :
function getTree()
{
var store = Ext.create('Ext.data.TreeStore',
{
root:
{
text: 'System',
id: 'root',
expanded: true
},
proxy:
{
type: 'ajax',
url: 'TreeServlet',
extraParams:
{
mode:'getChildren'
},
reader:
{
type:'json',
root:'result'
}
}
});
Now, I would like to use Play! to do the same, but I do not know how to use it.
. In routes.conf:
GET /tree controllers.Application.makeTree()
. In controller.Application:
public static Result makeTree(){
// What shoul I put here to call the "Servlet"
}
I do not want to use Servlet, but I don't know how to do it.
Thank you for you help!
EDIT 1:
Thank you to all of you!
Here is how I eventually manage to achieve my goal:
public class Tree extends Controller
{
private MenuManager menuManager;
String node;
String mode;
String hash;
ObjectNode response;
public void createTree() throws IOException{
this.menuManager = MenuManager.getMenuManager();
getParams();
createJson(mode, node, hash);
}
public static Result returnJson() throws IOException{
Tree t = new Tree();
t.createTree();
return ok(t.response);
}
}
And in routes:
GET /tree controllers.Tree.returnJson()
What do you guys think? Good practice?
In earlier play frameworks you have to create only static methods for each and every request handler in controller.
But in the newer version (after play 2.0) you don't need to have static methods you can use normal public methods and configure it in routes prefixed with '#' symbol.
And don't maintain or declare attributes within controller class.
Because play is an event driven framework not like oridinary servlet based framework.
It provides REST and it doesn't maintain any httpsession like in servlets.
Session is available in the form of cookies only.
Below is the remodified version of your code,
public class TreeController extends Controller
{
public void createTree() throws IOException{
MenuManager menuManager = MenuManager.getMenuManager();
String mode = request().getQueryString("mode");
String node = request().getQueryString("node");
String hash = request().getQueryString("hash");
TreeNodeDto treeObject = menuManager.buildTree();
ok(treeObject.toJson());
}
}
public class BaseDto<T extends BaseDto<T>> implements Serializable{
public JsonNode toJson() {
return Json.toJson(this);
}
public T fromJson(JsonNode jsonObject) {
return (T) Json.fromJson(jsonObject, this.getClass());
}
}
public static class TreeNodeDto extends BaseDto {
public String hash;
public String name;
public Set<TreeNodeDto> children;
// Override equals and hashcode, because we are using "set" to maintain the child nodes.
}
routes
GET /tree #controllers.TreeController.createTree()
Hope this will give some ideas.
Cheers..!!!
Check the WS object: https://www.playframework.com/documentation/2.3.x/JavaWS
It seems that the return from the Servlet is a Json so check how to process json in play here: https://www.playframework.com/documentation/2.3.x/JavaJsonActions
I believe something like that should do it
public static Promise<Result> makeTree() {
final Promise<Result> resultPromise = WS.url("TreeServlet").setHeader("Content-Type", "application/json").get().map(
new Function<WSResponse, Result>() {
public Result apply(WSResponse response) {
return ok("Feed title:" + response.asJson().findPath("result"));
}
}
);
return resultPromise;
}
Below is the structure for your http request in play,
public static Result makeTree() {
TreeDto treeDto=new TreeDto();
JsonNode jsonResponse = Json.newObject();
try {
treeDto = <<Logic to get the tree objects from db>>;
if(treeDto != null) {
jsonResponse = Json.toJson(treeDto);
}
} catch (XODAOException e) {
Logger.error("Error while building the tree.", e);
jsonResponse = generateErrorResponse("Error while building tree.", e);
}
return ok(jsonResponse);
}

Using generics to refactor

I am used to use generics in typed collections, but I never actually used them to develop something.
I have several classes like this:
public class LogInfoWsClient extends GenericWsClient {
public void sendLogInfo(List<LogInfo> logInfoList) {
WebResource ws = super.getWebResource("/services/logInfo");
try {
String response = ws.accept(MediaType.TEXT_HTML).type(MediaType.APPLICATION_XML).put(String.class, new GenericEntity<List<LogInfo>>(logInfoList) {
});
}
}
Where the only thing changing between one and another is the service String ("/services/info"), and the type of the list (LogInfo in this case)
I have refactored a couple of methods to a GenericWsClient class, but my objective would be to have something I can use like this:
List<LogInfo> myList = database.getList();
SuperGenericClient<List<LogInfo>> superClient = new SuperGenericClient<List<LogInfo>>();
superClient.send(myList,"/services/logInfo");
But I cannot figure out how to do it, or even if its possible. Would it be possible?
Yes it is possible infact if you look at java.util.collection package for example you will find all classes to be parameterzid.
So your class will be something like this
public SuperGenericClient<E> {
public E getSomething() {
return E;
}
}
Then to use it you will have
SuperGenericClient<String> myGenericClient = new SuperGenericClient<String>();
String something = myGenericClient.getSomething();
Extending your example itself your code will look like this:
public class SuperGenericClient<E> extends GenericWsClient {
public void send(List<E> entityList, String service) {
WebResource ws = super.getWebResource(service);
try {
String response = ws.accept(MediaType.TEXT_HTML).type(MediaType.APPLICATION_XML).put(String.class, new GenericEntity<E>(entityList) {
});
}
}
}
public class GenericEntity<E> {
public GenericEntity(List<E> list){
}
}
You must read this for a very good understanding of Generics.
You could write your class like the one below - you can apply the same idea to GenericEntity.
public class SuperGenericClient<T> extends GenericWsClient {
public void send(List<T> list, String service) {
WebResource ws = super.getWebResource(service);
try {
String response = ws.accept(MediaType.TEXT_HTML).type(MediaType.APPLICATION_XML).put(String.class, new GenericEntity<T>(list) {
});
}
}
}
You can then call it like that:
List<LogInfo> myList = database.getList();
SuperGenericClient<LogInfo> superClient = new SuperGenericClient<LogInfo>();
superClient.send(myList,"/services/logInfo");
Declare your class like this:
public class LogThing<T> {
public void sendLogInfo(List<T> list) {
// do thing!
}
}
And when you use it, do so like this:
List<LogInfo> myList = db.getList();
LogThing<LogInfo> superClient = new LogThing<LogInfo>();
superClient.sendLogInfo(myList);

Not able to append XML element in response

I have created a webservices which accepts XML data and after some computation over server it adds few fields in xml and returns the output to the client. I am using JAX-RS for Restful webservice and JAXB.
Now the problem is when the response is sent back to the client it doesn't include the newly upadated elemnts.
here is the code detail,
class that represent XML (Using JAXB)
#XmlRootElement(name = "market")
#XmlAccessorType(XmlAccessType.FIELD)
public class IBMarketInfo {
#XmlElement(name="contract")
Contract m_Contract;
#XmlElement(name="tickerId")
int m_tickerId;
#XmlElement(name="tickList")
String m_genericTickList;
#XmlElement(name="snapshot")
boolean m_snapshot;
#XmlElement(name="mktdata") // I AM NOT BE ABLE TO VIEW THIS ELEMENT IN THE RESPONSE
List<String>m_Ticker;
public IBMarketInfo(){
}
public void setTicker(String data){
if (m_Ticker == null) {
m_Ticker = new ArrayList<String>();
}
m_Ticker.add(data);
}
public List<String> getTicker(){
if (m_Ticker == null) {
m_Ticker = new ArrayList<String>();
}
return m_Ticker;
}
public void setTickerId(int tickerid){
m_tickerId = tickerid;
}
public void setGenericTickList(String ticklist){
m_genericTickList = ticklist;
}
public void setSnapshot(boolean snapshot){
m_snapshot=snapshot;
}
public void setContract(Contract contract){
m_Contract = contract;
}
public int getTickerId(){
return m_tickerId;
}
public String getGenericTickList() {
return m_genericTickList;
}
public boolean getSnapShot(){
return m_snapshot;
}
public Contract getContract(){
return m_Contract;
}
}
Restful Webservices request function
public JAXBElement<IBMarketInfo>getMarketData(JAXBElement<IBMarketInfo> info){
MainAccess ma = new MainAccess(); // MainAccess Will pull the data from external server
IBMarketInfo market = info.getValue();
ma.onRequestData(market.getTickerId(),market.getContract(),market.getGenericTickList(),
market.getSnapShot()); // set the user given input from xml
return info;
}
Inside MainAccess class i am doing following
public class MainAccess {
private IBMarketInfo m_marketInfo = new IBMarketInfo(); //declaring in class
// as an when data comes these following functions will add data into List
public void tickSize( int tickerId, int field, int size) {
String msg = EWrapperMsgGenerator.tickSize( tickerId, field, size);
m_marketInfo.setTicker(msg); // setting m_Ticker
}
public void tickPrice( int tickerId, int field, double price, int canAutoExecute) {
String msg = EWrapperMsgGenerator.tickPrice( tickerId, field, price, canAutoExecute);
m_marketInfo.setTicker(msg); //setting m_Ticker
}
}
i have created following Market information object and tried to set values of List in between the code
private IBMarketInfo m_marketInfo = new IBMarketInfo();
m_marketInfo.setTicker(msg);
XML Request
The problem is i am getting the same XML without appending that mktdata
<?xml version="1.0" encoding="UTF-8"?>
<market>
<contract>
<symbol>IBM</symbol>
<sectype>STK</sectype>
<exchange>SMART</exchange>
<currency>USD</currency>
</contract>
<tickerId>1</tickerId>
<tickList>1212,12121</tickList>
<snapshot>false</snapshot>
<ticker-data></ticker-data>
</market>
Your problem is in the following code. MainAccess creates an IBMaretInfo, but you are returning the IBMarkettInfo that was passed in unmodified.
public JAXBElement<IBMarketInfo>getMarketData(JAXBElement<IBMarketInfo> info){
MainAccess ma = new MainAccess(); // MainAccess Will pull the data from external server
IBMarketInfo market = info.getValue();
ma.onRequestData(market.getTickerId(),market.getContract(),market.getGenericTickList(),
market.getSnapShot()); // set the user given input from xml
return info;
}
You model appears to be correctly mapping, since when I run the following code:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(IBMarketInfo.class);
IBMarketInfo ibmi = new IBMarketInfo();
ibmi.setTicker("FOO");
ibmi.setTicker("BAR");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(ibmi, System.out);
}
}
I get the following output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<market>
<tickerId>0</tickerId>
<snapshot>false</snapshot>
<mktdata>FOO</mktdata>
<mktdata>BAR</mktdata>
</market>

Categories

Resources