I have the following method which uses a proxy to retrieve information from a server. Sometimes due to a bad proxy I get SocketException, SSLException, SSLHandshakeException or ConnectException
As you can see in my method I am already using catch (IOException ioe) I need to do that in order to get the contents of server response if the server returns anything other than code 200.
How can I make the method retry in case of the above exceptions?
public String getMeta() throws IOException
{
HttpsURLConnection con = null;
InputStream is = null;
StringWriter writer = new StringWriter();
try
{
String url = "https://api.myapp.com/meta";
URL urlObj = new URL(url);
if (useProxy && fullProxy)
{
myapp.Proxy proxyCustom = getRandomProxy();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyCustom.getProxyIp(), proxyCustom.getProxyPort()));
con = (HttpsURLConnection) urlObj.openConnection(proxy);
}
else
{
con = (HttpsURLConnection) urlObj.openConnection();
}
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", USER_AGENT);
con.setRequestProperty("Content-Type", "application/json; charset=utf-8");
con.setRequestProperty("host", urlObj.getHost());
con.setRequestProperty("Connection", "Keep-Alive");
int responseCode = 0;
responseCode = con.getResponseCode();
is = con.getInputStream();
writer = new StringWriter();
IOUtils.copy(is, writer, "UTF-8");
}
catch (IOException ioe)
{
if (con instanceof HttpsURLConnection)
{
HttpsURLConnection httpConn = (HttpsURLConnection) con;
int statusCode = httpConn.getResponseCode();
if (statusCode != 200)
{
is = httpConn.getErrorStream();
writer = new StringWriter();
IOUtils.copy(is, writer, "UTF-8");
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return writer.toString();
}
How can I make the method retry in case of the above exceptions?
One way shown below is to have the getMeta method actually throw the IOException. You can then then have a caller method recursively call itself due to any caught Exception.
I hope there is an simple way of setting how times it should retry
To be able to call the method n number of times, pass in the number of times as an argument and deal with the stopping criteria logic accordingly. For example:
public String caller(int total) throws IOException{
return callerImpl(1, total);
}
public String callerImpl(int current, int total) throws IOException{
try{
return getMeta();
}catch(IOException e){
current++;
if ( current > total ){
throw e;//or return null or empty string, depending upon upstream requirements
}
return callerImpl(current, total);
}
return null;
}
In getMeta:
try{
....
}catch(IOException io){
//log or handle io
throw io;
}
Note the above doesn't deal with the logging/logic of the thrown exceptions, which you may want to handle in some way.
The way I would do is create a new retry method and invoke it at the end of your getMeta() method so it is called in only in case of abnormalities
I am assuming you should call the retry only a few times and hence getMeta should throw some exception like RetryException after certain number of times
public String getMeta() throws IOException, RetryException
{
HttpsURLConnection con = null;
InputStream is = null;
StringWriter writer = new StringWriter();
try
{
String url = "https://api.myapp.com/meta";
URL urlObj = new URL(url);
if (useProxy && fullProxy)
{
myapp.Proxy proxyCustom = getRandomProxy();
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyCustom.getProxyIp(), proxyCustom.getProxyPort()));
con = (HttpsURLConnection) urlObj.openConnection(proxy);
}
else
{
con = (HttpsURLConnection) urlObj.openConnection();
}
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", USER_AGENT);
con.setRequestProperty("Content-Type", "application/json; charset=utf-8");
con.setRequestProperty("host", urlObj.getHost());
con.setRequestProperty("Connection", "Keep-Alive");
int responseCode = 0;
responseCode = con.getResponseCode();
is = con.getInputStream();
writer = new StringWriter();
IOUtils.copy(is, writer, "UTF-8");
return writer.toString();
}
catch (IOException ioe)
{
if (con instanceof HttpsURLConnection)
{
HttpsURLConnection httpConn = (HttpsURLConnection) con;
int statusCode = httpConn.getResponseCode();
if (statusCode != 200)
{
is = httpConn.getErrorStream();
writer = new StringWriter();
IOUtils.copy(is, writer, "UTF-8");
return writer.toString();
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return retryGetMeta();
}
private String retryGetMeta()
{
try
{
return getMeta();
}
finally
{
//Do your stuff
}
}
public RetryException extends Exception
{
private String message = "Retries exceeded";
public String getMessage()
{
return message;
}
}
If an exception is thrown in your getMeta(), catch will do its stuff as you are doing now, but after that , retryGetMeta will be called
Related
I can't understand how to switch to POST method in my HttpsURLConnection. On the debugger the request method is GET also after the setRequestMethod method. Can you tell me where is my mistake?
try {
URL url=new URL("https://smartmates.herokuapp.com");
HttpsURLConnection connection= (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
//I'll add some params here
connection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Thank you very much.
This is a piece of code that I use to POST the String mensaje and receiving rta.ToString(). I think that DoSetInput(true) is a mistake, because you want to send a POST (output) and, eventually, get a response.
`
String urlParametros = "<?xml version=\"1.0\"?>";
urlParametros = urlParametros + mensaje;
byte[] postDatos = urlParametros.getBytes(StandardCharsets.UTF_8);
try {
URL miurl = new URL(url);
con = (HttpURLConnection) miurl.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
//******…………..
con.setRequestProperty("User-Agent", "Java client");
con.setRequestProperty("Content-Type", "text/xml");
try (DataOutputStream datos = new DataOutputStream(con.getOutputStream())) {
datos.write(postDatos);
}
StringBuilder rta;
try (BufferedReader entrada = new BufferedReader(
new InputStreamReader(con.getInputStream()))) {
String linea;
rta = new StringBuilder();
while ((linea = entrada.readLine()) != null) {
rta.append(linea);
rta.append(System.lineSeparator());
}
}
return rta.toString();
} finally {
con.disconnect();
}´
Hope it helps
Daniel
I'm trying to determine what the current readTimeout and connectionTimeout is on my Java HttpURLConnection object. They are both returning a -1 with during the logging statements below:
private InputStream getSomethingImportant(final String letterId, final String documentId,
HttpURLConnection connection) throws IOException {
InputStream pdfStream = null;
final String url = this.getBaseURL() + "/letters/" + letterId + "/documents/" + documentId;
connection = RequestResponseUtil.initializeRequest(url, "GET", this.getAuthenticationHeader(), true, MediaType.APPLICATION_PDF_VALUE);
LOG.info("ConnectionTimeout is: {}", connection.getConnectTimeout());
LOG.info("ReadTimeout is: {}", connection.getReadTimeout());
// ...other non-relevant code...
}
public static HttpURLConnection initializeRequest(final String url, final String method,
final String httpAuthHeader, final boolean multiPartFormData, final String responseType) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod(method);
conn.setRequestProperty("X-Something-Authentication", httpAuthHeader);
conn.setRequestProperty("Accept", responseType);
if (multiPartFormData) {
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=BOUNDARY");
conn.setDoOutput(true);
}
else {
conn.setRequestProperty("Content-Type", "application/xml");
}
}
catch (final MalformedURLException e) {
throw new CustomException(e);
}
catch (final IOException e) {
throw new CustomException(e);
}
return conn;
}
The JavaDocs on getConnectTimeout and getReadTimeout both list 0 as a return option but say nothing about -1. How should I interpret this?
Also, the url I am using is valid and I am returning an InputStream successfully.
Finally, I am using Oracle JDK 1.8.0_77. And, of note, when I actually print out the conn class that is being used at runtime it is weblogic.net.http.SOAPHttpsURLConnection (I am using WebLogic 12.2).
Thank you.
I get an EOFException from getInputStream in the following Android Code.
private void downloadUrl() {
HttpsURLConnection conn = null;
DataOutputStream printout = null;
try {
try {
conn = (HttpsURLConnection) new URL("some url").openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setAllowUserInteraction(false);
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
}
catch (ProtocolException e){
}
JSONObject jsonReq = new JSONObject();
jsonReq.put("userID", "id");
jsonReq.put("password", "1234");
OutputStream output = conn.getOutputStream();
try {
OutputStreamWriter wr = new OutputStreamWriter(output);
wr.write(URLEncoder.encode(jsonReq.toString(),"utf-8"));
wr.flush();
wr.close();
}
catch (IOException e) {
}
InputStream in = conn.getInputStream(); //I get EOFException here
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(in));
String responseSingle = null;
while ((responseSingle = rd.readLine()) != null) {
response = response + responseSingle;
}
rd.close();
}
catch (IOException e) {
}
finally {
if (in != null)
in.close();
}
}
catch (IOException e){
}
catch (JSONException e) {
}
finally{
if(conn!=null)
conn.disconnect();
}
}
What is causing this Exception?
public class EOFException extends IOException:
Signals that an end of file or end of stream has been reached unexpectedly during input.
http://docs.oracle.com/javase/7/docs/api/java/io/EOFException.html
You are calling conn.getOutputStream(); and then calling conn.getInputStream(); without resetting. You are already at the end of the file.
In the "doInBackground" function of a AsyncTask, I got the following piece of code :
try
{
URL url = new URL(myUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
Authenticator.setDefault(new Authenticator(){
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication ("myUsername", "myPassword".toCharArray());
}
});
conn.connect();
int response = conn.getResponseCode();
inputStream = conn.getInputStream();
String content = convertInputStreamToString(inputStream);
return content;
catch (Exception e) {
e.printStackTrace();
return null;
}
When the credentials are ok, everything works fine and the ResponseCode is 200. But if I put the wrong credentials, getResponseCode() makes the AsyncTask wait for an answer indefinitely (timeout won't work). Looking at HttpUrlConnection object tells me that the ResponseCode is -1.
I need to handle every situation, even if the user provides a bad credential. How can I get an usable answer? Should I use another class than HttpUrlConnection?
Are you returning String back from this function and are you handling exceptions? Did you write onPostExecute?
Here is the code that works perfectly for me:
URL url = new URL(strUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Host", "myhost.com");
conn.setRequestProperty("Authorization", "Basic " + Base64.encodeToString(toencode, Base64.DEFAULT));
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; PPC; en-US; rv:1.3.1)");
conn.setRequestProperty("Accept-Charset", "UTF-8");
conn.setConnectTimeout (5000) ;
conn.setDoOutput(true);
conn.setDoInput(true);
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
result = Utilities.readStream(in);
status_code = conn.getResponseCode();
return result;
} catch (MalformedURLException e) {
return e.getMessage();
} catch (ProtocolException e) {
try {
status_code = conn.getResponseCode();
} catch (IOException e1) {
status_code = -1;
}
return e.getMessage();
} catch (IOException e) {
try {
status_code = conn.getResponseCode();
} catch (IOException e1) {
status_code = -1;
}
return e.getMessage();
} catch ( Exception e ) {
try {
status_code = conn.getResponseCode();
} catch (IOException e1) {
status_code = -1;
}
return e.getMessage();
}
finally
{
conn = null;
}
I try to send post request, but webserver returns that I added no post-values. I spent a lot of time trying to solve this issue, but no result. Here is the code:
public static String post(String url, String postParams)
{
URLConnection connection = null;
try
{
connection = initializeConnection(url);
connection.setDoOutput(true);
((HttpURLConnection) connection).setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/xml");
connection.setRequestProperty("Accept", "text/xml");
connection.setUseCaches(false);
connection.setDoInput(true);
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write(postParams.getBytes());
wr.flush();
wr.close();
// Get Response
InputStream is = connection.getInputStream();
return inputStreamToString(is);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
protected static HttpURLConnection initializeConnection(String stringUrl)
{
HttpURLConnection connection;
URL url = null;
try
{
url = new URL(stringUrl);
}
catch (MalformedURLException e1)
{
e1.printStackTrace();
}
try
{
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
return connection;
}
public static String inputStreamToString(InputStream is)
{
BufferedReader r = new BufferedReader(new InputStreamReader(is));
StringBuilder total = new StringBuilder();
String line;
try
{
while ((line = r.readLine()) != null)
{
total.append(line);
}
}
catch (IOException e)
{
e.printStackTrace();
}
return total.toString();
}
I receive a message from webserver where it is told that no post-values are added. As far as I understand from the code, the values are added. I'm stuck.
It turned out that all I had to do was to replace
connection.setRequestProperty("Content-Type", "text/xml");
with
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
So simple and so much time spent to clear it out...
By the way, how could I know that server requires this header? I thought that all the work that is essential to the request would be automatically done by java..
P.S. Installing fiddler helped to solve the issue, thanks for that.
debug the 'postParams' parameter and check what been sent.