I'm trying to apply retry logic to a number of methods. For example, I have method1(String) and method2(int, String) that I would like to retry up to a certain number of times.
I would ideally like:
int count = 0;
while (count < MAX_TRIES) {
try {
//run method
} catch (Exception e) {
//increment count
//throw e if count == MAX_TRIES
}
}
inside a method where I could pass in as a parameter method1 or method2. Is there any way to do this? Thanks!
Sure:
public <T> T retry(Callable<T> callable) throws Exception {
int count = 0;
while (true) {
try {
return callable.call();
} catch (Exception e) {
count++;
if (count == MAX_TRIES) {
throw(e);
}
}
}
}
And then
retry(() -> doSomething(a, b));
retry(() -> doSomethingElse(a));
This simple implementation is not very flexible, and could use better exception handling, though. You could use a library to do that (disclaimer: I'm the original author of this library), or at least see how it works and reuse some of its ideas.
Related
I can't find a way to keep my program running after it catch and error.
For example, I have:
String[] num={"1","2","3","NotNumber","4","5"};
I want to convert all into Integer, so num[3] is invalid, but I want keep running to num[4] and num[5] after it catch the error.
How can I do so?
It would have helped if you had shown what you had tried so far, but the simplest solution is to wrap your int.parse() in a try/catch block and swallow the exception.
for(int i = 0; i < items.length; i++) {
try {
newItems[i] = Itemger.parseInt(items[i]);
catch(Exception ex) {
// do nothing
}
}
Put the try-catch block inside your iteration
JAVA 7
List<Integer> intList = new ArrayList<>();
for(String s : num) {
try {
Integer n = Integer.valueOf(s);
intList.add(n);
} catch (Exception ex) { continue; }
}
JAVA 8 Streams
List<Integer> intList = Arrays.asList(num)
.stream()
.map(s -> {
try {
return Integer.valueOf(s);
} catch(Exception ex) { return null;}
})
.filter(i -> i != null)
.collect(Collectors.toList());
I have needed to program something like this several times since programming Java:
Do something that might fail. If it fails, try it again but at most 3 (or 2 or 5) times.
This approach should work:
for (int i = 0; i < 3; i++) {
try {
doSomething();
} catch(BadException e) {
continue;
}
break;
}
But I do not think that it is very expressive. Do you have a better solution?
Something like this would be nice:
try (maxTimes = 3) {
doSomething();
} catch(BadException e) {
retry;
}
Or:
try (maxTimes = 3) {
doSomething();
if(somethingFailed()) {
retry;
}
}
But this is not possible with Java. Do you know a language with which it is possible?
Java does not let you invent your own syntax, but you can define your own method to help you express the concept with less code:
public static boolean retry(int maxTries, Runnable r) {
int tries = 0;
while (tries != maxTries) {
try {
r.run();
return true;
} catch (Exception e) {
tries++;
}
}
return false;
}
Now you can call this method like this:
boolean success = retry(5, () -> doSomething());
// Check success to see if the action succeeded
// If you do not care if the action is successful or not,
// ignore the returned value:
retry(5, () -> doSomethingElse());
Demo.
I tried to re-execute a method specified number of times when exception occurs in a method,
but I am unable re-execute the method
int maxretries=10;
void show(){
try{
display();
}
catch(Exception e)
{
for(int i=1;i<maxretries;i++){
display();//on first retry only I am getting exception
}
}
}
when I run the code it is executed for first retry and I am getting exception but I want to reexecute display() method upto it is excuted successfully with in maximum retries.
The call you coded inside the catch is not inside a try, so it will not catch exceptions.
You need to use other concepts to do this, either calling the whole function again, or coding a successive try block inside the catch (and a further try block inside that catch block, etc.), or coding the loop around the whole try block (probably the best approach).
What about this:
int maxretries = 10;
for (int i = 0; i < maxretries; i++) {
try {
display();
break;
} catch (Exception e) {
// log exception
e.printStackTrace();
}
}
In below program I am executing rerun method specified number of times even though exception occured with 5 sec time gap.
public class ReExecuteMethod {
public static void main(String[] args) throws Exception {
int count = 0;
while (count <= 10) {
try {
rerun();
break;
} catch (NullPointerException e) {
Thread.sleep(5000);
System.out.println(count);
count++;
}
}
System.out.println("Out of the while");
}
static void rerun() {
// throw new NullPointerException();
System.out.println("Hello");
}
}
I have a problem... it's basically that my code is ugly and I don't like it. I was wondering if there was a way to simplify it (I use java 8)
I have these "code blocks" that follow this pattern, I have about 5 or 6 of them within a method so this method looks very repetitive and ugly.
The loops are all the same, just the code varies inside.
Is there any way to simplify this?
CODE BLOCK EXAMPLE
String id = null;
for (int i=0; i< NUM_CHECKS; i++) {
// BEGIN VARIABLE CODE
id = getPrice();
if (id != null) break;
// END VARIABLE CODE
// sleep between checks
if (i < NUM_CHECKS -1) Thread.sleep(DELAY);
}
EXAMPLE
String id = null;
for (int i=0; i< NUM_CHECKS; i++) {
// BEGIN VARIABLE CODE
id = getPrice();
if (id != null) break;
// END VARIABLE CODE
// sleep between checks
if (i < NUM_CHECKS -1) Thread.sleep(DELAY);
}
for (int i=0; i< NUM_CHECKS; i++) {
// BEGIN VARIABLE CODE
x=x*2;
if (x>25) break;
// END VARIABLE CODE
// sleep between checks
if (i < NUM_CHECKS -1) Thread.sleep(DELAY);
} etc... a couple more blocks
How about coding an abstraction to contain all the boilerplate?
class MyLoop
{
private int numChecks;
private int delay;
public MyLoop(int numChecks, int delay) {...}
public void loopAndSleep(MyTask task)
throws InterruptedException
{
// Update: It is important to set properly the order of the looping conditions,
// to stop invoking hasEnded() as soon as i<numChecks==false (Thaks to Simon Eismann).
for (int i=0; i<numChecks && !task.hasEnded(); i++)
{
if (i < numChecks -1)
{
Thread.sleep(DELAY);
}
}
}
}
interface MyTask
{
public boolean hasEnded();
}
So, you can replace each one of your 5-6 places in your program by:
new MyLoop(NUM_CHECKS, DELAY).loopAndSleep(new MyTask(){...});
By properly extending MyTask you can give them specific status variables.
If you want to try some operation until the return value is available, you may do the following (Java-8 way):
public static <T> Optional<T> retryWithDelay(int numberOfChecks, int delay,
Supplier<Optional<T>> supplier) throws InterruptedException {
for(int i=0; i<numberOfChecks; i++) {
if(i > 0)
Thread.sleep(DELAY);
Optional<T> result = supplier.get();
if(result.isPresent()) return result;
}
}
And use it like this:
String id = retryWithDelay(NUM_CHECKS, DELAY, () -> Optional.ofNullable(getPrice()))
.orElse(null);
Or if you don't like optionals for some reason, you can stick with null:
public static <T> T retryWithDelay(int numberOfChecks, int delay,
Supplier<T> supplier) throws InterruptedException {
for (int i = 0; i < numberOfChecks; i++) {
if (i > 0)
Thread.sleep(delay);
T result = supplier.get();
if (result != null)
return result;
}
return null;
}
And use it like this:
String id = retryWithDelay(NUM_CHECKS, DELAY, () -> getPrice());
Or using method reference:
String id = retryWithDelay(NUM_CHECKS, DELAY, this::getPrice);
Note that the second example with x = 2*x is more difficult as it has some mutable state. It can be solved in dirty way like this:
AtomicInteger x = new AtomicInteger(1);
Integer result = retryWithDelay(NUM_CHECKS, DELAY, () -> {
int val = x.get()*2;
x.set(val);
return val > 25 ? val : null;
});
However I hope this version was just for illustration, not the real code.
There's also somewhat more sophisticated approach which probably abuses the API, but allows more flexibility. You can create an IntStream of increasing numbers, but they are available with given delay:
public static IntStream delayedStream(int numberOfChecks, int delay) {
return IntStream.range(0, numberOfChecks)
.peek(x -> {
if(x > 0) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// ignore
}
}
});
}
So the first problem can be solved now as:
String id = delayedStream(NUM_CHECKS, DELAY)
.mapToObj(x -> getPrice())
.filter(Objects::nonNull)
.findFirst().orElse(null);
And the second can be solved like this (assuming initial x value is 1):
int x = delayedStream(NUM_CHECKS, DELAY)
.map(idx -> 1 << (idx+1))
.filter(val -> val > 25)
.findFirst().orElse(-1);
The structure you provide is called a "polling loop" and you are correct, it is poor programming style, as are all the replies that contain the same polling loop.
It would be far better to use events.
Look in the "getPrice()" function, get to wherever that return value is being changed, and create an event when the change happens. Then in your code write a handler and in the handler do all the stuff that currently happens after your polling loop succeeds.
You can use recursion to make to loop reusable, but this would only make sense if you use the loop a lot.
public void loopWithDelay(int numberOfChecks, int delay, Runnable r) {
if (numberOfChecks != 0) {
r.run();
loopWithDelay(numberOfChecks - 1, delay, r);
Thread.sleep(DELAY);
}
}
The actual call would look something like this:
loopWithDelay(5, 1000, new Runnable() {
#Override
public void run() {
//Variable code goes here
}
});
On a general note, are you sure you want to wait DELAY seconds after an action or have the action occur every DELAY seconds?
EDIT:
I am dumb, no need for recursion, this works aswell:
public void loopWithDelay(int numberOfChecks, int delay, Runnable r) {
for (int i = 0; i < numberOfChecks; i++) {
r.run();
if (i != numberOfChecks -1)
Thread.sleep(DELAY);
}
}
I am trying to write a program to equate the value of any number to any power, and I'm suppose to implement exception handling for exponents less than zero which i successfully did and also exception handle for when the value is too large to output i.e. infinity.
heres my power class which contains the function Power :
public class power
{
// instance variables - replace the example below with your own
public static double Power(double base, int exp) throws IllegalArgumentException
{
if(exp < 0){
throw new IllegalArgumentException("Exponent cannot be less than zero");
}
else if(exp == 0){
return 1;
}
else{
return base * Power(base, exp-1);
}
}
}
Heres the Test class :
public class powerTest
{
public static void main(String [] args)
{
double [] base = {2.0, 3.0, 2.0, 2.0, 4.0 };
int [] exponent = {10, 9, -8, 6400, 53};
for (int i = 0; i < 5; i++) {
try {
double result = power.Power(base[i], exponent[i]);
System.out.println("result " + result);
}
catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
}
}
}
heres the output of the test :
result 1024.0
result 19683.0
Exponent cannot be less than zero
result Infinity
result 8.112963841460668E31
my question is how can i get "result infinity" to say something else through ArithmeticException handling something along the lines of "Floating point Overflow"?
thanks in advance.
When you catch the exception, here
catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
just do
System.out.println("Floating point Overflow")
as well(if you want to add more) or replace the first print with this statement
This way like you said, "you get result infinity" to say something else through ArithmeticException handling"
Not sure if this is what you are looking for, but you can test for infinity/overflow with an if statement as well:
if( mfloat == Float.POSITIVE_INFINITY ){
// handle infinite case, throw exception, etc.
}
So in your situation, you would do something like this:
public static double
Power(double base, int exp) throws IllegalArgumentException
{
if(exp < 0){
throw new IllegalArgumentException("Exponent less than zero");
}
else if(exp == 0){
return 1;
}
else{
double returnValue = base * Power(base, exp-1);
if(returnValue == Double.POSITIVE_INFINITY)
throw new ArithmeticException("Double overflowed");
return returnValue;
}
}