I've a class that call a Rest web service to receive a file from server. While bytes are transferred, I've created an Async task, it checks if connection with server is fine to allow the stop connection if an error appears.
This async task has a loop that I have to stop:
#Component
public class ConnectionTest {
#Async
//Check connection with the server, if for three attemp it failes, throw exception
public void checkServerConnection(String serverIp) throws Exception{
int count=0;
for(;;Thread.sleep(7000)){
try{
System.out.println("TEST");
URL url = new URL(serverIp);
HttpURLConnection con = (HttpURLConnection) url
.openConnection();
con.connect();
if (con.getResponseCode() == 200){
System.out.println("Connection established!!");
}
if (count>0) count=0;
}catch(Exception e){
count++;
if (count==3)
throw new Exception("Connection error");
}
}
}
}
but how can I stop this method from the caller?
#Autowired
private ConnectionTest connectionTest;
#Override
public Response getFile(String username, String password, String serverIp, String toStorePath, String filePath){
ResponseEntity<byte[]> responseEntity = null;
try{
//it is used to check if connection of the client with the server goes down
connectionTest.checkServerConnection();
RestClient restClient = new RestClient(username, password);
// SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// requestFactory.setBufferRequestBody(false);
// restClient.setRequestFactory(requestFactory);
// RestTemplate restClient = new RestTemplate();
responseEntity = restClient.getForEntity(serverIp + "client/file/?filePath={filePath}", byte[].class, filePath);
//TODO kill async task and return false
UPDATE: as #Thomas has suggested I've used a boolean variable in ConnectionTest, I changed for cycle with while (!stop) and after the web service call I set ConnectionTest.setStop(true).
Pay attention to set stop=false before loop (and not as instance field) otherwise only the first request has this value and goes inside the while.
UPDATE 2
This is the my last code, it seems to work, maybe I should change while loop with wait-notify:
public Response getFile(String username, String password, String serverIp, String toStorePath, String filePath){
try{
//it is used to check if connection of the client with the server goes down
Future<Boolean> isConnect = connectionTest.checkServerConnection(serverIp);
Future<ResponseEntity<byte[]>> downloadResult = downloadAsync.makeRequest(username, password, serverIp, filePath);
while(!isConnect.isDone() && !downloadResult.isDone()){
}
if (isConnect.isDone()){
downloadResult.cancel(true);
return new Response(false, false, "Error with server connection!", null);
}else{
connectionTest.setStop(true);
ResponseEntity<byte[]> responseEntity = downloadResult.get();
if (MediaType.TEXT_PLAIN.toString().equals(responseEntity.getHeaders().getContentType().toString())){
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(new FileException("Error with file transfert!"));
return new Response(false, false, new String(Base64.decodeBase64(responseEntity.getBody()),Charset.forName("UTF-8")), errorResponse);
}else{
Path p = Paths.get(filePath);
String fileName = p.getFileName().toString();
FileOutputStream fos = new FileOutputStream(toStorePath+"\\"+ fileName);
fos.write(responseEntity.getBody());
fos.close();
return new Response(true, true, "Your file has been downloaded!", null);
}
}
}catch(Exception e){
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
return new Response(false, false, "Error on the client side!" , errorResponse);
}
}
connection check async:
#Component
public class ConnectionTest {
private boolean stop;
#Async
//Check connection with the server, if for three attemp it failes, throw exception
/**
*
* #param serverIp
* #throws IOException
*/
public Future<Boolean> checkServerConnection(String serverIp) throws IOException {
int count=0;
stop = false;
while (!stop){
try{
Thread.sleep(7000);
System.out.println("TEST");
//java.net.InetAddress.getByName(SERVER_ADDRESSS);
URL url = new URL(serverIp);
HttpURLConnection con = (HttpURLConnection) url
.openConnection();
con.connect();
if (count>0) count=0;
}catch(Exception e){
count++;
System.out.println(count);
if (count==3)
return new AsyncResult<Boolean>(stop);
}
}
return new AsyncResult<Boolean>(stop);
}
/**
* #return the stop
*/
public boolean isStop() {
return stop;
}
/**
* #param stop the stop to set
*/
public void setStop(boolean stop) {
this.stop = stop;
}
}
download async:
#Component
public class DownloadAsync {
#Async
public Future<ResponseEntity<byte[]>> makeRequest(String username, String password, String serverIp, String filePath){
RestClient restClient = new RestClient(username, password);
ResponseEntity<byte[]> response= restClient.getForEntity(serverIp + "client/file/?filePath={filePath}", byte[].class, filePath);
return new AsyncResult<ResponseEntity<byte[]>>(response);
}
}
When you deal with an #Async method, a good practice is to return a Future object from it because you need a connection point between the client and task code.
Let's make your task method return a Future:
public Future<Integer> checkServerConnection(String serverIp) {
// other code here
return new AsyncResult<>(count);
}
You'll need to add a couple of imports:
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.AsyncResult;
Finally, in the client code let's get the Future:
Future<Integer> checkTask = connectionTest.checkServerConnection();
Now, you can do some useful things with the checkTask. For example:
// Check if the task was completed including by an exception being thrown.
checkTask.isDone();
// Get the task result.
Integer count = checkTask.get(); // Note: this is a blocking method.
// If the task was finished by throwing an exception,
// get() method will also throw an exception.
// You can get the cause exception like this:
if (checkTask.isDone()) {
try {
checkTask.get();
} catch(Exception e) {
Exception cause = e.getCause(); // this will be your new Exception("Connection error")
}
}
// Not recommended, but you can also cancel the task:
checkTask.cancel(mayInterruptIfRunning);
first off I don't want to perplex the issue any further so I am going to give you a high level description for doing this. Particularly, look how this is done very elegantly in android, using publish delegates.
Basically, a publish delegate consists of 2 portions. First, an overridden method to publish changes, and another method to receive changes. The time interval in which changes are received, depend on the "CHUNK" size currently in the queue and the data size, but generally, you can think of this as a best effort attempt to receive publish events.
So a big high level picture is this.
ASYNCTASK
IN BACKGROUND (DOWNLOAD OVER TIME)
IN BACKGROUND (PUBLISH DOWNLOAD PROGRESS)
PUBLISH RECEIVER ( RECEIVE UPDATE OF THE DOWNLOAD [perhaps in percent]
MAKE A DECISION FROM HERE.
I am not neglecting the importance of the Spring context here, but I think once you receive this post, you will accept it's applicability, regardless of framework.
Best,
Mobile Dev
AT
Related
I'm calling a REST Service that returns a JSON String. It works, but I'm not sure how to handle the exceptions and return values. Here are my two methods I wrote:
public static String callRestService(String id) {
try {
URL url = new URL("http://"localhost:8080/rest/api/2/issue/" + id);
String basicAuth = ConnectionHelper.getServerAuthentication(serverConfig.get("authenticationType"),
serverConfig.get("username"), serverConfig.get("password"));
HttpURLConnection connection = ConnectionHelper.getHttpURLConnection(url, "GET", "Accept", basicAuth);
if (connection != null) {
InputStream responseStream = connection.getInputStream();
String response = StringHelper.convertInputStreamToString(responseStream);
connection.disconnect();
return response;
}
return "";
} catch (Exception e) {
return "";
}
}
public static HttpURLConnection getHttpURLConnection(URL url, String requestMethod, String requestProperty,
String authentication) {
try {
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
if (authentication != null && !authentication.isEmpty()) {
connection.addRequestProperty("Authorization", authentication);
}
connection.setRequestMethod(requestMethod);
connection.addRequestProperty(requestProperty, "application/json");
return connection;
} catch (Exception e) {
return null;
}
}
Is my return value and exception handling ok? Or is there a better way to do this?
For better client side handling you should have an Enum with return cases
for example if we are building a registration module your enum should be like the following :
public enum RestResponseEnum{
DONE(1,"done"),DUPLICATE_RECORD(2,"Sorry this is a duplicate record"),ERROR(3,"There is an error happened")
//Getter & Setter
private int code;
//Getter & Setter
private String msg;
private(int code,String msg){
this.code=code;
this.msg=msg;
}
public static String getAsJson(RestResponseEnum restResponseEnum){
JSONObject jsonObject=new JSONObject();
jsonObject.put("code", restResponseEnum.getCode());
jsonObject.put("message", restResponseEnum.getMsg());
return jsonObject.toString();
}
}
Use it like this :
{
// Your function code
if(registeredEmailIsFoundInDatabase){
return RestResponseEnum.getAsJson(RestResponseEnum.DUPLICATE_RECORD);
}
}
You should always faclitate and clearify the response to the client
you can see this methodology from dealing with most of apis like this one from github : https://api.github.com/users/any/any
If it is a proper REST service it will add additional information about a call in the http response code. So if it doesnt start with 2, there is no point in parsing the response body at all (in case there is no contract to return error details in the body).
How to handle your exception much depends on your current application. General rules of thumb are:
Log exceptions
Handle them on an appropriate level
Sometimes you need to ensure encapsulation and handle them where they occur, sometimes it's okay to rethrow them an catch them globally. E.g. you are using a framework like JSF, user has triggered an external service call, log the exception, rethrow it, catch it and inform the user about it without sharing too much technical details. Like:
Error: YOUR_ERROR_CODE has occured. Please contact technical support
if this keeps happening.
Example:
if (connection.getResponseCode().startsWith("2") {
// do stuff
// if any checked exception occurs here, add it to throws clause and let the caller catch it
}
else if connection.getResponseCode().equals("404") {
throw new EntityNotFoundRuntimeException(...);
}
...
But whether or not this solution is good for your case depends on your architecture.
This is my OkHttp Post Form Parameter Method using OkHttp's Async Get
public Call postGetCountries(Callback callback) {
RequestBody body = new FormEncodingBuilder()
.add("op", "op")
.build();
Log.d(TAG_PARAMS, "op=sgetcountrylist, app_type=1");
Request request = new Request.Builder()
.url(GATEWAY_URL)
.post(body)
.build();
Call call = CLIENT.newCall(request);
call.enqueue(callback);
return call;
}
This is my custom Callback.
private class GetCountriesCallback implements Callback {
#Override
public void onFailure(Request request, IOException e) {
Log.e("OkHttp", e.getMessage());
}
#Override
public void onResponse(Response response) throws IOException {
Log.d("PASSED", "PASS");
Log.d(Connection.TAG_RETURN, response.body().string());
try {
InputStream is = response.body().byteStream();
List test = connectionParser.parse(is, "op");
} catch (XmlPullParserException e) {
Log.e("PARSE ERROR", e.getMessage());
}
}
}
This is my instantiated parse method.
public List parse(InputStream in, String op) throws XmlPullParserException, IOException {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(in, null);
parser.nextTag();
return readFeed(parser, op);
} finally {
in.close();
}
}
I'm currently testing if it works unfortunately I receive a return of
10-06 11:54:42.492 6336-6892/ D/PASSED: PASS
10-06 11:54:42.692 6336-6892/ E/PARSE ERROR: Invalid stream or encoding: java.io.IOException: closed (position:START_DOCUMENT null#1:1) caused by: java.io.IOException: closed
This is what I use on my onCreate on the activity to start the whole process:
private Connection connect = Connection.getInstance();
connect.postGetCountries(new GetCountriesCallback());
I don't understand as to why the InputStream gets closed.
Two things could be going on. First, you can only read the body once. If you want to read it more than once, you need to store the result somewhere. You are reading the body twice, once here --
Log.d(Connection.TAG_RETURN, response.body().string());
and then here --
InputStream is = response.body().byteStream();
List test = connectionParser.parse(is, "op");
by the time you start to parse, you have already exhausted the available input in the stream. The quick solution is to remove the log statement.
Another thing that might be tripping you up, or could trip you up in the future is onResponse is called even in the event of HTTP returning an error code. You should check the Response's code() or isSuccesful() methods to decide if you should even attempt to parse the response.
I need to make a library in which I will have synchronous and asynchronous feature.
executeSynchronous() - waits until I have a result, returns the result.
executeAsynchronous() - returns a Future immediately which can be processed after other things are done, if needed.
Core Logic of my Library
The customer will use our library and they will call it by passing DataKey builder object. We will then construct a URL by using that DataKey object and make a HTTP client call to that URL by executing it and after we get the response back as a JSON String, we will send that JSON String back to our customer as it is by creating DataResponse object. Some customer will call executeSynchronous() and some might call executeAsynchronous() so that's why I need to provide two method separately in my library.
Interface:
public interface Client {
// for synchronous
public DataResponse executeSynchronous(DataKey key);
// for asynchronous
public Future<DataResponse> executeAsynchronous(DataKey key);
}
And then I have my DataClient which implements the above Client interface:
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
#Override
public DataResponse executeSynchronous(DataKey key) {
DataResponse dataResponse = null;
Future<DataResponse> future = null;
try {
future = executeAsynchronous(key);
dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key);
dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
// does this looks right?
future.cancel(true); // terminating tasks that have timed out
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
#Override
public Future<DataResponse> executeAsynchronous(DataKey key) {
Future<DataResponse> future = null;
try {
Task task = new Task(key, restTemplate);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
Simple class which will perform the actual task:
public class Task implements Callable<DataResponse> {
private DataKey key;
private RestTemplate restTemplate;
public Task(DataKey key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
#Override
public DataResponse call() {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using key object
private String createURL() {
String url = somecode;
return url;
}
}
When I started working on this solution, I was not terminating the tasks that have timed out. I was reporting the timeout to the client, but the task continues to run in the thread pool (potentially occupying one of my limited 10 threads for a long time). So I did some research online and I found out that I can cancel my tasks those have timed out by using cancel on future as shown below -
future.cancel(true);
But if I am doing like this as shown in my above solution, then do I need to close any other resources like RestTemplate as soon as thread is interrupted? If yes, then how would I do that? Also, can we interrupt RestTemplate calls? Since I tried calling cancel on my future as soon as the task got timed out but I guess my thread doesn't got interrupted.
Should we always be terminating the tasks that have got timed out? If we don't do that then what might be the impact I will have? Will it affect my performance?
Is there any better solution to deal with this case with my current setup?
It appears that a call to a RestTemplate cannot be interrupted or canceled. Even if the "kludge" using a callback is utilized, the RestTemplate might have resources locked up internally, waiting for the response before invoking the callback.
When the underlying socket is accessible, network I/O can be aborted by closing the socket from another thread. For example, a timer can be started to close the socket after a timeout elapses. Or, if you want an indefinite timeout that is sensitive to interrupts (due to a user pressing a "Cancel" button, for example), you can submit a task that waits indefinitely but responds to interrupts by closing the socket.
Unfortunately, it doesn't look like the authors of RestTemplate provided this capability.
Yes, you should clean up resources that are no longer needed because of task cancellation or expiration. Yes, it will affect performance. If your thread pool has a limited number of threads, eventually all will be stuck in defunct tasks. If it has an unlimited number of threads, eventually memory will become exhausted.
Sometimes it is not possible to interrupt thread especially when thread performs blocking operations on Socket.
So instead of cancelling the task when it timeouts, you should rather set timeouts on http connection.
Unfortunately timeousts are set per Connection Factory and RestTemplate, thus each request must use it's own RestTemplate.
You can create new RestTemplate per task, or reuse previusly created templates using ThreadLocal or resource pooling.
For example the task using Thread local might look like below:
public class Task implements Callable<DataResponse> {
private DataKey key;
private ThreadLocal<RestTemplate> restTemplateThreadLocal =
ThreadLocal.withInitial(()->new RestTemplate(new SimpleClientHttpRequestFactory()));
public Task(DataKey key) {
this.key = key;
}
private SimpleClientHttpRequestFactory getConnectionFactory(){
return (SimpleClientHttpRequestFactory)restTemplateThreadLocal.get().getRequestFactory();
}
#Override
public DataResponse call() {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
//it is up to you, how to set connection and read timeouts from provided key.getTimeout
getConnectionFactory().setConnectTimeout(1000);
getConnectionFactory().setReadTimeout(key.getTimeout());
response = restTemplateThreadLocal.get().getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using key object
private String createURL() {
String url = somecode;
return url;
}
}
BTW.
Spring also provides AsyncRestTemplate, which may make your code simpler.
If used with Netty4ClientHttpRequestFactory you can get NIO based client connections. In such case, you should be able to interrupt your tasks even while it makes Http connection.
Short sample below. It uses NIO thus you does not have to care if the request is really cancelled after Timeout.
URI url = new URI("http://www.chicagotribune.com/news/ct-college-of-dupage-investigation-met-20150330-story.html");
Netty4ClientHttpRequestFactory asyncRequestFactory = new Netty4ClientHttpRequestFactory();
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(asyncRequestFactory);
ListenableFuture<ResponseEntity<String>> entity = asyncRestTemplate.getForEntity(url, String.class);
System.out.println("entity.get() = " + entity.get());
asyncRequestFactory.destroy();
so here is my issue: I am working on a mobile App, that requires a login. I am programming in Android Studio / Java. Got a quite good experience in Java but i've never done networking.. There is a .asp script on a server that processes the login, which i need to send the login data to. The best way to solve this i think is a HTTP - Request, because if you enter the url of the script into the browser, followed by the query string containing the login data, you already get a response.
http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android
returns a Json String containing a profile token and a timestamp and the profile name.Like:
{"profil_token":"qn2hJcRQixYjG7yyW956g1407921902","profil_name":"Marc Klingenhäger","timestamp":"1407921902"}
This profile token is then attached to every url the user requests, and in that way the user gains permission to all the websites.
I read that you can do the same thing with a http GET request, but me and my coworker worked on this
(such a simple thing) for nine ours and didn't get our code working...
We tried out heaps of snippets, this is our most simple attempt:
In the Main activity, on clicking the button that leads to the login, LoginActivity.class is called using a Intent.
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
After entering the user data, the user clicks the login button, and the method attemptLogin(); gets called.
public void attemptLogin() {
if (mAuthTask != null) {
return;
}
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password, if the user entered one.
if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
// Check for a valid email address.
if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!isEmailValid(email)) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mAuthTask = new UserLoginTask(email, password);
mAuthTask.execute((Void) null);
}
}
So after some error detection, the class userLoginTask (subclass of AsyncTask) is initialized to handle the networking stuff, because initializing the http request in the main thread seems to cause an exception. We didn't manage to code a HTTP - Request here so far.. (thats the main problem)
public class UserLoginTask extends AsyncTask {
private final String mEmail;
private final String mPassword;
UserLoginTask(String email, String password) {
mEmail = email;
mPassword = password;
}
#Override
protected Boolean doInBackground(Void... params) {
// TODO: attempt authentication against a network service.
try {
// Simulate network access.
Thread.sleep(2000);
} catch (InterruptedException e) {
return false;
}
for (String credential : DUMMY_CREDENTIALS) {
String[] pieces = credential.split(":");
if (pieces[0].equals(mEmail)) {
// Account exists, return true if the password matches.
return pieces[1].equals(mPassword);
}
}
// TODO: register the new account here.
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
showProgress(false);
if (success) {
finish();
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
So my question is basically, how can i initialize a HTTP - Request in the UserLoginTask class. Any Ideas? Thanks in advance! :)
Falco
The easiest way is to use a URL object and open a stream to your HTTP server.
The server response can be read through this stream:
String url = "http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android";
try {
URL u = new URL(url);
InputStream is = u.openStream(); // Opens streaming connection to url
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuffer result = new StringBuffer(); // Buffer to store saved data
String input = null;
while((input = reader.readLine()) != null) {
// Read data until the end of the stream
result.append(input);
}
// Do something with result here
} catch (IOException e) {
e.printStackTrace();
}
When you have retrieved the data as a string, you can parse the JSON to get the profile_token
Use Android's Volley http://developer.android.com/training/volley/index.html and issue an HTTP POST request, sending username / password.
I advise hashing the password (MD5 or something else - depends on what the back-end handles to decrypt).
Google suggests using HttpUrlConnection.
An example that should do what you want is very simple, especially when using GET. First, construct an URL from String. Your response is InputStream, which you parse to JSONObject and obtain your token.
URL url = new URL("http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android");
//later:
URL url = new URL("http://sampleurl.info/actions/checklogin.asp?token=abcde");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
finally {
urlConnection.disconnect();
}
}
This is recommended approach, since it does not require any external libraries, and it can easily be converted to use POST instead of GET and HTTPS instead of HTTP.
I have a high volume java application in which I have to send http posts to another server.
Currently I'm using org.apache.commons.httpclient library:
private static void sendData(String data) {
HttpClient httpclient = new HttpClient();
StringRequestEntity requestEntity;
try {
requestEntity = new StringRequestEntity(data, "application/json", "UTF-8");
String address = "http://<my host>/events/"
PostMethod postMethod = new PostMethod(address);
postMethod.setRequestEntity(requestEntity);
httpclient.executeMethod(postMethod);
} catch (Exception e) {
LOG.error("Failed to send data ", e);
}
}
This means I'm sending my http requests synchronously, which doesn't fit my multithreaded high volume app. So I would like to change those calls to asynchronous non-blocking http calls.
I was going through number of options such as apache async client and xsocket but was not able to make it work.
Tried ning:
private static void sendEventToGrpahiteAsync(String event) {
LOG.info("\n" + "sendEventToGrpahiteAsync");
try (AsyncHttpClient asyncHttpClient = new AsyncHttpClient()) {
BoundRequestBuilder post = asyncHttpClient.preparePost();
post.addHeader("Content-Type", "application/json");
post.setBodyEncoding("UTF-8");
post.setBody(event);
post.execute(new HttpRequestCompletionHandler());
} catch (Exception e) {
LOG.error("Failed to sending event", e);
}
}
I tried Apache HttpAsyncClient:
private static void sendEventToGrpahiteAsync(String event) {
LOG.info("\n" + "sendEventToGrpahiteAsync");
try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
httpclient.start();
HttpPost request = new HttpPost(addr);
StringEntity entity = new StringEntity(event, ContentType.create("application/json", Consts.UTF_8));
request.setEntity(entity);
httpclient.execute(request, null);
} catch (Exception e) {
LOG.error("Failed to sending event", e);
}
}
I tried xsocket:
private static void sendEventToGrpahiteAsync2(String event) {
LOG.info("\n" + "sendEventToGrpahiteAsync");
try (INonBlockingConnection con = new NonBlockingConnection(<SERVER_IP>, 80);
IHttpClientEndpoint httpClientConnection = new HttpClientConnection(con)) {
IHttpResponseHandler responseHandler = new MyResponseHandler();
IHttpRequest request = new PostRequest(url_address, "application/json", Consts.UTF_8.toString(), event);
request.setTransferEncoding(Consts.UTF_8.toString());
httpClientConnection.send(request, responseHandler);
} catch (Exception e) {
LOG.error("Failed to sending event", e);
}
}
I get no exceptions but the post doesn't get to the target as well.
To be clear, the target is a graphite server so once a post arrives it is clearly seen in a graph. The synchronous posts works well, I can see the result on the graph, but none of the asynchronous posts shows on my destination graph.
What am I missing?
Thanks
Got it.
All the libraries I'n using are implemented using an extra IO thread, so my process probably ends before a full handshake.
Once I added Thread.sleep(2000) after the http calls things worked just fine.
So for a web app (which is my case) my suggested implementations are just fine (but for a java process you might consider NickJ's answer).
You could use the Java Executor framework:
First, create a Callable to do your work:
public class MyCallable implements Callable<MyResult> {
#Override
public MyResult call() throws Exception {
//do stuff
return result;
}
}
Get an Exectutor which will run your Callable. There are various way to get one, here's one example:
ExecutorService executor = Executors.newFixedThreadPool(NTHREDS);
Finally, run it:
MyCallable callable = new MyCallable();
Future<MyResult> futureResult = executor.submit(callable);
Getting the result:
boolean resultReady = futureResult.isDone(); //is the result ready yet?
Result r = futureResult.get(); //wait for result and return it
try {
Result r = futureResult.get(10, TimeUnit.SECONDS); //wait max. 10 seconds for result
} catch (TimeOutException e) {
//result still not ready after waiting 10 seconds
}