2015-03-12 5 views
1

Мне нужно создать библиотеку, в которой у меня будут синхронные и асинхронные методы.Как реализовать асинхронный вызов с обратным вызовом?

Основной логика моей библиотеки -

Клиента будет использовать нашу библиотеку, и они будут называть его пропусканием DataKey объекта строителя. Затем мы создадим URL-адрес с помощью этого объекта DataKey и сделаем клиентский вызов HTTP с этим URL-адресом, выполнив его, и после того, как мы получим ответ в виде строки JSON, мы отправим эту JSON-строку нашему клиенту, поскольку он создает DataResponse объект.

У меня будут синхронные и асинхронные методы. Некоторые клиенты вызовут метод executeSynchronous, чтобы получить такую ​​же функцию, и некоторый клиент будет называть наш метод executeAsynchronous и с помощью метода executeAsynchronous, они будут вызывать future.get там, где сам код.

Ниже мой интерфейс -

public interface Client { 

    // for synchronous 
    public DataResponse executeSynchronous(DataKey dataKey); 

    // for asynchronous 
    public Future<DataResponse> executeAsynchronous(DataKey dataKey); 
} 

Ниже мой DataResponse класс -

public class DataResponse { 

    private String response; 
    private DataErrorEnum error; 
    private DataStatusEnum status; 

    // constructor here 

    // and getters here 
} 

Ниже мой DataStatusEnum класс -

public enum DataStatusEnum { 
    SUCCESS, ERROR; 
} 

Ниже мой DataErrorEnum класс -

public enum DataErrorEnum { 
    NONE(200, "NONE", "Response is success."), 
    SERVER_DOWN(3145, "Server Down", "some long message here which can give more details."), 
    CLIENT_ERROR(3123, "Client Error", "some long message here which can give more details."), 
    TIMEOUT_ON_CLIENT(3187, "Client Timeout", "some long message here which can give more details."); 

    private final int code; 
    private final String status; 
    private final String description; 

    // constructor and getters here 
} 

И затем у меня есть мой DataClient, который реализует вышеуказанный интерфейс Client.

public class DataClient implements Client { 

    private RestTemplate restTemplate = new RestTemplate(); 
    private ExecutorService service = Executors.newFixedThreadPool(10); 

    // for synchronous call 
    @Override 
    public DataResponse executeSynchronous(DataKey dataKey) { 
     DataResponse dataResponse = null; 

     try { 
      Future<String> future = executeAsynchronous(dataKey); 
      dataResponse = future.get(dataKey.getTimeout(), TimeUnit.MILLISECONDS); 
     } catch (TimeoutException ex) { 
      PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey); 
      dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR); 
     } catch (Exception ex) { 
      PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey); 
      dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR); 
     } 

     return dataResponse; 
    } 

    //for asynchronous call 
    @Override 
    public Future<DataResponse> executeAsynchronous(DataKey dataKey) { 
     Future<DataResponse> future = null; 

     try { 
      Task task = new Task(dataKey, restTemplate); 
      future = executor.submit(task); 
     } catch (Exception ex) { 
      PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey); 
     } 

     return future; 
    } 
} 

Теперь ниже мой простой класс, который будет выполнять фактическую задачу -

public class Task implements Callable<DataResponse> { 

    private DataKey dataKey; 
    private RestTemplate restTemplate; 

    public Task(DataKey dataKey, RestTemplate restTemplate) { 
     this.dataKey = dataKey; 
     this.restTemplate = restTemplate; 
    } 

    @Override 
    public DataResponse call() throws Exception { 
     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, dataKey); 
      dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR); 
     } catch (Exception ex) { 
      PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey); 
      dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR); 
     } 

     return dataResponse; 
    } 

    // create a URL by using dataKey object 
    private String createURL() { 
     String url = somecode; 

     return url; 
    } 
} 

Постановка задачи: -

Как я уже упоминал выше, некоторые клиенты будут вызывать executeSynchronous метод, чтобы получить данные для этого идентификатора пользователя, которые они передают в объекте DataKey, и некоторые клиенты вызовут метод executeAsynchronous с объектом DataKey, но в последнем случае они будут делать future.get в их базе кода.

Если вы видите мой executeSynchronous метод, я делаю future.get после вызова executeAsynchronous метода и если есть TimeoutException, то я войти, используя PotoLogging класс, который является специфичным в нашей компании, и что журналы будут идти в какой-то другой сервис здесь, который мы используем для просмотра всех журналов ошибок на панели управления. И в основном это зависит от того, как мы регистрируем его с именами, чтобы мы могли видеть эти имена в панели управления.

Теперь проблема клиента в нашей компании можно также назвать executeAsynchronous метод, но это означает, что они будут делать future.get в их кодовой базы, и что также может привести к TimeoutException в своем коде, но я не могу заставить их войти так же, как Я делаю это.Так что мой вопрос - Есть ли способ, что я могу получить обратный вызов, если есть какие-либо TimeoutException так, что я могу войти это так, если кто-то звонит executeAsynchronous метод моей библиотеки в их базе кода -

PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey); 

мне нужно для этого, чтобы моя библиотека могла регистрировать TimeoutException в инструменте, который у нас есть в нашей компании, как мы этого хотим. Другой, я должен сказать каждому клиенту сделать это так, чтобы мы могли видеть его в нашей панели. Как я могу получить обратный вызов от асинхронного вызова и все еще использовать все возможности асинхронности?

Каков наилучший способ для этого?

ответ

2

Future - это просто интерфейс. Предоставьте реализацию, которая завершает экземпляр, возвращенный вашей службой. Сделайте делегирование всех своих вызовов фактическим Future и завершите эти вызовы соответствующими блоками try-catch.

Future<DataResponse> wrapper = new Future<DataResponse>() { 
    private final Future<DataResponse> delegate = future; 

    @Override 
    public boolean cancel(boolean mayInterruptIfRunning) { 
     return delegate.cancel(mayInterruptIfRunning); 
    } 

    @Override 
    public boolean isCancelled() { 
     return delegate.isCancelled(); 
    } 

    @Override 
    public boolean isDone() { 
     return delegate.isDone(); 
    } 

    @Override 
    public DataResponse get() throws InterruptedException, ExecutionException { 
     DataResponse dataResponse = null; 
     try { 
      delegate.get(); 
     } catch (TimeoutException ex) { 
      PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey); 
      dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR); 
     } 
     return dataResponse; 
    } 

    @Override 
    public DataResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 
     DataResponse dataResponse = null; 
     try { 
      delegate.get(timeout, unit); 
     } catch (TimeoutException ex) { 
      PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey); 
      dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR); 
     } 
     return dataResponse; 
    } 
}; 
return wrapper; 
+0

Спасибо за ваше предложение. Я не могу понять, как использовать это с моей текущей настройкой или мне нужно что-то изменить в моем текущем проекте? Я никогда не работал с обратным вызовом и будущей реализацией, поэтому мне было трудно понять это. Если возможно, вы можете помочь мне понять, как я буду использовать это? – john

+0

@david Вы делаете это в своем 'executeAsynchronous', беря' Future', возвращенный 'ExecutorService'. Вы завершаете этот экземпляр с помощью «обертки», указанной выше, и возвращаете этот экземпляр «wrapper». Обратите внимание, как анонимный класс в моем ответе имеет поле 'delegate', которое инициализируется вашим« будущим ». –

Смежные вопросы