Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions ambassador/src/main/java/com/iluwatar/ambassador/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,26 @@
package com.iluwatar.ambassador;

/**
*
* The ambassador pattern creates a helper service that sends network requests on behalf of a
* client. It is often used in cloud-based applications to offload features of a remote service.
*
* An ambassador service can be thought of as an out-of-process proxy that is co-located with
* the client. Similar to the proxy design pattern, the ambassador service provides an interface
* for another remote service. In addition to the interface, the ambassador provides extra
* functionality and features, specifically offloaded common connectivity tasks. This usually
* consists of monitoring, logging, routing, security etc. This is extremely useful in
* legacy applications where the codebase is difficult to modify and allows for improvements
* in the application's networking capabilities.
* <p>An ambassador service can be thought of as an out-of-process proxy that is co-located with
* the client. Similar to the proxy design pattern, the ambassador service provides an interface for
* another remote service. In addition to the interface, the ambassador provides extra functionality
* and features, specifically offloaded common connectivity tasks. This usually consists of
* monitoring, logging, routing, security etc. This is extremely useful in legacy applications where
* the codebase is difficult to modify and allows for improvements in the application's networking
* capabilities.
*
* In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while the
* <p>In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
* the
* ({@link RemoteService}) class represents a remote application.
*
*/
public class App {

/**
* Entry point
*/
* Entry point.
*/
public static void main(String[] args) {
Client host1 = new Client();
Client host2 = new Client();
Expand Down
5 changes: 2 additions & 3 deletions ambassador/src/main/java/com/iluwatar/ambassador/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@

package com.iluwatar.ambassador;

import org.slf4j.LoggerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A simple Client
* A simple Client.
*/
public class Client {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

package com.iluwatar.ambassador;

import static java.lang.Thread.sleep;

import com.iluwatar.ambassador.util.RandomProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.lang.Thread.sleep;

/**
* A remote legacy application represented by a Singleton implementation.
*/
Expand All @@ -55,9 +55,11 @@ private RemoteService() {
RemoteService(RandomProvider randomProvider) {
this.randomProvider = randomProvider;
}

/**
* Remote function takes a value and multiplies it by 10 taking a random amount of time.
* Will sometimes return -1. This imitates connectivity issues a client might have to account for.
* Remote function takes a value and multiplies it by 10 taking a random amount of time. Will
* sometimes return -1. This imitates connectivity issues a client might have to account for.
*
* @param value integer value to be multiplied.
* @return if waitTime is less than {@link RemoteService#THRESHOLD}, it returns value * 10,
* otherwise {@link RemoteServiceInterface#FAILURE}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,24 @@

package com.iluwatar.ambassador;

import static java.lang.Thread.sleep;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.lang.Thread.sleep;

/**
*
* ServiceAmbassador provides an interface for a ({@link Client}) to access ({@link RemoteService}).
* The interface adds logging, latency testing and usage of the service in a safe way that will not
* add stress to the remote service when connectivity issues occur.
*
*/
public class ServiceAmbassador implements RemoteServiceInterface {

private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
private static final int RETRIES = 3;
private static final int DELAY_MS = 3000;

ServiceAmbassador() {}
ServiceAmbassador() {
}

@Override
public long doRemoteFunction(int value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,34 @@

package com.iluwatar.async.method.invocation;

import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;

/**
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated value,
* <code>AsyncCallback</code> which can be provided to be executed on task completion and <code>AsyncExecutor</code>
* that manages the execution of the async tasks.
* <p>
* The main method shows example flow of async invocations. The main thread starts multiple tasks with variable
* durations and then continues its own work. When the main thread has done it's job it collects the results of the
* async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the
* tasks complete.
* <p>
* Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected
* in the main thread but the callbacks are executed within the worker threads. This should be noted when working with
* thread pools.
* <p>
* Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and
* ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming,
* the implementations are not trivial. This example does not take all possible scenarios into account but rather
* provides a simple version that helps to understand the pattern.
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
*
* <p>The main method shows example flow of async invocations. The main thread starts multiple
* tasks with variable durations and then continues its own work. When the main thread has done it's
* job it collects the results of the async tasks. Two of the tasks are handled with callbacks,
* meaning the callbacks are executed immediately when the tasks complete.
*
* <p>Noteworthy difference of thread usage between the async results and callbacks is that the
* async results are collected in the main thread but the callbacks are executed within the worker
* threads. This should be noted when working with thread pools.
*
* <p>Java provides its own implementations of async method invocation pattern. FutureTask,
* CompletableFuture and ExecutorService are the real world implementations of this pattern. But due
* to the nature of parallel programming, the implementations are not trivial. This example does not
* take all possible scenarios into account but rather provides a simple version that helps to
* understand the pattern.
*
* @see AsyncResult
* @see AsyncCallback
* @see AsyncExecutor
*
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
* @see java.util.concurrent.ExecutorService
Expand All @@ -61,27 +60,29 @@ public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
* Program entry point
* Program entry point.
*/
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
AsyncExecutor executor = new ThreadAsyncExecutor();

// start few async tasks with varying processing times, two last with callback handlers
AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
AsyncResult<Integer> asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
AsyncResult<String> asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
final AsyncResult<Integer> asyncResult1 = executor.startProcess(lazyval(10, 500));
final AsyncResult<String> asyncResult2 = executor.startProcess(lazyval("test", 300));
final AsyncResult<Long> asyncResult3 = executor.startProcess(lazyval(50L, 700));
final AsyncResult<Integer> asyncResult4 =
executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
final AsyncResult<String> asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));

// emulate processing in the current thread while async tasks are running in their own threads
Thread.sleep(350); // Oh boy I'm working hard here
log("Some hard work done");

// wait for completion of the tasks
Integer result1 = executor.endProcess(asyncResult1);
String result2 = executor.endProcess(asyncResult2);
Long result3 = executor.endProcess(asyncResult3);
final Integer result1 = executor.endProcess(asyncResult1);
final String result2 = executor.endProcess(asyncResult2);
final Long result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
asyncResult5.await();

Expand All @@ -94,10 +95,8 @@ public static void main(String[] args) throws Exception {
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
* @param value
* value to evaluate
* @param delayMillis
* artificial delay in milliseconds
* @param value value to evaluate
* @param delayMillis artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
private static <T> Callable<T> lazyval(T value, long delayMillis) {
Expand All @@ -111,8 +110,7 @@ private static <T> Callable<T> lazyval(T value, long delayMillis) {
/**
* Creates a simple callback that logs the complete status of the async result.
*
* @param name
* callback name
* @param name callback name
* @return new async callback
*/
private static <T> AsyncCallback<T> callback(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,17 @@
import java.util.Optional;

/**
*
* AsyncCallback interface
* AsyncCallback interface.
*
* @param <T>
*
* @param <T> Type of Result
*/
public interface AsyncCallback<T> {

/**
* Complete handler which is executed when async task is completed or fails execution.
*
* @param value the evaluated value from async task, undefined when execution fails
* @param ex empty value if execution succeeds, some exception if executions fails
* @param ex empty value if execution succeeds, some exception if executions fails
*/
void onComplete(T value, Optional<Exception> ex);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@
import java.util.concurrent.ExecutionException;

/**
*
* AsyncExecutor interface
*
* AsyncExecutor interface.
*/
public interface AsyncExecutor {

Expand All @@ -45,7 +43,7 @@ public interface AsyncExecutor {
* Starts processing of an async task. Returns immediately with async result. Executes callback
* when the task is completed.
*
* @param task task to be executed asynchronously
* @param task task to be executed asynchronously
* @param callback callback to be executed on task completion
* @return async result for the task
*/
Expand All @@ -57,7 +55,7 @@ public interface AsyncExecutor {
*
* @param asyncResult async result of a task
* @return evaluated value of the completed task
* @throws ExecutionException if execution has failed, containing the root cause
* @throws ExecutionException if execution has failed, containing the root cause
* @throws InterruptedException if the execution is interrupted
*/
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import java.util.concurrent.ExecutionException;

/**
* AsyncResult interface
* AsyncResult interface.
*
* @param <T> parameter returned when getValue is invoked
*/
public interface AsyncResult<T> {
Expand All @@ -42,7 +43,7 @@ public interface AsyncResult<T> {
* Gets the value of completed async task.
*
* @return evaluated value or throws ExecutionException if execution has failed
* @throws ExecutionException if execution has failed, containing the root cause
* @throws ExecutionException if execution has failed, containing the root cause
* @throws IllegalStateException if execution is not completed
*/
T getValue() throws ExecutionException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
import java.util.concurrent.atomic.AtomicInteger;

/**
*
* Implementation of async executor that creates a new thread for every task.
*
*/
public class ThreadAsyncExecutor implements AsyncExecutor {

/** Index for thread naming */
/**
* Index for thread naming.
*/
private final AtomicInteger idx = new AtomicInteger(0);

@Override
Expand All @@ -52,21 +52,23 @@ public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callba
} catch (Exception ex) {
result.setException(ex);
}
} , "executor-" + idx.incrementAndGet()).start();
}, "executor-" + idx.incrementAndGet()).start();
return result;
}

@Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException,
InterruptedException {
if (!asyncResult.isCompleted()) {
asyncResult.await();
}
return asyncResult.getValue();
}

/**
* Simple implementation of async result that allows completing it successfully with a value or exceptionally with an
* exception. A really simplified version from its real life cousins FutureTask and CompletableFuture.
* Simple implementation of async result that allows completing it successfully with a value or
* exceptionally with an exception. A really simplified version from its real life cousins
* FutureTask and CompletableFuture.
*
* @see java.util.concurrent.FutureTask
* @see java.util.concurrent.CompletableFuture
Expand All @@ -90,11 +92,10 @@ private static class CompletableResult<T> implements AsyncResult<T> {
}

/**
* Sets the value from successful execution and executes callback if available. Notifies any thread waiting for
* completion.
* Sets the value from successful execution and executes callback if available. Notifies any
* thread waiting for completion.
*
* @param value
* value of the evaluated task
* @param value value of the evaluated task
*/
void setValue(T value) {
this.value = value;
Expand All @@ -106,11 +107,10 @@ void setValue(T value) {
}

/**
* Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for
* completion.
* Sets the exception from failed execution and executes callback if available. Notifies any
* thread waiting for completion.
*
* @param exception
* exception of the failed task
* @param exception exception of the failed task
*/
void setException(Exception exception) {
this.exception = exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
import org.junit.jupiter.api.Test;

/**
*
* Application test
*
*/
public class AppTest {

Expand Down
Loading