on 06-15-2012 2:26 PM
I am creating an Android app (for Android 4.0 - API level 15) to consume data from a SAP NetWeaver Gateway 2.0 through an odata channel.
I used the Eclipse plugin for Java (http://www.sdn.sap.com/irj/scn/downloads?rid=/webcontent/uuid/b09d414f-f227-2f10-bdbf-ba31c844b432) to generate proxy classes for the entities in the gateway service.
When I try to use the proxy class to GET an a collection from the gateway, the call fails in the "getResponseString(String url)" method of the proxy class. More specifically, the line
IRestResponse response = client.execute(request);
throws a RestClientException which, in turn, triggers a ProxyException.
The RestClientException prints the request URL is called. Using the RESTClient plugin in Mozilla Firefox, the exact same URL returns the wanted collection XML data.
In addition, the triggered ProxyException prints the message:
System expected the element '{&EXPECTED_NAMESPACE&}&EXPECTED_NAME&'
I have tried using the DefaultHttpClient class to manually issue a GET request to the same gateway requesting the same data. Since this method works, I assume that the gateway connection from within the Android emulator works fine. As this method would require a lot of manual work that has to be done for each Android application individually, I would prefer not having to resort to it.
I have been stuck with this problem for a while now and couldn't find a solution so far, so I would very much appreciate any input that might take me closer to a solution to this.
Thanks!
Sebastian
P.S.: Using Wireshark, I found out that the application sends a POST request, although I am specifically setting GET as the method for the request (request.setMethod(Method.GET)). Could a POST request with an empty payload cause an error message like this? And how do I get the request to actually use GET?
I finally found the problem. I tested the same code within a plain Java command line programm and it worked with no errors. In this case, the application actally sends out a GET request (proof by Wireshark) and the call works like a charm.
Knowing this, I started looking around for a specific Android problem involving GET and POST and pretty soon found this: http://webdiary.com/2011/12/14/ics-get-post/
So, apparently they changed something in the java.net.HttpURLConnection implementation in Android 4.0 which results in every GET request being sent out as a POST request. Using the SAP Gateway library for Java, I don't have control over the instance of the HttpURLConnection instance, so I cannot use the fix recommended in the article above.
It seems that my only option, right now, is to wait until an Android-compatible version of the SAP Gateway library comes out which takes this issue into account. Until then, I will use the Android API level 12 which doesn't come with this problem.
If you have a nicer solution, please let me know!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I am happy to say that we have released a new Android Toolkit as part of the SAP NetWeaver Gateway plug-in for Eclipse 2.5.200, you can find it here!
However, the new toolkit uses SAP OData Mobile SDK 2.1.3 which currently supports Android 2.2 - 3.2 (API level 8 - 13).
might be able to suggest a better solution of how you can still manipulate the HttpURLConnection instance.
Just a last quick update on this topic...
I just changed the finished application to use the current Eclipse plugin version (2.5.200) which officially supports Android. Although it doesn't officially support Android 4.0, it runs without any modifications like the ones described above. It probably uses another HttpClient class for sending the requests.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
I can suggest as a workaround to implement a new class which will extend the class
com.sap.nw.gateway.odata.client.connectivity.impl.DefaultRestClient
In this class you should replace the send method with your implementation.
If needed I can provide the DefaultRestClient code for this method so you will have a base code which you can modify.
Regards,
Boris
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
public IRestResponse send(IRestRequest restRequest) throws RestClientException | ||
{ | ||
String urlString = getUrl(restRequest.getUrl()); |
HttpURLConnection httpURLConnection = null; | ||
OutputStreamWriter outputStreamWriter = null; | ||
OutputStream outputStream = null; | ||
InputStream inputStream = null; |
try | ||
{ |
httpURLConnection = (HttpURLConnection) new URL(urlString).openConnection(); |
if (this.serverConnectionParameters.useHttps()) | ||||||
{ | ||||||
if (this.sslSocketFactory != null) | ||||||
{ | ||||||
if (httpURLConnection instanceof HttpsURLConnection) | ||||||
{ | ||||||
((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(this.sslSocketFactory); | ||||||
} | ||||||
} | ||||||
} |
httpURLConnection.setRequestMethod(restRequest.getMethod().toString()); |
httpURLConnection.setDoOutput(true); |
httpURLConnection.setDoInput(true); |
if (!cookies.isEmpty()) | |||||
{ | |||||
StringBuilder cookies = new StringBuilder(); | |||||
for (Map.Entry<String, String> entry : this.cookies.entrySet()) | |||||
{ | |||||
cookies.append(entry.getKey()).append("=").append(entry.getValue()).append(";"); | |||||
} | |||||
cookies.deleteCharAt(cookies.lastIndexOf(";")); | |||||
httpURLConnection.setRequestProperty(ConnectivityConstants.COOKIE, cookies.toString()); |
} |
// add additional headers to request | |||
addHeadersToRequest(httpURLConnection, restRequest.getHeaders()); |
if (restRequest.getBody() != null) | ||||||
{ | ||||||
httpURLConnection.setRequestProperty(ConnectivityConstants.CONTENT_LENGTH, | ||||||
Integer.toString(restRequest.getBody().length())); | ||||||
} |
if (restRequest.getBody() != null) | ||||
{ | ||||
outputStream = httpURLConnection.getOutputStream(); | ||||
outputStreamWriter = new OutputStreamWriter(outputStream, UTF_8); | ||||
outputStreamWriter.write(restRequest.getBody()); | ||||
outputStreamWriter.close(); // $JL-RESOURCE$ | ||||
} |
IRestResponse restResponse; |
try | ||||
{ | ||||
inputStream = httpURLConnection.getInputStream(); | ||||
restResponse = new RestResponse(); |
} | ||||
catch (IOException e) | ||||
{ | ||||
inputStream = httpURLConnection.getErrorStream(); | ||||
restResponse = new RestClientException(e.getMessage()); | ||||
} |
restResponse.setStatusCode(httpURLConnection.getResponseCode()); |
restResponse.setBody(Util.convertStreamToByteArray(inputStream)); // restResponse.setBody(Util.convertStreamToString(inputStream)); |
Headers headers = new Headers(); |
for (int i = 0;; i++) | ||||
{ | ||||
String headerName = httpURLConnection.getHeaderFieldKey(i); | ||||
String headerValue = httpURLConnection.getHeaderField(i); |
headers.put(headerName, headerValue); |
if (headerName == null && headerValue == null) | |||||
{ | |||||
// No more headers | |||||
break; | |||||
} | |||||
} |
restResponse.setHeaders(headers); |
httpURLConnection.disconnect(); |
return restResponse; | |||||
} | |||||
catch (MalformedURLException e) | |||||
{ | |||||
LOGGER.severe(e.getMessage()); | |||||
throw new RestClientException(e.getMessage(), e); | |||||
} | |||||
catch (ProtocolException e) | |||||
{ | |||||
LOGGER.severe(e.getMessage()); | |||||
throw new RestClientException(e.getMessage(), e); | |||||
} | |||||
catch (IOException e) | |||||
{ | |||||
LOGGER.severe(e.getMessage()); | |||||
throw new RestClientException(e.getMessage(), e); | |||||
} | |||||
finally | |||||
{ | |||||
if (outputStreamWriter != null) | |||||
{ | |||||
try | |||||
{ | |||||
outputStreamWriter.close(); | |||||
} | |||||
catch (IOException e) | |||||
{ | |||||
LOGGER.severe(e.getMessage()); | |||||
throw new RestClientException(e.getMessage(), e); | |||||
} | |||||
} | |||||
} | |||||
} |
Thank you very much for the code! I just wanted to try it out, but I am stuck with the calls to the methods "getUrl(String)" and "addHeadersToRequest(HttpURLConnection, Headers)".
I guess, I could replace "String urlString = getUrl(restRequest.getUrl());" with "String urlString = restRequest.getUrl();" assuming that nothing else happens in the getUrl()-Method of DefaultRestClient...
But what about the second method? Could you please post the code for both methods, too? Or alternatively tell me what extra work needs to be done there?
Thanks again for your great support!
Here it is:
private String getUrl(String urlString) throws RestClientException | ||||
{ | ||||
urlString = urlString.toLowerCase(Locale.ENGLISH).startsWith("http") ? urlString : baseUrl + urlString; | ||||
if (this.serverConnectionParameters.getClient() != null | ||||
&& !(this.serverConnectionParameters.getClient().trim().length() == 0)) | ||||
{ | ||||
urlString = Util.addParameter(urlString, "sap-client", this.serverConnectionParameters.getClient());//$NON-NLS-1$ //$NON-NLS-2$ | ||||
} | ||||
urlString = urlString.replace(" ", "%20"); | ||||
return urlString; | ||||
} |
private void addHeadersToRequest(HttpURLConnection httpURLConnection, Headers headers) | |||
{ | |||
if (headers.isEmpty()) | |||
return; |
for (Entry<String, List<String>> entry : headers.entrySet()) | ||||
{ | ||||
for (String value : entry.getValue()) | ||||
{ | ||||
httpURLConnection.addRequestProperty(entry.getKey(), value); | ||||
} | ||||
} | ||||
} |
Thank you again, Boris!
I only had to make a minor modification to the "getUrl()" method because the field "baseUrl" isn't visibile to subclasses of DefaultRestClient. I put together the string by using the values of the field IServerConnectionParameters serverConnectionParameters.
The crucial modification for making the client send a GET request in Android 4.0 has to be made in the send() method, where the line
httpURLConnection.setDoOutput(true);
has to be replaced with
httpURLConnection.setDoOutput(false);
in case of a GET request.
Thank you so much for your help, this enables my application to run on Android 4.0 devices. In addition, the modifcations do not seem to have a negative effect on the application when running on a 3.1 device.
User | Count |
---|---|
87 | |
10 | |
10 | |
10 | |
7 | |
6 | |
6 | |
5 | |
5 | |
4 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.