RESTlet on GWT unable to retrieve RESTful resource not backed by GWT enabled server - rest

I am trying to get GWT+RESTlet to communicate with a RESTful service, which is not "GWT aware".
On the GWT client, I do something like
Reference ref = new Reference("http://localhost:8080/control/programs/");
ProgramListResourceProxy clientResource = GWT.create( ProgramListResourceProxy.class );
clientResource.setReference( proxyRef );
clientResource.setFollowingRedirects( true );
clientResource.accept( MediaType.APPLICATION_JSON );
clientResource.accept( MediaType.APPLICATION_XML );
ProgramListResourceProxy resource = RestClient.createProgramListResource();
resource.retrieve( new Result<ArrayList<ProgramRef>>()
{
#Override
public void onFailure( Throwable caught )
{
while( caught != null)
{
Window.alert( "Error retrieving programs.\n" + caught.getMessage() );
caught = caught.getCause();
}
}
#Override
public void onSuccess( ArrayList<ProgramRef> result )
{
Window.alert( "Programs: " + result );
programs = result;
view.setRowData( toStringList( result ) );
}
});
If I request the resource from the browser, I get
[{"name":"niclas","link":{"action":"GET","path":"/control/programs/niclas/"}}]
as expected.
But when doing the code above in GWT, I get the popup alert telling me that there is a problem, and in the nested exception is;
Error retrieving programs.
Can't parse the enclosed entity because of its media type.
Expected <application/x-java-serialized-object+gwt> but was
<application/json>. Make sure you have added the
org.restlet.client.ext.gwt.jar file to your server.
The MediaTypes matches in the request/response and the traffic looks like this.
Request;
GET /control/programs/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Accept: application/json, application/xml
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22
Referer: http://localhost:8080/connect/Connect.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Response;
HTTP/1.1 200 OK
Date: Sat, 30 Mar 2013 15:46:04 GMT
Content-Type: application/json; charset=UTF-8
Date: Sat, 30 Mar 2013 15:46:04 GMT
Accept-Ranges: bytes
Server: Restlet-Framework/2.1.2
Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
Transfer-Encoding: chunked
4E
[{"name":"niclas","link":{"method":"GET","path":"/control/programs/niclas/"}}]
Can anyone explain why Restlet is expecting "application/x-java-serialized-object+gwt" and not the set MediaType in the resource??
Is it related to using org.restlet.client.resource.ClientProxy? If so, is there another way to do these async requests with RESTlet??
I am using RESTlet 2.1.2 and GWT 2.2.0.
Thanks in Advance.

I usually use JsonpRequestBuilder with overlay types (in this example JsonVideoList):
JsonpRequestBuilder builder = new JsonpRequestBuilder();
builder.requestObject(url, new AsyncCallback<JsonVideoList>() {
#Override
public void onFailure( Throwable exception) {
}
#Override
public void onSuccess(JsonVideoList list) {
}
});

I found this issue searching for your problem in google, this might be connected.
The following might not be productive but you could also have a look to restyGWT for making rest calls.

End of the day there were several problems wrapped up in this, and I thought I'd better share my findings;
I didn't realize that I had a "same-origin" problem. This was solved by using a transparent proxy on the GWT serving port to my Rest Server. This also seems to correctly forward the authentication of the Rest server to the GWT application in the browser.
Restlet's packaging in Editions seems like a really good idea. But it was really difficult to get the IntelliJ IDEA to be able to set up the GWT debug environment with these plus a shared DTO module. I think I had other problems regarding Restlet's way to figure out which content types being used and what not. End of the day, I convinced myself to not bother with a shared DTO library and instead;
Server side uses Restlet's Jackson extension, and pojo classes as DTOs.
On the GWT side, I ask Restlet to only deal with Representation type and use the AutoBean feature in GWT to do the serialization. (See below)
The AutoBean is great;
public interface Program
{
String getName();
void setName( String name );
List<Block> getBlocks();
void setBlocks(List<Block> blocks);
List<Connection> getConnections();
void setConnections(List<Connection> connections );
public static class Factory
{
public static Program make() {
AutoBean<Program> ref = ModelFactory.instance.program();
return ref.as();
}
public static String toJson(Program ref) {
AutoBean<Program> bean = AutoBeanUtils.getAutoBean( ref );
return AutoBeanCodex.encode( bean ).getPayload();
}
public static Program fromJson(String json) {
AutoBean<Program> bean = AutoBeanCodex.decode( ModelFactory.instance, Program.class, json );
return bean.as();
}
}
}
To make the sample complete, my ModelFactory has creation methods for these, which are also fully handled by GWT;
public interface ModelFactory extends AutoBeanFactory
{
ModelFactory instance = GWT.create( ModelFactory.class );
AutoBean<Program> program();
AutoBean<ProgramRefList> programRefList();
:
}
One issue that wasn't obvious was how to handle top-level JSON lists, since AutoBean matches JSON keys with fields in the interfaces. But I found a neat little trick, which can be seen in the following snippet of the same program;
public interface ProgramRefList
{
List<ProgramRef> getList();
public static class Factory
{
public static ProgramRefList make()
{
AutoBean<ProgramRefList> ref = ModelFactory.instance.programRefList();
return ref.as();
}
public static String toJson( ProgramRefList ref )
{
AutoBean<ProgramRefList> bean = AutoBeanUtils.getAutoBean( ref );
return AutoBeanCodex.encode( bean ).getPayload();
}
public static ProgramRefList fromJson( String json )
{
json = "{ \"list\": " + json + "}";
AutoBean<ProgramRefList> bean = AutoBeanCodex.decode( ModelFactory.instance, ProgramRefList.class, json );
return bean.as();
}
}
}
The top-level list is faked into another object with a single key ("list" in this case) which then is reachable from the getList() method.
And this is then used very simply in the Rest client code;
ClientResource resource = RestClient.createProgramList( new Uniform()
{
#Override
public void handle( Request request, Response response )
{
Logger.trace( this, "handle(" + request + "," + response + ")" );
try
{
if( response.isEntityAvailable() )
{
String jsonText = response.getEntity().getText();
programs = ProgramRefList.Factory.fromJson( jsonText );
ArrayList<String> rowData = toStringList( programs );
view.setRowData( rowData );
}
}
catch( Exception e )
{
Logger.handleException( this, "loadProgramsList()", e );
}
}
} );
resource.get();

Related

Headers in POST in Grails 3 app are not being sent with rest of service

Using Grails 3.0.9, and grabbing the freshest REST API with this snippet in gradle.build:
compile 'org.grails:grails-datastore-rest-client:4.0.7.RELEASE', {
['commons-codec', 'grails-async', 'grails-core',
'grails-plugin-converters', 'grails-web', 'groovy'].each {
exclude module: it
}
}
I am trying to make the following POST request:
def rest = new RestBuilder(headers:["X-LSS-Env":"devmo"], connectTimeout:10000, readTimeout:20000)
response = rest.post("http://..../..") {
accept "application/json"
contentType "application/json"
json jsonBuilder
}
Now, the POST receiver gets the json okay, give back a response okay, but this is the problem: it receives the headers as an empty map or as null!
So, what is the correct way of passing header data to the POST receiver? This is needed because the environment key X-LSS-Env could have different values, which instructs the receiver to do further routing based on it. Same with the GET request of course.
* UPDATE *
The consumer of my POST requests is actually a Java application, running on Apache Tomcat/8.0.26. The is how the service looks on the other side:
private javax.servlet.http.HttpServletRequest hsr;
#POST
#Path("/na")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response postSomething(Ggfp ggfp ){
try {
Enumeration<String> hnames = hsr.getHeaderNames();
int i = 0;
while (hnames.hasMoreElements()) {
String headerName = hnames.nextElement();
System.out.println(++i+ " headerName: " + headerName);
String val = hsr.getHeader(headerName);
System.out.println(" val: " + val);
}
String hval = hsr.getHeader("X-LSS-Env");
return Response.status(Status.OK).entity("X-LSS-Env is " + hval).build();
} catch (Exception e) {
}
}
Calling this service from Postman works, headers are identified. Calling it from the Grails app results into an empty map - like I am sending no headers!
The RestBuilder constructor never liked the way I used (or abused) it. Here is a clean way of achieving what I set out to do, with tryCatch logic if a timeout transpires.
def makePostWsr(serviceUrl, jsonBuilder) {
try {
def rest = new RestBuilder(connectTimeout:connectTimeout, readTimeout:readTimeout)
def response = rest.post("$wsUrl/$serviceUrl") {
header 'X-LSS-Env', 'devmo'
accept "application/json"
contentType "application/json"
json jsonBuilder
}
response
} catch (Exception e) {
println "== problem makePostWsr on $serviceUrl"
null
}
}

spring boot (mvc) response with different content type encoding on error

I need help with spring handling an error.
a client service is sending a request accepting two different content types - binary and json. when everything works fine I prefer communicating to my server with binary encoding to save bandwidth. but on error I would like serialise ResponseEntity to json as my binary serialiser do not know how to serialise it to binary format, plus it is better for logging, etc.
I configured instance of ResponseEntityExceptionHandler and I am handling different exceptions from that implementation. but spring always choses binary format as it is first on the accept (or produces) list.
all I get is (because spring do not know how to serialise ResponseEntity to my custom binary format. see AbstractMessageConverterMethodProcessor#writeWithMessageConverters)
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
client sends
headers {Accept: [application/custom-binary, application/json]
server's controller is configured to
// pseudo code
#RequestMapping(method = GET, produces = {"application/custom-binary", APPLICATION_JSON_VALUE})
public BannerMetaCollection get(#RequestParam(value = "q") UUID[] q) {
if (q != null) {
return service.getAllDataWith(q);
} else {
throw new IllegalArgumentException("invalid data");
}
}
// pseudo code
public class RestExceptionResolverSupport extends ResponseEntityExceptionHandler {
#ExceptionHandler
public ResponseEntity<Object> illegalArgumentException(IllegalArgumentException ex, WebRequest request {
Object body = errorResponse()
.withCode(BAD_REQUEST)
.withDescription("Request sent is invalid")
.withMessage(ex.getMessage())
.build());
return new ResponseEntity<Object>(body, new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
}
any hints?
What I do to get this to work is that a let my endpoint method return a ResponseEntity and I don't declare what content is produced in the #RequestMapping annotation. I then set the Content-type header myself before returning the response, e.g.
// pseudo code
#RequestMapping(method = GET)
public ResponseEntity<BannerMetaCollection> get(#RequestParam(value = "q") UUID[] q) {
if (q != null) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "application/custom-binary");
return new ResponseEntity<>(service.getAllDataWith(q),
headers,
HttpStatus.OK);
} else {
throw new IllegalArgumentException("invalid data");
}
}

GWT Client: sending a file with multipart/form-data post request

I have already tried a lot of approaches to send a xml file as string + pictures with a POST request using GWT on the client side. I can send the strings successfully, but I do not know how to send files (pictures) using the RequestBuilder, I am just able to send the strings.
Do someone know how to send files with a multipart/form-data POST request using GWT Client (RequestBuilder)?
P.S.: As I am not wishing to upload files, I don't need a uploader or something similar. I am developing a mobile app with Phonegap, and making pictures, which should be sent per POST request to a server (a third party service).
Thanks in advance!
Here some code:
public void sendPost() throws RequestException {
String boundary = createBoundary();
String xml = "<note> <to>Müller</to> <from>Jani</from> <heading>Erinnerung</heading> <body>Ich wohne in der Leipzigerstraße</body> </note>";
String requestData = getRequestData(boundary, xml);
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, "http://localhost:8080/xxx/yyy");
builder.setHeader("Content-Type", "multipart/form-data; charset=UTF-8; boundary=" + boundary);
builder.setHeader("Content-Length", Long.toString(requestData.length()));
try {
builder.sendRequest(requestData, new RequestCallback() {
public void onResponseReceived(Request request, Response response) {
}
public void onError(Request request, Throwable exception) {
exception.printStackTrace();
}
});
} catch (RequestException e) {
e.printStackTrace();
}
}
private String getRequestData(String boundary, String xml) {
String s = "";
s += "--" + boundary + "\r\n";
s += getRequestParameter("xml", xml + "");
s += "--" + boundary + "--\r\n"; // end
return s;
}
private String getRequestParameter(String key, String value) {
return "Content-Disposition: form-data; name=\"" + key + "\"\r\n\r\n"
+ value + "\r\n";
}
private String createBoundary() {
return "----GoneVerticalBoundary" + getRandomStr() + getRandomStr();
}
private String getRandomStr() {
return Long.toString(random.nextLong(), 36); //random -> DEFINED IN THE CLASS BODY
}
If you are using gwt + phonegap you should be using gwt-phonegap, right?
What I do in my apps, is to use gwtupload for the desktop version, and phonegap file transfer in the mobile. I use gwtupload servlet in the server side for both cases.
This is my code using gwt-phonegap:
FileUploadOptions options = new FileUploadOptions();
options.setFileKey("file");
options.setFileName("my_file.txt");
options.setMimeType("text/plain");
phonegap.getFile().createFileTransfer().upload(
"file:///my_path/my_file.txt",
"http://my-gwtupload-servlet/servlet.gupld",
options,
new FileUploadCallback() {
#Override
public void onSuccess(FileUploadResult result) {
if (result.getResponseCode() == 200) {
} else {
}
}
#Override
public void onFailure(FileTransferError error) {
Window.alert("Error sending the file, error-code: " + error.getCode());
}
});
I use deferred binding for selecting the appropriate implementation using phonegap.env:
<replace-with class="...UploadFilePhoneGap">
<when-type-is class="....UploadFile" />
<when-property-is name="phonegap.env" value="yes" />
</replace-with>
If you are looking for pure gwt solution then you need to work with FileUpload
If you do not mind using third party open source jar then you can try gwtupload
For locale issues just ensure you are using UTF-8 and GWT locale cookies and locale settings.
For all the people who wants to manage files and pictures on the client side, before sending them to the server, as Manolo has recommended, I suggest lib-gwt-file.
For people working with PhoneGap, I suggest enconding the pictures using Base64, and decoding on the server side (see class Base64Util)

Calling remote Servlet from GWT

I am trying to call remote servlet from GWT, actually the GWT-RPC doesn't seems to work, so I am trying to do it using the RequestBuilder.
Here's the code snippet:
String url = "http://some-remote-host:8888/GWTJSTest/SomeServlet?name=" + textBox.getText();
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
// requestBuilder.setHeader("Origin", "*");
// requestBuilder.setHeader("Access-Control-Allow-Origin", "*");
try
{
requestBuilder.sendRequest(null, new RequestCallback()
{
public void onResponseReceived(Request request, Response response)
{
if (response.getStatusCode() == 200)
{
Window.alert(response.getText());
}else
{
Window.alert(response.getText() + " : " + response.getStatusCode() + response.getStatusText());
}
}
public void onError(Request arg0, Throwable arg1)
{
Window.alert(arg1.toString());
}
});
} catch (RequestException e)
{
Window.alert("CATCH BLOCK: " + e.getMessage());
e.printStackTrace();
}
Actually, IE8 returns the data but after a warning message, but Firefox doesn't! Why is this?
As you see, I am trying to set some request headers but no way.
If you're trying to make a request to your own server and port (the same one that your GWT page is on), replace the first line with:
String url = "/GWTJSTest/SomeServlet?name=" + textBox.getText();
If you're trying to talk to a different server, or a different port on your own server, the Same Origin Policy will keep you from doing that. You'll need to proxy it from your own server.
The remote servlet is the one that needs to set the CORS header that you have:
Access-Control-Allow-Origin: *
Alternatively you can specify just your own domain instead of * if you don't want other domains interacting with the remote servlet.
I've added :
<add-linker name="xs" /> to .gwt.xml
And then replaces GWT-PRC by JsonpRequestBuilder (transforming JSONP between the server and the client)

How do I add a SOAP Header using Java JAX-WS

A typical SOAP client request using JAX-WS might be
FooService service = new FooService();
FooPort port = service.getFooPort();
FooPayload payload = new FooPayload();
payload.setHatSize(3);
payload.setAlias("The Hat");
...
port.processRequest(payload);
This generates an HTTP request content something like
<?xml ... ?>
<S:Envelope xmlns:S="http://...soap-envelope">
<S:Body>
<!-- payload -->
</S:Body>
</S:Envelope>
By manipulating the arguments to the port.processRequest() call you can only affect the "payload" part. You can't affect the outer part of the XML message.
I want to insert a SOAP header just before the SOAP Body
<S:Header>
<X:Security xmlns:X="http://...wsssecurity...>
<X:BinarySecurityToken>kjh...897=</X:BinarySecurityToken>
</X:Security>
</S:Header>
How do I do that?
Thanks Nuno,
Just as soon as I work out how to log in properly to stackoverflow.com I'll do the right thing with your reply.
In the mean time here's the code I ended up with:
FooService service = new FooService();
service.setHandlerResolver(new HandlerResolver() {
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(new RGBSOAPHandler());
return handlerList;
}
});
FooPort port = service.getFooPort();
FooPayload payload = new FooPayload();
payload.setHatSize(3);
payload.setAlias("The Hat");
...
port.processRequest(payload);
and
class RGBSOAPHandler implements SOAPHandler<SOAPMessageContext> {
public Set<QName> getHeaders() {
return new TreeSet();
}
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
SOAPMessage message = context.getMessage();
try {
SOAPEnvelope envelope = context.getMessage()
.getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
String prefix = "X";
String uri = "http://...wsssecurity...";
SOAPElement securityElem =
factory.createElement("Security",prefix,uri);
SOAPElement tokenElem =
factory.createElement("BinarySecurityToken",prefix,uri);
tokenElem.addTextNode("kjh...897=");
securityElem.addChildElement(tokenElem);
SOAPHeader header = envelope.addHeader();
header.addChildElement(securityElem);
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
} else {
// inbound
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void close(MessageContext context) {
//
}
}
you might want to look at handlers and handler chains.- I recently had to add a cookie to a given Webservice call and that was how i did it, just created a handler that intercepted the initial call and injected the cookie, you can also manipulate the call headers with a Pivot Handler
for add Soap header, if you implement the WS on the web application server, the Was will add security part at header , after you have configure as per WS-SECURITY standard , such as web-policy etc. I don't understand why need add yourself except the encrypted content part , such as encrypted password etc