I want to load an image at periodic intervals to an imageitem. My outer class is generating the URL and I need to pass it to the innerclass. How do I achieve this?
public class MapTimer extends TimerTask{
public void run() {
System.out.println("Map starting...");
String URL=null,serverquery=null;
try {
sendMessage(this.message);
item.setLabel(item.getLabel()+"start");
serverquery=receiveMessage();
item.setLabel(item.getLabel()+"stop");
URL = getURL(serverquery); // my url to be passed to innerclass
System.out.println("URl is "+serverquery);
item.setLabel(URL+item.getLabel());
Thread t = new Thread() {
public void run() {
item.setLabel(item.getLabel()+"6");
try {
Image image = loadImage(URL); // using url
System.out.println("GEtting image....");
item = new ImageItem(null, image, 0, null);
form.append(item);
display.setCurrent(form);
} catch (IOException ioe) {
item.setLabel("Error1");
}
catch (Exception ioe) {
item.setLabel("Error1");
}
}
};
t.start(); // write post-action user code here
}
catch(Exception e){
System.out.println("Error3"+e);
}
}
}
How do I pass the URL to my innerthread class?
You have to declare the variable final or don't use a variable but a field in the class.
public class YourClass {
private String url;
public void yourMethod {
url = getURL(serverquery);
System.out.println("URl is "+serverquery);
item.setLabel(URL+item.getLabel());
Thread t = new Thread() {
public void run() {
item.setLabel(item.getLabel()+"6");
try {
Image image = loadImage(url); // using url
System.out.println("GEtting image....");
item = new ImageItem(null, image, 0, null);
form.append(item);
display.setCurrent(form);
} catch (IOException ioe) {
item.setLabel("Error1");
}
catch (Exception ioe) {
item.setLabel("Error1");
}
}
};
t.start(); // write post-action user code here
}
}
It should work if your URL is contained in a final reference. This means that the reference won't move. Consequently, you can't initialize it to null and then call getURL, you need to declare it at this point:
final String URL = getURL(serverquery);
I found that there are (at least) 2 major ways of doing this on whever your thread gets the information or recieves the information (in your case URL)
1) gets the informations:
what you want to do is keep a reference of the object containing the data you need (like URL) in your Thread, and when your ready the Thread gets the next item/URL to load.
public class MapTimer extends TimerTask{
public void run() {
...
}
private URLGenerator urlGenerator = null;
public MapTimer(URLGenerator urlGen){
...
}
This will give you the option to get the next URL when your thread is free (with a if(urlGen != null) of course)
2) Receives Information
Works better for cases where the thread is called only when the rest of the program wants to (user inputing data etc)
public class MapTimer extends TimerTask{
public void run() {
...
}
private URL urlToLoad = null;
public void setURL(URL urlToLoad){
...
//store and wake up thread
}
this way your thread recieve with a setter the data it needs to run, processes it then waits for the next data to be send to it (of course you need to be careful of thread issues like multiple calls etc.)
Hope this helps you
Jason
Just make the field a static variable belonging to the class and not any object instance of the class, like this:
public YourClass {
private static String url;
//...
}
Please note however that the static variable will be shared among all object instances of the class.
Related
I have a daemon thread running which calls a function (prepareOrder) whenever the cook is not busy and there are orders to be delivered. The prepareOrder calls the orderComplete function after a certain interval of time depending upon the time required to complete the order. Now the problem i am facing is only the last call to the prepareOrder gets displayed on sout.
The daemon
package ui;
import Model.takeOrderModel;
public class daemonThread extends Thread{
//call this method in the main method of driving fucntion
private takeOrderModel orderModel;
daemonThread(takeOrderModel orderModel){
this.orderModel = orderModel;
}
public void assignCook(){
while(true){
int toComplete = orderModel.toCompleteOrders.size();
if ( !orderModel.cookBusy && toComplete>0 ) orderModel.prepareOrder();
}
}
}
The prepare order function.
public void prepareOrder(){
// pick the last element from list
if (toCompleteOrders.size() > 0){
String nextPrepare = toCompleteOrders.get(toCompleteOrders.size()-1);
order orderToComplete = allOrdersPlaced.get(nextPrepare);
completeOrder(orderToComplete);
toCompleteOrders.remove(nextPrepare);
}
}
//Helper function to prepareOrder moves an order from toComplete to prepared order
private void completeOrder(order orderToComplete){
changeCookState();
new java.util.Timer().schedule(
new java.util.TimerTask(){
#Override
public void run() {
changeCookState();
preparedOrders.add(orderToComplete.id);
deliverOrder(orderToComplete.id);
}
}, (long) (orderToComplete.timeToComplete*60)
);
}
public void changeCookState(){
this.cookBusy = !cookBusy;
}
// MODIFIES removes a order from the prepared list and puts it in delivered list
public String deliverOrder(String completedOrder){
preparedOrders.remove(completedOrder);
deliveredOrders.add(completedOrder);
System.out.println(String.format("The order of %s is here", allOrdersPlaced.get(completedOrder).customerName));
return String.format("The order of %s is here", allOrdersPlaced.get(completedOrder).customerName);
}
The main function driving code.
orderMachine.takeNewOrder(fullMeal, "Tom");
orderMachine.takeNewOrder(halfMeal, "Bob");
daemonThread backThread = new daemonThread(orderMachine);
backThread.setDaemon(true);
backThread.assignCook();
Now for me only the last placed order("Bob") gets printed on sout. How can all calls created by Timer.schedule stay in stack.
Edits
The take new order function.
public boolean takeNewOrder(List<item> itemsInOrder, String customerName){
try {
order newOrder = new order(itemsInOrder, customerName);
allOrdersPlaced.put(newOrder.id, newOrder);
toCompleteOrders.add(newOrder.id);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
Edit 2
here is the public repo containing the complete code
https://github.com/oreanroy/Share_code_samples/tree/master/takeOrder
The problem in this code is a concurrency bug - the cookBusy variable is being written to from two different threads. To fix this, use an AtomicBoolean instead of a boolean, as this is thread safe.
AtomicBoolean cookBusy = new AtomicBoolean(false);
Use compareAndSet to ensure the shared variable is set to a known value before updating it.
public void changeCookState(boolean busy){
if (!this.cookBusy.compareAndSet(!busy, busy))
{
throw new RuntimeException("shared variable set to unexpected value");
}
}
I'm trying to create an internet checker class, that will check the connection to a certain url and update the status property accordingly. To avoid ui freeze i want to use a thread, and a timer to recheck after a certain interval. Problem is the CHECK method call in the timeline keyframe is called from the FX thread still. How can i use a timeline inside a thread?
CODE:
public class InternetChecker {
private String baseUrl;
/***Properties***/
private ObjectProperty<Status> status = new SimpleObjectProperty<>(Status.ACTIVE);
/****************************************************************
********** CONSTRUCTORS ************
****************************************************************/
public InternetChecker(String baseUrl) {
this(baseUrl, 1000);
}
public InternetChecker(String baseUrl, int millisCheckInterval) {
this.baseUrl = baseUrl;
new Thread(() -> {
Timeline timelineCheck = new Timeline();
timelineCheck.getKeyFrames().add(
new KeyFrame(Duration.millis(millisCheckInterval), e -> {
check();
}));
timelineCheck.setCycleCount(Animation.INDEFINITE);
timelineCheck.play();
}).start();
}
/*******************************
* Will check if there is an internet connection present
* and update the status accordingly
*******************************/
public void check() {
// Check if base internet connection
// is working, if it is we continue
// to see if domain connection is working
try {
if ("127.0.0.1".equals(InetAddress.getLocalHost().getHostAddress())) {
setStatus(Status.INTERNET_DISCONNECTED);
return;
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
// Check if base domain connection is working
try {
final URL url = new URL(baseUrl);
final URLConnection conn = url.openConnection();
conn.connect();
conn.getInputStream().close();
setStatus(Status.ACTIVE);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
setStatus(Status.BASE_URL_UNREACHABLE);
}
}
/****************************************************************
********** ACCESSORS ************
****************************************************************/
public Status getStatus() {
return status.get();
}
public ObjectProperty<Status> statusProperty() {
return status;
}
private void setStatus(Status status) {
this.status.set(status);
}
/*******************************
* ACTIVE (Base url reachable)
* BASE_URL_UNREACHABLE (Internet available, but base url is unreachable)
* INTERNET_DISCONNECTED (Internet is not available)
********************************/
public enum Status {
ACTIVE,
BASE_URL_UNREACHABLE,
INTERNET_DISCONNECTED;
}
}
Since you need to do a periodic background task that communicates with the JavaFX Application Thread it would be better to use a ScheduledService. This class executes a (new) Task periodically using an Executor that can be defined by the developer. Note that ScheduledService extends javafx.concurrent.Service.
Here is a skeleton example of what you'd need to do to implement this:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
public class ConnectionStatusService extends ScheduledService<Status> {
// Property allows you to change the "baseUrl" between executions
private final StringProperty baseUrl = new SimpleStringProperty(this, "baseUrl");
// property getter and setters omitted...
#Override
protected Task<Status> createTask() {
// creates a new Task and gives the current "baseUrl"
// as an argument. This is called every cycle
return new ConnectionStatusTask(getBaseUrl());
}
private static class ConnectionStatusTask extends Task<Status> {
// A Task is a one-shot thing and its initial state should be
// immutable (or at least encapsulated from external modification).
private final String baseUrl;
private ConnectionStatusTask(String baseUrl) {
this.baseUrl = baseUrl;
}
#Override
protected Status call() throws Exception {
// Do what you need to determine connection status
return computedStatus;
}
}
}
Then you'd listen/bind to the lastValue property.
public void initService() {
ConnectionStatusService service = new ConnectionStatusService();
service.setBaseUrl(/* your URL */);
service.setPeriod(Duration.seconds(1)); // run every 1 seconds
service.lastValueProperty().addListener(/* your listener */); // or bind to this property
// you may also want to add EventHandlers/Listeners to handle when the
// service fails and such.
service.start();
}
It's important you observe the lastValue property and not the value property. The reason is given in the Javadoc of lastValue:
The last successfully computed value. During each iteration, the
"value" of the ScheduledService will be reset to null, as with any
other Service. The "lastValue" however will be set to the most
recently successfully computed value, even across iterations. It is
reset however whenever you manually call reset or restart.
I recommend reading the Javadoc of Task, Service, and ScheduledService for more information. All three of these classes implement the javafx.concurrent.Worker interface.
You only want a single statement to be executed on the JavaFX application thread and that is status.set(status);. Since you're planing to run this statement with some delay in between, you can simply use Platform.runLater to do this.
As for repeatedly executing the check: ScheduledExecutorService is designed for this purpose.
public class InternetChecker implements Runnable {
private final String baseUrl;
/***Properties***/
// use readonly wrapper here to restrict outside access to the property
private final ReadOnlyObjectWrapper<Status> status = new ReadOnlyObjectWrapper<>(Status.ACTIVE);
/****************************************************************
********** CONSTRUCTORS ************
****************************************************************/
public InternetChecker(String baseUrl) {
this.baseUrl = baseUrl;
}
/*******************************
* Will check if there is an internet connection present
* and update the status accordingly
*******************************/
#Override
public void run() {
// Check if base internet connection
// is working, if it is we continue
// to see if domain connection is working
try {
if ("127.0.0.1".equals(InetAddress.getLocalHost().getHostAddress())) {
setStatus(Status.INTERNET_DISCONNECTED);
return;
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
// Check if base domain connection is working
try {
final URL url = new URL(baseUrl);
final URLConnection conn = url.openConnection();
conn.connect();
conn.getInputStream().close();
setStatus(Status.ACTIVE);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
setStatus(Status.BASE_URL_UNREACHABLE);
}
}
/****************************************************************
********** ACCESSORS ************
****************************************************************/
public Status getStatus() {
return status.get();
}
public ReadOnlyObjectProperty<Status> statusProperty() {
return status.getReadOnlyProperty​();
}
private void setStatus(final Status status) {
Platform.runLater(() -> this.status.set(status));
}
/*******************************
* ACTIVE (Base url reachable)
* BASE_URL_UNREACHABLE (Internet available, but base url is unreachable)
* INTERNET_DISCONNECTED (Internet is not available)
********************************/
public enum Status {
ACTIVE,
BASE_URL_UNREACHABLE,
INTERNET_DISCONNECTED;
}
}
InternetChecker checker = new InternetChecker(url);
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor​();
// use delay here to avoid race condition
executorService.scheduleAtFixedDelay(checker, 0, millisCheckInterval, TimeUnit.MILLISECONDS);
Note that you need to shut down the service "manually" or use a ThreadFactory returning daemon threads:
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor​(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
i have joined to one of those Vertx lovers , how ever the single threaded main frame may not be working for me , because in my server there might be 50 file download requests at a moment , as a work around i have created this class
public abstract T onRun() throws Exception;
public abstract void onSuccess(T result);
public abstract void onException();
private static final int poolSize = Runtime.getRuntime().availableProcessors();
private static final long maxExecuteTime = 120000;
private static WorkerExecutor mExecutor;
private static final String BG_THREAD_TAG = "BG_THREAD";
protected RoutingContext ctx;
private boolean isThreadInBackground(){
return Thread.currentThread().getName() != null && Thread.currentThread().getName().equals(BG_THREAD_TAG);
}
//on success will not be called if exception be thrown
public BackgroundExecutor(RoutingContext ctx){
this.ctx = ctx;
if(mExecutor == null){
mExecutor = MyVertxServer.vertx.createSharedWorkerExecutor("my-worker-pool",poolSize,maxExecuteTime);
}
if(!isThreadInBackground()){
/** we are unlocking the lock before res.succeeded , because it might take long and keeps any thread waiting */
mExecutor.executeBlocking(future -> {
try{
Thread.currentThread().setName(BG_THREAD_TAG);
T result = onRun();
future.complete(result);
}catch (Exception e) {
GUI.display(e);
e.printStackTrace();
onException();
future.fail(e);
}
/** false here means they should not be parallel , and will run without order multiple times on same context*/
},false, res -> {
if(res.succeeded()){
onSuccess((T)res.result());
}
});
}else{
GUI.display("AVOIDED DUPLICATE BACKGROUND THREADING");
System.out.println("AVOIDED DUPLICATE BACKGROUND THREADING");
try{
T result = onRun();
onSuccess((T)result);
}catch (Exception e) {
GUI.display(e);
e.printStackTrace();
onException();
}
}
}
allowing the handlers to extend it and use it like this
public abstract class DefaultFileHandler implements MyHttpHandler{
public abstract File getFile(String suffix);
#Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
new BackgroundExecutor<Void>(ctx) {
#Override
public Void onRun() throws Exception {
File file = getFile(URLDecoder.decode(suffix, "UTF-8"));
if(file == null || !file.exists()){
utils.sendResponseAndEnd(ctx.response(),404);
return null;
}else{
utils.sendFile(ctx, file);
}
return null;
}
#Override
public void onSuccess(Void result) {}
#Override
public void onException() {
utils.sendResponseAndEnd(ctx.response(),404);
}
};
}
and here is how i initialize my vertx server
vertx.deployVerticle(MainDeployment.class.getCanonicalName(),res -> {
if (res.succeeded()) {
GUI.display("Deployed");
} else {
res.cause().printStackTrace();
}
});
server.requestHandler(router::accept).listen(port);
and here is my MainDeployment class
public class MainDeployment extends AbstractVerticle{
#Override
public void start() throws Exception {
// Different ways of deploying verticles
// Deploy a verticle and don't wait for it to start
for(Entry<String, MyHttpHandler> entry : MyVertxServer.map.entrySet()){
MyVertxServer.router.route(entry.getKey()).handler(new Handler<RoutingContext>() {
#Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
entry.getValue().Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
}
this is working just fine when and where i need it , but i still wonder if is there any better way to handle concurencies like this on vertx , if so an example would be really appreciated . thanks alot
I don't fully understand your problem and reasons for your solution. Why don't you implement one verticle to handle your http uploads and deploy it multiple times? I think that handling 50 concurrent uploads should be a piece of cake for vert.x.
When deploying a verticle using a verticle name, you can specify the number of verticle instances that you want to deploy:
DeploymentOptions options = new DeploymentOptions().setInstances(16);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
This is useful for scaling easily across multiple cores. For example you might have a web-server verticle to deploy and multiple cores on your machine, so you want to deploy multiple instances to take utilise all the cores.
http://vertx.io/docs/vertx-core/java/#_specifying_number_of_verticle_instances
vertx is a well-designed model so that a concurrency issue does not occur.
generally, vertx does not recommend the multi-thread model.
(because, handling is not easy.)
If you select multi-thread model, you have to think about shared data..
Simply, if you just only want to split EventLoop Area,
first of all, you make sure Check your a number of CPU Cores.
and then Set up the count of Instances .
DeploymentOptions options = new DeploymentOptions().setInstances(4);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);
But, If you have 4cores of CPU, you don't set up over 4 instances.
If you set up to number four or more, the performance won't improve.
vertx concurrency reference
http://vertx.io/docs/vertx-core/java/
I am trying to run two different instances of a Java thread I created, and I see it create the two threads, but only one thread is being called and used at a time. If I run them separately they work fine, but when I try to run the two threads at the same time only one is being updated.
Here is the pertinent code from a main() method:
for( Properties prop: propList){
Send send = new MainTest().new Send(iterationOffset,prop);
send.start();
}
public class Send extends Thread{
private double iterationOffset;
private Properties prop;
SendEsd(double off, Properties p){
this.iterationOffset = off;
this.prop = p;
}
#Override
public void run() {
while (true) {
String id = prop.get("platform.id").toString();
System.out.println("$$$$$$$$$$$$$$ create Thred : send = " +id);
sendData( iterationOffset, prop );
}
}
private void sendData(double iterationOffset, Properties prop ){
id = prop.get("platform.id").toString();
// *** the itDataList is a really large CSV it inputs,
// so it will spin here a long time
for (it itData : itDataList) {
try {
//*** it will send some data here
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error("Sleep thread was interrupted.");
}
}
}
}
Here is my DataClientFactory class.
public class DataClientFactory {
public static IClient getInstance() {
return ClientHolder.INSTANCE;
}
private static class ClientHolder {
private static final DataClient INSTANCE = new DataClient();
static {
new DataScheduler().startScheduleTask();
}
}
}
Here is my DataClient class.
public class DataClient implements IClient {
private ExecutorService service = Executors.newFixedThreadPool(15);
private RestTemplate restTemplate = new RestTemplate();
// for initialization purpose
public DataClient() {
try {
new DataScheduler().callDataService();
} catch (Exception ex) { // swallow the exception
// log exception
}
}
#Override
public DataResponse getDataSync(DataKey dataKeys) {
DataResponse response = null;
try {
Future<DataResponse> handle = getDataAsync(dataKeys);
response = handle.get(dataKeys.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// log error
response = new DataResponse(null, DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
} catch (Exception e) {
// log error
response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
}
return response;
}
#Override
public Future<DataResponse> getDataAsync(DataKey dataKeys) {
Future<DataResponse> future = null;
try {
DataTask dataTask = new DataTask(dataKeys, restTemplate);
future = service.submit(dataTask);
} catch (Exception ex) {
// log error
}
return future;
}
}
I get my client instance from the above factory as shown below and then make a call to getDataSync method by passing DataKey object. DataKey object has userId and Timeout values in it. Now after this, call goes to my DataTask class to call method as soon as handle.get is called.
IClient dataClient = DataClientFactory.getInstance();
long userid = 1234l;
long timeout_ms = 500;
DataKey keys = new DataKey.Builder().setUserId(userid).setTimeout(timeout_ms)
.remoteFlag(false).secondaryFlag(true).build();
// call getDataSync method
DataResponse dataResponse = dataClient.getDataSync(keys);
System.out.println(dataResponse);
Here is my DataTask class which has all the logic -
public class DataTask implements Callable<DataResponse> {
private DataKey dataKeys;
private RestTemplate restTemplate;
public DataTask(DataKey dataKeys, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.dataKeys = dataKeys;
}
#Override
public DataResponse call() {
DataResponse dataResponse = null;
ResponseEntity<String> response = null;
int serialId = getSerialIdFromUserId();
boolean remoteFlag = dataKeys.isRemoteFlag();
boolean secondaryFlag = dataKeys.isSecondaryFlag();
List<String> hostnames = new LinkedList<String>();
Mappings mappings = ClientData.getMappings(dataKeys.whichFlow());
String localPrimaryAdress = null;
String remotePrimaryAdress = null;
String localSecondaryAdress = null;
String remoteSecondaryAdress = null;
// use mappings object to get above Address by using serialId and basis on
// remoteFlag and secondaryFlag populate the hostnames linked list
if (remoteFlag && secondaryFlag) {
hostnames.add(localPrimaryHostIPAdress);
hostnames.add(localSecondaryHostIPAdress);
hostnames.add(remotePrimaryHostIPAdress);
hostnames.add(remoteSecondaryHostIPAdress);
} else if (remoteFlag && !secondaryFlag) {
hostnames.add(localPrimaryHostIPAdress);
hostnames.add(remotePrimaryHostIPAdress);
} else if (!remoteFlag && !secondaryFlag) {
hostnames.add(localPrimaryHostIPAdress);
} else if (!remoteFlag && secondaryFlag) {
hostnames.add(localPrimaryHostIPAdress);
hostnames.add(localSecondaryHostIPAdress);
}
for (String hostname : hostnames) {
// If host name is null or host name is in local block host list, skip sending request to this host
if (hostname == null || ClientData.isHostBlocked(hostname)) {
continue;
}
try {
String url = generateURL(hostname);
response = restTemplate.exchange(url, HttpMethod.GET, dataKeys.getEntity(), String.class);
// make DataResponse
break;
} catch (HttpClientErrorException ex) {
// make DataResponse
return dataResponse;
} catch (HttpServerErrorException ex) {
// make DataResponse
return dataResponse;
} catch (RestClientException ex) {
// If it comes here, then it means some of the servers are down.
// Add this server to block host list
ClientData.blockHost(hostname);
// log an error
} catch (Exception ex) {
// If it comes here, then it means some weird things has happened.
// log an error
// make DataResponse
}
}
return dataResponse;
}
private String generateURL(final String hostIPAdress) {
// make an url
}
private int getSerialIdFromUserId() {
// get the id
}
}
Now basis on userId, I will get the serialId and then get the list of hostnames, I am suppose to make a call depending on what flag is passed. Then I iterate the hostnames list and make a call to the servers. Let's say, if I have four hostnames (A, B, C, D) in the linked list, then I will make call to A first and if I get the data back, then return the DataResponse back. But suppose if A is down, then I need to add A to block list instantly so that no other threads can make a call to A hostname. And then make a call to hostname B and get the data back and return the response (or repeat the same thing if B is also down).
I have a background thread as well which runs every 10 minutes and it gets started as soon we get the client instance from the factory and it parses my another service URL to get the list of block hostnames that we are not supposed to make a call. Since it runs every 10 minutes so any servers which are down, it will get the list after 10 minutes only, In general suppose if A is down, then my service will provide A as the block list of hostnames and as soon as A becomes up, then that list will be updated as well after 10 minutes.
Here is my background thread code DataScheduler-
public class DataScheduler {
private RestTemplate restTemplate = new RestTemplate();
private static final Gson gson = new Gson();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void startScheduleTask() {
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
callDataService();
} catch (Exception ex) {
// log an error
}
}
}, 0, 10L, TimeUnit.MINUTES);
}
public void callDataService() throws Exception {
String url = null;
// execute the url and get the responseMap from it as a string
parseResponse(responseMap);
}
private void parseResponse(Map<FlowsEnum, String> responses) throws Exception {
// .. some code here to calculate partitionMappings
// block list of hostnames
Map<String, List<String>> coloExceptionList = gson.fromJson(response.split("blocklist=")[1], Map.class);
for (Map.Entry<String, List<String>> entry : coloExceptionList.entrySet()) {
for (String hosts : entry.getValue()) {
blockList.add(hosts);
}
}
if (update) {
ClientData.setAllMappings(partitionMappings);
}
// update the block list of hostnames
if (!DataUtils.isEmpty(responses)) {
ClientData.replaceBlockedHosts(blockList);
}
}
}
And here is my ClientData class which holds all the information for block list of hostnames and partitionMappings details (which is use to get the list of valid hostnames).
public class ClientData {
private static final AtomicReference<ConcurrentHashMap<String, String>> blockedHosts = new AtomicReference<ConcurrentHashMap<String, String>>(
new ConcurrentHashMap<String, String>());
// some code here to set the partitionMappings by using CountDownLatch
// so that read is blocked for first time reads
public static boolean isHostBlocked(String hostName) {
return blockedHosts.get().contains(hostName);
}
public static void blockHost(String hostName) {
blockedHosts.get().put(hostName, hostName);
}
public static void replaceBlockedHosts(List<String> blockList) {
ConcurrentHashMap<String, String> newBlockedHosts = new ConcurrentHashMap<>();
for (String hostName : blockList) {
newBlockedHosts.put(hostName, hostName);
}
blockedHosts.set(newBlockedHosts);
}
}
Problem Statement:-
When all the servers are up (A,B,C,D as an example) above code works fine and I don't see any TimeoutException happening at all from the handle.get but if let's say one server (A) went down which I was supposed to make a call from the main thread then I start seeing lot of TimeoutException, by lot I mean, huge number of client timeouts happening.
And I am not sure why this is happening? In general this won't be happening right since as soon as the server goes down, it will get added to blockList and then no thread will be making a call to that server, instead it will try another server in the list? So it should be smooth process and then as soon as those servers are up, blockList will get updated from the background thread and then you can start making a call.
Is there any problem in my above code which can cause this problem? Any suggestions will be of great help.
In general, what I am trying to do is - make a hostnames list depending on what user id being passed by using the mappings object. And then make a call to the first hostname and get the response back. But if that hostname is down, then add to the block list and make a call to the second hostname in the list.
Here is the Stacktrace which I am seeing -
java.util.concurrent.TimeoutException\n\tat java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:258)
java.util.concurrent.FutureTask.get(FutureTask.java:119)\n\tat com.host.client.DataClient.getDataSync(DataClient.java:20)\n\tat
NOTE: For multiple userId's, we can have same server, meaning server A can get resolve to multiple userId's.
In DataClient class, at the below line:
public class DataClient implements IClient {
----code code---
Future<DataResponse> handle = getDataAsync(dataKeys);
//BELOW LINE IS PROBLEM
response = handle.get(dataKeys.getTimeout(), TimeUnit.MILLISECONDS); // <--- HERE
} catch (TimeoutException e) {
// log error
response = new DataResponse(null, DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
} catch (Exception e) {
// log error
response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR);
----code code-----
You have assigned a timeout to handle.get(...), which is timing out before your REST connections can respond. The rest connections themselves may or may not be timing out, but since you are timing out of get method of future before the completion of the execution of the thread, the blocking of hosts has no visible effect, while the code inside the call method of DataTask may be performing as expected. Hope this helps.
You asked about suggestions, so here are some suggestions:
1.) Unexpected return value
Method returns unexpectedly FALSE
if (ClientData.isHostBlocked(hostname)) //this may return always false! please check
2.) Exception-Handling
Are you really sure, that a RestClientException occurs?
Only when this exception occured, the host will be added to blocked list!
Your posted code seems to ignore logging (it is commented out!)
...catch (HttpClientErrorException ex) {
// make DataResponse
return dataResponse;
} catch (HttpServerErrorException ex) {
// make DataResponse
return dataResponse;
} catch (RestClientException ex) {
// If it comes here, then it means some of the servers are down.
// Add this server to block host list
ClientData.blockHost(hostname);
// log an error
} catch (Exception ex) {
// If it comes here, then it means some weird things has happened.
// log an error
// make DataResponse
}