J'envoie plus d'une demande à un service Web, ci-dessous il y a un exemple de cette demande. Il est important pour mon application d'obtenir la réponse du service Web, donc s'il y a une exception, l'application essaiera plusieurs fois d'obtenir la réponse.

À cause de cela, obtenir quelque chose de simple comme deviceList = serviceAdapter.getDevices(); est transformé en code ci-dessous.

boolean flag = true;
int counter = 1;
List<Device> deviceList = null;

while (flag) {
   try {
        deviceList  = serviceAdapter.getDevices(); 
        flag = false;
   } catch (Exception e) {
           try {
                if (counter == 5) {
                    System.out.println("Timeout Occured!");
                    flag = false;
                } else {
                    Thread.sleep(1000 * counter);
                    counter++;
                }
            } catch (InterruptedException e1) {
            }
    }
}

Et dans mon application, j'ai beaucoup de demandes, ce qui signifie qu'il y aura plus de codes laids. Existe-t-il un moyen d'appeler mes méthodes de requête en tant que paramètre pour une autre méthode, quelque chose comme ceci:

deviceList = wrapperMethod(serviceAdapter.getDevices());

Le problème est qu'il y aura différents types de requêtes, donc ils renverront différents types d'objets (liste, tableau, chaîne, int) et leurs paramètres changeront. Existe-t-il une solution appropriée en java pour ce problème?

1
W. Colosus 23 mai 2018 à 15:40

4 réponses

Meilleure réponse

Vous pouvez passer un Supplier<T> au wrapperMethod:

public static <T> T wrapperMethod (Supplier<T> supp) {
    boolean flag = true;
    int counter = 1;
    T value = null;

    while (flag) {
       try {
            value = supp.get(); 
            flag = false;
       } catch (Exception e) {
           try {
                if (counter == 5) {
                    System.out.println("Timeout Occured!");
                    flag = false;
                } else {
                    Thread.sleep(1000 * counter);
                    counter++;
                }
            } catch (InterruptedException e1) {
            }
        }
    }
}

Et appelez-le avec:

List<Device> deviceList = wrapperMethod (() -> serviceAdapter.getDevices());

J'ai peur, cependant, que cela limite les méthodes que vous appelez dans l'expression lambda à lancer uniquement des RuntimeException s.

0
Eran 23 mai 2018 à 12:46

Vous pouvez utiliser une implémentation de commande pour exécuter certains codes spécifiques:

Voici un exemple simple de commande

interface Command{
    void run();
}

Et quelques implémentations:

class SayHello implements Command{
    @Override
    public void run() {System.out.println("Hello World");}
}

class KillMe implements Command{
    public void run() { throw new RuntimeException();};
}

Tout ce que nous avons à faire pour exécuter ces méthodes est de recevoir une instance de Command et d'exécuter la méthode:

public static void execCommand(Command cmd) {
    cmd.run();
}

Et pour utiliser ça

public static void main(String[] args) {
    execCommand(new SayHello());
    execCommand(new KillMe());
}

Bonjour tout le monde
Exception dans le thread "main" java.lang.RuntimeException

Il accepte également l'expression lambda:

execCommand(() -> System.out.println("Say goodbye"));

Et référence de méthode:

public class Test{
    public static void testMe() {
        System.out.println("I work");
    }
}

execCommand(Test::testMe);

Notez que je n'ai pas spécifié que cela pouvait déclencher Exception donc je suis limité à une exception non vérifiée comme RuntimeException mais bien sûr void run() throws Exception pourrait être une solution. De cette façon, vous pouvez faire ce que vous voulez.

Exemple complet (avec exceptions):

public class Test {

    public static void main(String[] args) {
        try {
            execCommand(new SayHello());
            execCommand(() -> System.out.println("Say goodbye"));
            execCommand(Test::testMe);
            execCommand(new KillMe());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void testMe() throws IOException{
        System.out.println("I work");
    }

    public static void execCommand(Command cmd) throws Exception {
        cmd.run();
    }
}

interface Command{
    void run() throws Exception;
}

class SayHello implements Command{
    @Override
    public void run() {System.out.println("Hello World");}
}

class KillMe implements Command{
    public void run() { throw new RuntimeException();};
}

Production:

Hello World  
Say goodbye  
I work  
Exception in thread "main" java.lang.RuntimeException  
    at main.KillMe.run(Test.java:39)  
    at main.Test.execCommand(Test.java:25)
    at main.Test.main(Test.java:17)
0
AxelH 23 mai 2018 à 13:18

Vous pouvez utiliser l'annotation @RetryOnFailure de jcabi- aspects

Créez une méthode wrapper puis annotez-la pour activer la nouvelle tentative automatique en cas d'exception

Par exemple:

@RetryOnFailure(attempts = 5)
List<Device> retryWhenFailed(ServiceAdapter serviceAdapter) throws Exception {
    return serviceAdapter.getDevices();
}
0
Jed Cua 23 mai 2018 à 13:38

Cette solution utilise des génériques pour pouvoir gérer différents objets avec la plupart du même code et un exécutable pour exécuter la récupération.

Avec cette solution, il vous suffirait d'écrire les différents adaptateurs s'étendant de ServiceAdapter<T extends Fetchable> pour implémenter la logique pour récupérer les données pour chaque classe différente (qui devrait implémenter Fetchable).

Définissez une interface qui extrait les objets pouvant être récupérés par les différents services.

package so50488682;

public interface Fetchable {

}

L'objet à récupérer implémente cette interface afin que vous puissiez utiliser le même code pour différentes classes.

package so50488682;

public class Device implements Fetchable{
    private String  id;

    public Device(String id) {
        this.id = id;
    }
    public String toString() {
        return "I am device " + id;
    }

}

Définissez un résumé ServiceAdapter que les différents adaptateurs de service vont étendre pour implémenter la logique pour chaque type d'objet à récupérer. Nous ajoutons throws Exception à la méthode get () afin que cette méthode délègue simplement la gestion des exceptions au FetcherService et décide s'il doit réessayer ou échouer.

package so50488682;

import java.util.List;

public abstract class ServiceAdapter<T extends Fetchable> {

    public abstract List<T> get() throws Exception;
}

Ceci est un exemple d'implémentation effectuée pour obtenir des objets de la classe Device.

package so50488682;

import java.util.ArrayList;
import java.util.List;

public class DeviceServiceAdapter extends ServiceAdapter<Device>{

    @Override
    public List<Device> get() throws Exception{

        List<Device> rtn = new ArrayList<>();

        // fetch the data and put it into rtn, this is a mock
        Device d = new Device("1");
        rtn.add(d);
        d = new Device("2");
        rtn.add(d);
        d = new Device("3");
        rtn.add(d);
        //

        return rtn;
    }

}

Enfin, il s'agit d'une solution générique pour exécuter les différents adaptateurs de service.

public class FetcherService<T extends Fetchable> implements Runnable{

    List<T> result = new ArrayList<>();
    ServiceAdapter<T> serviceAdapter;

    @Override
    public void run() {

        boolean flag = true;
        int counter = 1;

        while (flag) {
           try {
                result  = serviceAdapter.get(); 
                flag = false;
           } catch (Exception e) {
                   try {
                        if (counter == 5) {
                            System.out.println("Timeout Occured!");
                            flag = false;
                        } else {
                            Thread.sleep(1000 * counter);
                            counter++;
                        }
                    } catch (InterruptedException e1) {
                        throw new RuntimeException("Got Interrupted in sleep", e);
                    }
            }
        }
    }

    public List<T> getResult() {
        return result;
    }

    public void setResult(List<T> result) {
        this.result = result;
    }

    public void setAdapter(ServiceAdapter<T> adapter) {
        this.serviceAdapter = adapter;
    }

}

Depuis le programme principal ou appelant, cela fonctionne comme ceci:

package so50488682;

import java.util.List;

public class SO50488682 {

    public static void main(String args[]) {

        try {
            DeviceServiceAdapter deviceServiceAdapter = new DeviceServiceAdapter();


            FetcherService<Device> deviceFetcherService = new FetcherService<>();
            deviceFetcherService.setAdapter(deviceServiceAdapter);
            deviceFetcherService.run();
            List<Device> devices = deviceFetcherService.getResult();

            for(Device device : devices) {
                System.out.println(device.toString());
            }

        }catch(Exception e) {
            System.out.println("Exception after retrying a couple of times");
            e.printStackTrace();
        }
    }

}
0
Juan 23 mai 2018 à 14:11