Handling wrong URLs - java

I have csv files stored on my server. If I enter the right key (which is a part of URL) I get what I want, but if the entered key was wrong my app crashes. I want to be able to catch the error.
String url="http://mysite.com/template";
url=url+et.getText().toString().toLowerCase()+".csv";
csv.setURL(url);
if(csv.checkURL()){
enterToDB();
}
else{
tv.setText("Wrong key");
}
and my CSVReader looks like:
public void setURL(String file){
try {
URL url = new URL(file);
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
success=true;
in.close();
} catch (MalformedURLException e) { success=false;} catch (IOException e) { success=false; }
}
public boolean checkURL(){
return success;
}
}

Without the complete minimal code necessary to replicate the problem, and without information about what the specific error is (like a stack trace), I'm just guessing that the setURL/checkURL routines don't exactly do what you want. They appear to assume that openStream will throw an exception if the key in the URL is wrong, but that's not the case. Even if the path or key in the URL is wrong, the HTTP server will still provide a response. The response might not be 200 OK and the response body might not include what you're looking for, but it'll still give a response, and the open stream can be used to read the response.
So, if I understand correctly, you actually want to inspect the contents of the response (including probably the HTTP status code), before deciding whether the "success" is true or false.
Thane posted some code I gave him over in How should I handle server timeouts and error code responses to an http post in Android App?. I recommend reviewing it and seeing if it provides the structure you need to handle successful and failed responses accordingly.
Making sense?

Related

Issues while File download

I'm trying to download a file from Angular UI, even after I got exception in backed code still I'm getting 200 ok as the response.
Here is the code I have :
public ResponseEntity<Object> downloadDocument(#PathVariable("docId") Long docId,
HttpServletResponse response) {
OutPutStream outputStream = null;
try {
outputStream = response.getOutputStream();
docService.downloadDocument(docId,outputStream);
return ResponseEntity.ok().contentType(MediaType.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.body("Success");
} catch(Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
} finally {
if (Objects.nonNull(outputStream)) {
IOUtils.closeQuietly(outputStream);
}
}
Can you please help me out what's wring here.
When you open the outputstream, the headers are sent. period.
There's no backtracking from that point on. You can't first open the outputstream and then later go: Oh, wait! No! nevermind! bad request!
Here's how it works - you pick a side and stick to it. You can either:
Handle it yourself; use response and the methods available there to set up your response; you can set headers, the return code and message, and you can obtain an outputstream for the response body, and send data that way. If you do this, you can't ALSO return a ResponseEntity!
Do NOT even add an HttpServletResponse parameter, and instead return a ResponseEntity object.
You're doing both, which is not allowed.
I'm frankly surprised; spring is a bit broken and ought to be throwing exceptions here, as it cannot possibly serve up what you're asking it to do here.
NB: Note that the type of an exception is usually more informative than the message (many exceptions don't even have a message).
Putting it all together:
public ResponseEntity<?> downloadDocument(#PathVariable("docId") Long docId) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
docService.downloadDocument(docId, baos);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.body(baos.toByteArray());
} catch(Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
'toString' defaults to printing its own type if there is no message, and its own type plus the message if there is, so you get e.g.:
NullPointerException
or
NullPointerException: parameter foo
which is what you want (versus a literal blank string in the former case, and just 'parameter foo' in the latter, which isn't particularly insightful either).
messages are generally intended not to necessarily make sense without the context of the type of the exception.
NB: This will cache the entire content of the downloaded document into the memory of the server before sending it onwards. If the document is large, this is a bad idea, but, if you want to 'stream' it, you have a pretty serious problem inherent in the HTTP protocol: Once you start sending, you've already sent the 'error status' (i.e. you already sent 200 OK), so if the document download process throws an exception halfway through, you cannot go back and send an error message. You need some sort of wire protocol where you send the body in chunks and have a 'type' code you send, so that the recipient can scan for these and knows 'this type means there's more data flowing', and 'this type means an error occured and now I can read the error description'. That all gets quite complicated. By caching it, you avoid this mess. But if that document can be very large you're going to have to deal with the above, there are no quick fixes.

Sending a zero-length HTTPS PUT?

I'm working with a system that, in order to make a particular service call, requires the following:
Issue an HTTP PUT command
Set the URL to some_url_here
Set the end user certificate.
Ensure that the entity body is empty and set the Content-Length headers to 0.
Here's the method I wrote to build secure connections. I've tested the GETs; they work fine. I know the problem isn't in the certificate.
public HttpsURLConnection getSecureConnection(final URL url, final String method, final int connectTimeout,
final int readTimeout) throws IOException {
Validate.notNull(sslContext);
Validate.notNull(url);
Validate.notNull(method);
Validate.isTrue(connectTimeout > 0);
Validate.isTrue(readTimeout > 0);
HttpsURLConnection connection;
try {
connection = (HttpsURLConnection) url.openConnection();
} catch (final IOException ioe) {
LOGGER.error("[CertificateLoader] Unable to open URL connection!", ioe);
throw new IOException("Unable to open URL connection!", ioe);
}
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setRequestMethod(method);
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
connection.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
if (method.equals("PUT")) {
connection.setRequestProperty("Content-Length", "0");
}
if (connection.getContentLength() > 0) {
Object foo = connection.getContent();
LOGGER.error("This is what's in here: " + foo.toString());
}
return connection;
}
Now, the reason for that funky if at the bottom is that when I go to make the PUT call, even though I'm not putting a body on the call directly, my logs insist I'm getting a non-zero content length. So, I added that little block to try to figure out what's in there, and lo and behold it reports the following:
This is what's in here: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#70972170
Now, that sucker's in there by default. I didn't put it in there. I didn't create that object to put in there. I just created the object as is from the URL, which I created from a String elsewhere. What I need is a way to remove that HttpInputStream object, or set it to null, or otherwise tell the code that there should be no body to this PUT request, so that my server won't reject my message as being ill-formatted. Suggestions?
Now, the reason for that funky if at the bottom is that when I go to make the PUT call, even though I'm not putting a body on the call directly, my logs insist I'm getting a non-zero content length.
The way to set a zero Content-length is as follows:
connection.setDoOutput(true); // if it's PUT or POST
connection.setRequestMethod(method);
connection.getOutputStream().close(); // send a zero length request body
It is never necessary to call connection.setRequestProperty("Content-Length", "0"). Java sets it for you. Or possibly it is omitted, in which case you may be able to ensure it via
connection.setFixedLengthStreamingMode(0);
So, I added that little block to try to figure out what's in there, and lo and behold it reports the following:
This is what's in here: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream#70972170
Now, that sucker's in there by default. I didn't put it in there.
Java put it there.
I didn't create that object to put in there.
Java put it there.
I just created the object as is from the URL, which I created from a String elsewhere. What I need is a way to remove that HttpInputStream object, or set it to null, or otherwise tell the code that there should be no body to this PUT request, so that my server won't reject my message as being ill-formatted.
No it isn't. It is an input stream, not a piece of content. And it is an input stream to the content of the response, not of the request. And in any case, the server is perfectly entitled to return you content in response to your request.
Your task is to:
Get the response code and log it.
If it is >=200 and <= 299, get the connection's input stream.
Otherwise get the connection's error stream.
Whichever stream you got, read it till end of stream, and log it.
That will tell you what is really happening.
I will add that a PUT without a body is a really strange thing to do. Are you sure you've understood the requirement? 411 means Length required.

Send an error to appengine endpoint client

In my GAE endpoint, in my user register API method, I check whether a user already exists with the given username. If a user already exists, I need to send an error to the endpoint client.
Currently I throw an exception like below from endpoint.
User user = ofy().load().key(Key.create(User.class, username)).now();
if (user != null) {
throw new BadRequestException("Username already exists");
}
Then in endpoint client, I catch the exception like below.
try {
gaeEndpoint.registerUser(mEmail, mPassword).execute();
} catch (HttpResponseException e) {
mErrorMessage = e.getContent();
} catch (IOException e) {
mErrorMessage = "Failed to communicate with server. Please check your internet connection.";
}
When endpoint throws a BadRequestException, client gets HttpResponseException e and e.getContent() contains a json string including the error message string sent from endpoint. I need to parse json to get the actual error message sent from server.
Even though this works, I feel that there should be a better way to send an error message to the client from the endpoint. So, does anyone know a better (or a recommended) way of doing this?
I think you're doing everything right.
HTTP is designed to send a response with 4xx code and meaningful content to a user. It can be HTML for a browser, JSON for an api client or anything that can provide some context to the client.
If you're worried about using exceptions, that's fine too. Of course, you can prepare a response with a manually set status code, but that doesn't change anything, it's just more code. It's also less likely that you'll create a bug when you're using exceptions in this case.

Android: App working on WiFi but neither on 3G nor 2G

I desperately need help with this problem. I know there are already questions on these lines but none of them are quite like the issue I'm facing at the moment.
I have an app which pulls the follow JSON data from a URL:
{
"dateToday" = "17th May"
}
The code to retrieve that data is as follows:
protected List<String> doInBackground(List<String>... arg0) {
Log.d("Refresh Check","In background...");
// TODO Auto-generated method stub
List<String> matchDetails = new ArrayList<String>();
try {
Log.d("Refresh Check","In try...");
json = jsonData.retrieveData(URL, client);
Log.d("Refresh Check",json.toString());
String date = json.getString("dateToday");
matchDetails.add(date);
return matchDetails;
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Arrays.asList("Connectivity issue");
}
And the retrieve data function is:
public JSONObject retrieveData(String URL, HttpClient client)
throws ClientProtocolException, IOException, JSONException {
StringBuilder url = new StringBuilder(URL);
HttpGet get = new HttpGet(url.toString());
HttpResponse r = client.execute(get);
int status = r.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity e = r.getEntity();
String data = EntityUtils.toString(e);
JSONObject last = new JSONObject(data);
return last;
} else {
return null;
}
}
So when I run this on my phone using WiFi, everything works and gets updated perfectly but when I switch to my data plan on 3G or 2G, the data just doesn't refresh at all.
The 3rd Log.d in my code, which checks for the String, keeps returning the date as 17th May even if I change the JSON value to 18th.
The only difference in the logcat I could note for WiFi versus my data plan was that the data plan had an error on the lines of Network Controller: iconLevel >= 5
Here's where it gets annoying though, there are times when the app works completely fine even on 2G, it's like it won't work for a while, then say I switch from 2G to WiFi and then back to 2G, it works. The behaviour of the app has been very erratic.
Things I've tried so far:
1.) Use another phone to see if the issue persists (it did).
2.) Check if my phone browser could access the URL and display the updated results (it could).
So I am now clueless as hell and really close to giving up on the app after 3 weeks of unexplained execution.
If anyone could help, I'd be very very very grateful. This is what I have now narrowed to:
1.) Why precisely would getting light-weight data from a particular URL behave differently on WiFi and on 3G/2G
2.) Is android saving the data to cache or something? Because every time I start the app, it should reach the doInBackground wherein, it should retrieve the updated data from the site but the log simply shows the old data.
3.) Would it help if I tried loading the apk separately onto my device rather than running it on my phone through eclipse? I mean, is there any difference between the two?
Any help would be extremely appreciate. Thanks in advance.
it's probably the caching when you request json data from server.
You can try to add a timestamp string behind your url when you requesting data from server to ensure ever request is a new request to avoid caching
for example :
Original Url : mytest.com/webservice/?date=today
add extra parameter : mytest.com/webservice/?date=today&timestamp=ddmmyyhhmmss
You obviously have a cache problem here. As everything is running fine on wifi, I think the issue probably comes from a cache in your network provider infrastructure. Your request is a GET, it could easily get cached by any element on the path. to work around this you can
configure the HTTP server to add a cache control headers (header Cache-Control with no-cache or max-age or header expire ) to the answer to your request (how to do this depends on the http server you use). Actually, it can be a bit more tricky than that, see this section on wikipedia avoid avoiding caching.
add a random parameter to your url (as suggested by #Darkangle)
I apologize for the delay in getting back about this.
Darkangle's suggestion seems to be spot on.
However, I had checked through the cache issue on stackoverflow and found a solution which worked well for me.
This was the change to the code:
StringBuilder url = new StringBuilder(URL);
HttpGet get = new HttpGet(url.toString());
get.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
get.setHeader("Pragma", "no-cache"); // HTTP 1.0.
HttpResponse response = client.execute(get);
The get.setHeaders which made sure new data is fetched every time.
Thanks to everyone who helped.

URLConnection FileNotFoundException for non-standard HTTP port sources

I was trying to use the Apache Ant Get task to get a list of WSDLs generated by another team in our company. They have them hosted on a weblogic 9.x server on http://....com:7925/services/. I am able to get to the page through a browser, but the get task gives me a FileNotFoundException when trying to copy the page to a local file to parse. I was still able to get (using the ant task) a URL without the non-standard port 80 for HTTP.
I looked through the Ant source code, and narrowed the error down to the URLConnection. It seems as though the URLConnection doesn't recognize the data is HTTP traffic, since it isn't on the standard port, even though the protocol is specified as HTTP. I sniffed the traffic using WireShark and the page loads correctly across the wire, but still gets the FileNotFoundException.
Here's an example where you will see the error (with the URL changed to protect the innocent). The error is thrown on connection.getInputStream();
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class TestGet {
private static URL source;
public static void main(String[] args) {
doGet();
}
public static void doGet() {
try {
source = new URL("http", "test.com", 7925,
"/services/index.html");
URLConnection connection = source.openConnection();
connection.connect();
InputStream is = connection.getInputStream();
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
The response to my HTTP request returned with a status code 404, which resulted in a FileNotFoundException when I called getInputStream(). I still wanted to read the response body, so I had to use a different method: HttpURLConnection#getErrorStream().
Here's a JavaDoc snippet of getErrorStream():
Returns the error stream if the
connection failed but the server sent
useful data nonetheless. The typical
example is when an HTTP server
responds with a 404, which will cause
a FileNotFoundException to be thrown
in connect, but the server sent an
HTML help page with suggestions as to
what to do.
Usage example:
public static String httpGet(String url) {
HttpURLConnection con = null;
InputStream is = null;
try {
con = (HttpURLConnection) new URL(url).openConnection();
con.connect();
//4xx: client error, 5xx: server error. See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
boolean isError = con.getResponseCode() >= 400;
//In HTTP error cases, HttpURLConnection only gives you the input stream via #getErrorStream().
is = isError ? con.getErrorStream() : con.getInputStream();
String contentEncoding = con.getContentEncoding() != null ? con.getContentEncoding() : "UTF-8";
return IOUtils.toString(is, contentEncoding); //Apache Commons IO
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
//Note: Closing the InputStream manually may be unnecessary, depending on the implementation of HttpURLConnection#disconnect(). Sun/Oracle's implementation does close it for you in said method.
if (is != null) {
try {
is.close();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
if (con != null) {
con.disconnect();
}
}
}
This is an old thread, but I had a similar problem and found a solution that is not listed here.
I was receiving the page fine in the browser, but got a 404 when I tried to access it via the HttpURLConnection. The URL I was trying to access contained a port number. When I tried it without the port number I successfully got a dummy page through the HttpURLConnection. So it seemed the non-standard port was the problem.
I started thinking the access was restricted, and in a sense it was. My solution was that I needed to tell the server the User-Agent and I also specify the file types I expect. I am trying to read a .json file, so I thought the file type might be a necessary specification as well.
I added these lines and it finally worked:
httpConnection.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
httpConnection.setRequestProperty("Accept","*/*");
check the response code being returned by the server
I know this is an old thread but I found a solution not listed anywhere here.
I was trying to pull data in json format from a J2EE servlet on port 8080 but was receiving the file not found error. I was able to pull this same json data from a php server running on port 80.
It turns out that in the servlet, I needed to change doGet to doPost.
Hope this helps somebody.
You could use OkHttp:
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
I've tried that locally - using the code provided - and I don't get a FileNotFoundException except when the server returns a status 404 response.
Are you sure that you're connecting to the webserver you intend to be connecting to? Is there any chance you're connecting to a different webserver? (I note that the port number in the code doesn't match the port number in the link)
I have run into a similar issue but the reason seems to be different, here is the exception trace:
java.io.FileNotFoundException: http://myhost1:8081/test/api?wait=1
at sun.reflect.GeneratedConstructorAccessor2.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at sun.net.www.protocol.http.HttpURLConnection$6.run(HttpURLConnection.java:1491)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.protocol.http.HttpURLConnection.getChainedException(HttpURLConnection.java:1485)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1139)
at com.doitnext.loadmonger.HttpExecution.getBody(HttpExecution.java:85)
at com.doitnext.loadmonger.HttpExecution.execute(HttpExecution.java:214)
at com.doitnext.loadmonger.ClientWorker.run(ClientWorker.java:126)
at java.lang.Thread.run(Thread.java:680)
Caused by: java.io.FileNotFoundException: http://myhost1:8081/test/api?wait=1
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1434)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:379)
at com.doitnext.loadmonger.HttpExecution.execute(HttpExecution.java:166)
... 2 more
So it would seem that just getting the response code will cause the URL connection to callGetInputStream.
I know this is an old thread but just noticed something on this one so thought I will just put it out there.
Like Jessica mentioned, this exception is thrown when using non-standard port.
It only seems to happen when using DNS though. If I use IP number I can specify the port number and everything works fine.

Categories