Ý 
Servlet 2.2
JSP 1.1
Servlet 2.3
JSP 1.2
JSTL 1.0
Servlet 2.4
JSP 2.0
JSTL 1.1
Servlet 2.5
JSP 2.1
EL 2.1
JSTL 1.2
Servlet 3.0
JSP 2.2
EL 2.2
JSTL 1.2
Servlet 3.1
JSP 2.3
EL 3.0
JSTL 1.2
Ý


Ý
GET /hello?name=arwan HTTP/1.1
<html> … </html>
Tomcat
Undertow
Jetty
HttpServletRequest

HttpServletResponse
@WebServlet(urlPatterns = {"/hello"})
class HelloServlet extends HttpServlet { … }
Ý
Ý
Ý
Ý
Ý
Ý
Ý
Ý{
“content”: “…”
}
Ý
var es = new EventSource(‘/notification/stream');
es.onmessage = function (event) {
//
};
es.addEventListener(‘feed-notify', function(event) {
// ‘feed-notify'
console.log(event.data);
}, false);
es.onerror = function (event) {
//
};
@WebServlet("/notification/stream")

public class NotificationStreamServlet extends HttpServlet {

final static Log log = LogFactory.getLog(NotificationStreamServlet.class);


@Override

protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("in notification/stream");



response.setCharacterEncoding("utf-8");

response.setContentType("text/event-stream");



try {

NotificationStream notificationStream = new NotificationStream(obtainUsername(request));

Iterator<Notification> notifies = notificationStream.feedNotifies();



while (notifies.hasNext()) {

Notification notification = notifies.next();



ServletOutputStream outputStream = response.getOutputStream();

outputStream.write(("event: " + notification.getEvent() + "n").getBytes());

outputStream.write(("data: " + notification.getData() + "nn").getBytes());

outputStream.flush();

}

} catch (Exception error) {

log.error("error notification/stream : " + error.getMessage());

}



log.info("out notification/stream”);
}



String obtainUsername(HttpServletRequest request) {

return request.getParameter("username");

}

}
class FeedService {



final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";

final ObjectMapper MAPPER = new ObjectMapper();



FeedNotify getFeedNotify(String username) {

try {

URL url = new URL(String.format(FEED_NOTIFY_URL, username));



HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

urlConnection.setRequestMethod("GET");

urlConnection.setRequestProperty("Accept", "application/json");

urlConnection.setConnectTimeout(3000);

urlConnection.setReadTimeout(3000);



return MAPPER.readValue(urlConnection.getInputStream(), FeedNotify.class);

} catch (IOException error) {

throw new ServiceOperationException(error);

}

}



}
// FeedService , 

FeedService feedService = new FeedService();



String username = "guest";

Iterator<Notification> feedNotifies = Stream.generate(() -> feedService.getFeedNotify(username))

.map(Notification::of)

.iterator();





while (feedNotifies.hasNext()) {

Notification next = feedNotifies.next();



System.out.println(next);

}
Ý
@WebServlet(urlPatterns = "/async", asyncSupported = true)

public class SimpleAsyncServlet extends HttpServlet {

protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {


// 

AsyncContext asyncContext = request.startAsync(request, response);

// 

asyncContext.setTimeout(60 * 1000);


// 

asyncContext.addListener(new AsyncListener() {

//
public void onComplete(AsyncEvent event) throws IOException { … }
// AsyncContext.setTimeout() 

public void onTimeout(AsyncEvent event) throws IOException { … }

//
public void onError(AsyncEvent event) throws IOException { … }
// 

public void onStartAsync(AsyncEvent event) throws IOException { … }

});

// 

asyncContext.start(new Runnable() {

public void run() {

// 

asyncContext.complete(); // 

}

});

}

}
@WebServlet(urlPatterns = "/notification/stream/async", asyncSupported = true)

public class AsyncNotificationStreamServlet extends HttpServlet {



@Override

protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {

log.info("in notification/stream/async");



response.setCharacterEncoding("utf-8");

response.setContentType("text/event-stream");



AsyncContext asyncContext = request.startAsync();

asyncContext.setTimeout(30000);

asyncContext.addListener(new AsyncListener() {

public void onComplete(AsyncEvent event) throws IOException { … }

public void onTimeout(AsyncEvent event) throws IOException { … }

public void onError(AsyncEvent event) throws IOException { … }

public void onStartAsync(AsyncEvent event) throws IOException { … }

});

asyncContext.start(() -> {

try {

NotificationStream notificationStream = new NotificationStream(obtainUsername(request));

Iterator<Notification> notifies = notificationStream.feedNotifies();



while (notifies.hasNext() && !asyncDone.get()) {

Notification notification = notifies.next();



ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();

outputStream.write(("event: " + notification.getEvent() + "n").getBytes());

outputStream.write(("data: " + notification.getData() + "nn").getBytes());

outputStream.flush();

}

} catch (Exception error) {

log.error("error notification/stream/async - " + error.getMessage());

} finally {

asyncContext.complete();

}

});



log.info("out notification/stream/async");

}
}
Ý
Ý
Ý
Ý
class NotifyApi {



static Log LOG = LogFactory.getLog(NotifyApi.class);



final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";

final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";



final ObjectMapper MAPPER = new ObjectMapper();



FeedNotify getFeedNotify(String username) {

LOG.info("FeedNotify .");



try {

URL url = new URL(String.format(FEED_NOTIFY_URL, username));

HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

FeedNotify feedNotify = MAPPER.readValue(urlConnection.getInputStream(), FeedNotify.class);



LOG.info("FeedNotify .");



return feedNotify;

} catch (IOException error) {

throw new ServiceOperationException(error);

}

}



FriendRecommendationNotify getFriendRecommendationNotify(String username) {

LOG.info("FriendRecommendationNotify .");

try {

URL url = new URL(String.format(FRIEND_NOTIFY_URL, username));

// 



LOG.info("FriendRecommendationNotify .");



return notify;

} catch (IOException error) {

throw new ServiceOperationException(error);

}

}

}
public class SynchronousCallExample {



public static void main(String[] args) {





NotifyApi notifyApi = new NotifyApi();

String username = "guest";





FeedNotify feedNotify = notifyApi.getFeedNotify(username);



// feedNotify 





FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username);



// friendNotify 



}



}
FeedNotify
getFeedNotify(username)
HTTP /
FriendRecommendationNotify
getFriendRecommendationNotify(username)
HTTP /
public class AsynchronousCallByThreadExample {



static Log LOG = LogFactory.getLog(AsynchronousCallByThreadExample.class);



public static void main(String[] args) throws Exception {



NotifyApi notifyApi = new NotifyApi();

String username = "guest";



Thread feedThread = new Thread(new Runnable() {



@Override

public void run() {



FeedNotify feedNotify = notifyApi.getFeedNotify(username);

LOG.info(feedNotify);



}



});

feedThread.start();





Thread friendThread = new Thread(() -> {



FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username);

LOG.info(friendNotify);



});

friendThread.start();



}



}
<<Interface>>
Executor
<<Interface>>
ExecutorService
<<Interface>>
ScheduledExecutorService
ForkJoinPool
AbstractExecutorService
ThreadPoolExecutor
ScheduledThreadPoolExecutor
ExecutorService executorService = Executors.newFixedThreadPool(4);
public class AsynchronousCallByExecutorExample {



static Log LOG = LogFactory.getLog(AsynchronousCallByExecutorExample.class);



public static void main(String[] args) throws Exception {



NotifyApi notifyApi = new NotifyApi();

String username = "guest";





Executor executor = Executors.newFixedThreadPool(4);





executor.execute(new Runnable() {


@Override

public void run() {

FeedNotify feedNotify = notifyApi.getFeedNotify(username);

LOG.info(feedNotify);

}


});



executor.execute(() -> {


FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username);

LOG.info(friendNotify);


});



}



}
ExecutorService executorService = Executors.newCachedThreadPool();



Future<String> future = executorService.submit(new Callable<String>() {


@Override

public String call() throws Exception {



// 



return "asynchronous call";

}


});



String result = future.get(1, TimeUnit.SECONDS);
class NotifyApi {



static Log LOG = LogFactory.getLog(NotifyApi.class);



final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";

final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";



final ExecutorService executorService = Executors.newCachedThreadPool();

final ObjectMapper objectMapper = new ObjectMapper();



FeedNotify getFeedNotify(String username) { … }



Future<FeedNotify> getFeedNotifyForFuture(String username) {


return executorService.submit(new Callable<FeedNotify>() {


@Override

public FeedNotify call() throws Exception {

return getFeedNotify(username);

}


});

}



FriendRecommendationNotify getFriendRecommendationNotify(String username) { … }



Future<FriendRecommendationNotify> getFriendRecommendationNotifyForFuture(String username) {

return executorService.submit(() -> getFriendRecommendationNotify(username));

}



}
NotifyApi notifyApi = new NotifyApi();

String username = "guest";





Future<FeedNotify> feedFuture = notifyApi.getFeedNotifyForFuture(username);

Future<FriendRecommendationNotify> friendFuture = notifyApi.getFriendRecommendationNotifyForFuture(username);



for (;;) {

if (feedFuture.isDone()) {

FeedNotify feedNotify = feedFuture.get();



// feedNotify 



break;

}



// 

// feedFuture.cancel(true);


LOG.info("FeedNotify .");

Thread.sleep(100);

}





FriendRecommendationNotify friendNotify = friendFuture.get(1, TimeUnit.SECONDS);



// friendNotify
getFeedNotify(username)
HTTP /
feedFuture
HTTP /
friendFuture
getFriendRecommendationNotify(username)
feedFuture.isDone() or cancel()
feedFuture.get()
friendFuture.get()
NotifyApi notifyApi = new NotifyApi();

String username = "elton";





Future<User> userFuture = notifyApi.getUserForFuture(username);

for (;;) {

if (userFuture.isDone()) {

User user = userFuture.get();



FeedNotify feedNotify = notifyApi.getFeedNotifyForFuture(user)
.get(1, TimeUnit.SECONDS);



// 



break;

}



LOG.info("User .");

Thread.sleep(100);

}
NotifyApi notifyApi = new NotifyApi();

String username = "guest";
notifyApi.getFeedNotify(username, new CompletionHandler<FeedNotify>() {



@Override

public void onSuccess(FeedNotify result) {

// 

}



@Override

public void onFailure(Throwable ex) {

// 

}


});
class NotifyApi {

// 

interface CompletionHandler<R> extends SuccessCallback<R>, FailureCallback {
}
// 

interface SuccessCallback<R> {


void onSuccess(R result);


}
// 

interface FailureCallback {


void onFailure(Throwable ex);


}


}
class NotifyApi {



static Log LOG = LogFactory.getLog(NotifyApi.class);



final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";

final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";



final ExecutorService executorService = Executors.newCachedThreadPool();

final ObjectMapper objectMapper = new ObjectMapper();



FeedNotify getFeedNotify(String username) { … }



void getFeedNotify(String username, CompletionHandler<FeedNotify> completionHandler) {

executorService.execute(new Runnable() {


@Override

public void run() {

try {

completionHandler.onSuccess(getFeedNotify(username));

} catch (Exception error) {

completionHandler.onFailure(error);

}

}


});

}



FriendRecommendationNotify getFriendRecommendationNotify(String username) { … }



void getFriendRecommendationNotify(String username, CompletionHandler<FriendRecommendationNotify> handler) { … }



}
NotifyApi notifyApi = new NotifyApi();

String username = "guest";





notifyApi.getFeedNotify(username, new CompletionHandler<FeedNotify>() {



@Override

public void onSuccess(FeedNotify result) {

// 

}



@Override

public void onFailure(Throwable ex) {

// 

}


});





notifyApi.getFriendRecommendationNotify(username, new CompletionHandler<FriendRecommendationNotify>() {


@Override

public void onSuccess(FriendRecommendationNotify result) {

}


@Override

public void onFailure(Throwable ex) {

}


});


execute callback
getFeedNotify(username)
HTTP /
execute callback
getFriendRecommendationNotify(username)
HTTP /
NotifyApi notifyApi = new NotifyApi();

String username = "elton";





notifyApi.getUser(username, new NotifyApi.CompletionHandler<User>() {



@Override

public void onSuccess(User user) {



notifyApi.getFeedNotify(user, new NotifyApi.CompletionHandler<FeedNotify>() {



@Override

public void onSuccess(FeedNotify feedNotify) {

// 

}



@Override

public void onFailure(Throwable error) {

// 

}



});



}



@Override

public void onFailure(Throwable error) {

// 

}



});
CompletableFuture.supplyAsync(() -> {



// 



return "async task";

}).thenApply(result -> {



// 



return result.length();

}).exceptionally(error -> {



// 



return Integer.MIN_VALUE;

}).thenAccept(length -> {



// 



LOG.info("Length : " + length);

});
class NotifyApi {



static Log LOG = LogFactory.getLog(NotifyApi.class);



final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";

final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";



final ExecutorService executorService = Executors.newCachedThreadPool();

final ObjectMapper objectMapper = new ObjectMapper();



FeedNotify getFeedNotify(String username) { … }



CompletableFuture<FeedNotify> getFeedNotifyForCF(String username) {
return CompletableFuture.supplyAsync(new Supplier<FeedNotify>() {


@Override

public FeedNotify get() {

return getFeedNotify(username);

}


}, executorService);

}



FriendRecommendationNotify getFriendRecommendationNotify(String username) { … }



CompletableFuture<FriendRecommendationNotify> getFriendRecommendationNotifyForCF(String username) {

return CompletableFuture.supplyAsync(() -> getFriendRecommendationNotify(username), executorService);

}



}
NotifyApi notifyApi = new NotifyApi();

String username = "guest";





notifyApi.getFeedNotifyForCF(username).thenAccept(feedNotify -> {

// feedNotify 

});



notifyApi.getFriendRecommendationNotifyForCF(username).thenAccept(friendNotify -> {

// friendNotify 

});





// 2 

notifyApi.getUserForCF(username)

.thenCompose(notifyApi::getFeedNotifyForCF)

.thenAccept(notifications -> {

// 

});


class NotificationStreamObservable {



ExecutorService executorService = Executors.newFixedThreadPool(1);

Future<?> future = new FutureTask<>(() -> {}, null);



NotifyApi notifyApi = new NotifyApi();



String username;

List<NotificationStreamObserver> observers = new CopyOnWriteArrayList<>();



NotificationStreamObservable(String username) {

this.username = username;

}



void register(NotificationStreamObserver observer) {

observers.add(observer);

}



void unregister(NotificationStreamObserver observer) {

observers.remove(observer);

}



void subscribe() {

future = executorService.submit(() -> {

boolean running = true;

while (running) {

Notification feedNotify = Notification.of(notifyApi.getFeedNotify(username));

observers.forEach(observer -> observer.onNotification(feedNotify));
// 

}

});

}



void unsubscribe() {

future.cancel(true);

observers.clear();

}

}
interface NotificationStreamObserver {



void onNotification(Notification notification);



}


String username = "guest";



NotificationStreamObservable observable = new NotificationStreamObservable(username);

observable.register(notification -> {



// 



});

observable.subscribe();





// , 

// observable.unsubscribe();
@WebServlet(urlPatterns = "/notification/stream/async", asyncSupported = true)

public class AsyncNotificationStreamServlet extends HttpServlet {

@Override

protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {

log.info("in notification/stream/async");



response.setCharacterEncoding("utf-8");

response.setContentType("text/event-stream");



AtomicBoolean asyncDone = new AtomicBoolean(false);



AsyncContext asyncContext = request.startAsync();

asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);

asyncContext.addListener(new AsyncListener() {



@Override

public void onComplete(AsyncEvent event) throws IOException { }



@Override

public void onTimeout(AsyncEvent event) throws IOException { }



@Override

public void onError(AsyncEvent event) throws IOException { }



@Override

public void onStartAsync(AsyncEvent event) throws IOException { }



});


asyncContext.start(() -> {

//


});



log.info("out notification/stream/async");

}
}
@WebServlet(urlPatterns = "/notification/stream/async", asyncSupported = true)

public class AsyncNotificationStreamServlet extends HttpServlet {

@Override

protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {

log.info("in notification/stream/async");



response.setCharacterEncoding("utf-8");

response.setContentType("text/event-stream");



AtomicBoolean asyncDone = new AtomicBoolean(false);



AsyncContext asyncContext = request.startAsync();

asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);

asyncContext.addListener(new AsyncListener() {



@Override

public void onComplete(AsyncEvent event) throws IOException { }



@Override

public void onTimeout(AsyncEvent event) throws IOException { }



@Override

public void onError(AsyncEvent event) throws IOException { }



@Override

public void onStartAsync(AsyncEvent event) throws IOException { }



});


asyncContext.start(() -> {

//


});



log.info("out notification/stream/async");

}
}
Ý
Ý
public class NotificationStreamObservable extends Observable {



final static ExecutorService executorService = Executors.newCachedThreadPool();



Iterator<Notification> feedNotifies;

Future<?> feedFuture;



public NotificationStreamObservable(NotificationStream stream) {

this.feedNotifies = Objects.requireNonNull(stream.feedNotifies());

}



public void subscribe() {

feedFuture = executorService.submit(() -> {

boolean running = true;

while (running) {

if (countObservers() > 0) {

try {

Notification notification = feedNotifies.next();



setChanged();

notifyObservers(notification);

} catch (Exception error) {

running = false;



setChanged();

notifyObservers(error);

}

}

if (Thread.interrupted()) {

running = false;

}

Thread.yield();

}

});

}



public void unsubscribe() {

feedFuture.cancel(true);

deleteObservers();

}
}
class NotificationStreamObserver implements Observer, AsyncListener {



final AsyncContext asyncContext;



public NotificationStreamObserver(AsyncContext asyncContext) {

this.asyncContext = Objects.requireNonNull(asyncContext);

}



@Override

public void update(Observable observable, Object event) {

if (event instanceof Notification) {

handler((Notification) event);

}



// 

if (event instanceof Throwable) {

handlerError(observable, (Throwable) event);

}

}



@Override

public void onComplete(AsyncEvent event) throws IOException {

log.info("complete notification/stream/async-observer");

}



@Override

public void onTimeout(AsyncEvent event) throws IOException {

handlerError(new TimeoutException("timeout"));

}



@Override

public void onError(AsyncEvent event) throws IOException {

handlerError(event.getThrowable());

}



@Override

public void onStartAsync(AsyncEvent event) throws IOException { }

// handle, handleError
}
@WebServlet(urlPatterns = "/notification/stream/observer", asyncSupported = true)

public class ObserverNotificationStreamServlet extends HttpServlet {



@Override

protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {

log.info("in notification/stream/observer");



response.setCharacterEncoding("utf-8");

response.setContentType("text/event-stream");





AsyncContext asyncContext = request.startAsync(request, response);

asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);



NotificationStream notificationStream = new NotificationStream(obtainUsername(request));

NotificationStreamObservable streamObservable = new NotificationStreamObservable(notificationStream);



NotificationStreamObserver streamObserver = new NotificationStreamObserver(asyncContext);

streamObservable.addObserver(streamObserver);

asyncContext.addListener(streamObserver);



streamObservable.subscribe();



log.info("out notification/stream/observer");

}



String obtainUsername(HttpServletRequest request) {

String username = request.getParameter("username");

if (StringUtils.hasText(username)) {

return username;

}



return "anonymous";

}


}
Ý
Ý
Ý
Ý
public class NotificationStreamObservable extends Observable {



final static ExecutorService executorService = Executors.newCachedThreadPool();



Iterator<Notification> feedNotifies;

Future<?> feedFuture;



public NotificationStreamObservable(NotificationStream stream) {

this.feedNotifies = requireNonNull(stream).feedNotifies();

}



public void subscribe() {

feedFuture = executorService.submit(() -> {

boolean running = true;

while (running) {

if (countObservers() > 0) {

try {

Notification notification = feedNotifies.next();


setChanged();

notifyObservers(notification);

} catch (Exception error) {

running = false;



setChanged();

notifyObservers(error);

}

}
// 

}

});

}

//
}
public class NotificationStreamObservable extends Observable {



final static ExecutorService executorService = Executors.newCachedThreadPool();



Iterator<Notification> feedNotifies;

Future<?> feedFuture;
Iterator<Notification> friendNotifies;

Future<?> friendFuture;



public NotificationStreamObservable(NotificationStream stream) {

this.feedNotifies = requireNonNull(stream).feedNotifies();

this.friendNotifies = requireNonNull(stream).friendRecommendationNotifies();

}



public void subscribe() {

feedFuture = executorService.submit(() -> {

boolean running = true;

while (running) {

if (countObservers() > 0) {

try {

Notification feedNotify = feedNotifies.next();

setChanged();

notifyObservers(feedNotify);
Notification friendNotify = friendNotifies.next();

setChanged();

notifyObservers(friendNotify);

} catch (Exception error) {

running = false;



setChanged();

notifyObservers(error);

}

}
// 

}

});

}

//
}
feedNotifies.next()
friendNotifies.next()
feedNotify
friendNotify
class NotificationPublisher implements Runnable {



final Iterator<Notification> notifies;

final AtomicBoolean running = new AtomicBoolean(true);



NotificationPublisher(Iterator<Notification> notifies) {

this.notifies = notifies;

}



@Override

public void run() {

while (running.get()) {

if (countObservers() > 0) {

try {

Notification notification = notifies.next();



setChanged();

notifyObservers(notification);

} catch (Exception error) {

cancel();



setChanged();

notifyObservers(error);

}

}



if (Thread.interrupted()) {

cancel();

}



Thread.yield();

}

}



void cancel() {

running.set(false);

}



}
class MultipleNotificationStreamObservable extends Observable {



final static ExecutorService executor = Executors.newCachedThreadPool();



NotificationPublisher feedPublisher;

NotificationPublisher friendPublisher;



public MultipleNotificationStreamObservable(NotificationStream stream) {

this.feedPublisher = new NotificationPublisher(requireNonNull(stream).feedNotifies());

this.friendPublisher = new NotificationPublisher(requireNonNull(stream).friendRecommendationNotifies());

}



public void subscribe() {

executor.execute(feedPublisher);

executor.execute(friendPublisher);

}



public void unsubscribe() {

feedPublisher.cancel();

friendPublisher.cancel();



deleteObservers();

}



}
class MultipleNotificationStreamObserver implements Observer, AsyncListener {



@Override

public void update(Observable observable, Object event) {


synchronized (this) {

if (event instanceof Notification) {

handler((Notification) event);

}



// 

if (event instanceof Throwable) {

handlerError((Throwable) event);

}

}


}



//
}
class MultipleNotificationStreamObserver implements Observer, AsyncListener {



ReentrantLock reentrantLock = new ReentrantLock();



@Override

public void update(Observable observable, Object event) {



reentrantLock.lock();



if (event instanceof Notification) {

handler((Notification) event);

}



// 

if (event instanceof Throwable) {

handlerError((Throwable) event);

}



reentrantLock.unlock();


}



//
}
BlockingQueue<Object> notifyQueue = new LinkedBlockingQueue<>();
class SequentialNotifyObserversPublisher implements Runnable {



final Iterator<Notification> notifies;

final AtomicBoolean running = new AtomicBoolean(true);



SequentialNotifyObserversRunnable(Iterator<Notification> notifies) {

this.notifies = notifies;

}



@Override

public void run() {

while (running.get()) {

if (countObservers() > 0) {

try {

notifyQueue.put(notifies.next());

} catch (Exception error) {

cancel();



try {

notifyQueue.put(error);

} catch (InterruptedException ignore) { }

}

}



if (Thread.interrupted()) {

cancel();

}



Thread.yield();

}

}



void cancel() {

running.set(false);

}



}
class MultipleNotificationStreamObservable extends Observable {



final static ExecutorService executor = Executors.newFixedThreadPool(3);



BlockingQueue<Object> notifyQueue = new LinkedBlockingQueue<>();

Future<?> notifyFuture;



SequentialNotifyObserversPublisher sequentialFeed;

SequentialNotifyObserversPublisher sequentialFriend;



public MultipleNotificationStreamObservable(NotificationStream stream) {

this.sequentialFeed = new SequentialNotifyObserversRunnable(stream.feedNotifies());

this.sequentialFriend = new SequentialNotifyObserversRunnable(stream.friendRecommendationNotifies());

}



public void subscribe() {

executor.execute(sequentialFeed);

executor.execute(sequentialFriend);



notifyFuture = executor.submit(() -> {

boolean running = true;

while (running) {

if (countObservers() > 0) {

try {

Object event = notifyQueue.take();



setChanged();

notifyObservers(event);

} catch (InterruptedException e) {

running = false;

}

}

if (Thread.interrupted()) {

running = false;

}

Thread.yield();

}

});

}

// 

}
void printUserInfo(String username) {

NotifyApi notifyApi = new NotifyApi();



NotifyApi.User user = notifyApi.getUser(username);

Long feedCount = notifyApi.getFeedCount(username);

Long friendCount = notifyApi.getFriendCount(username);



UserInfo userInfo = new UserInfo(user.getName(), feedCount, friendCount);



System.out.println("User Info");

System.out.println("name = " + userInfo.getName());

System.out.println("feedCount = " + userInfo.getFeedCount());

System.out.println("friendCount = " + userInfo.getFriendCount());

}




printUserInfo("fay"); // 1

printUserInfo("murphy"); // 2

printUserInfo("nichole"); // 3
void printUserInfo(String username) { … }
ExecutorService executorService = Executors.newFixedThreadPool(3);


executorService.execute(() -> printUserInfo("fay")); // 1

executorService.execute(() -> printUserInfo("murphy")); // 2

executorService.execute(() -> printUserInfo("nichole")); // 3
void printUserInfo(String username) { … }
ExecutorService executorService = Executors.newFixedThreadPool(3);


executorService.execute(() -> printUserInfo("fay")); // 1

executorService.execute(() -> printUserInfo("murphy")); // 2

executorService.execute(() -> printUserInfo("nichole")); // 3


executorService.execute(() -> printUserInfo("phillip")); // 4

executorService.execute(() -> printUserInfo("adrienne")); // 5

executorService.execute(() -> printUserInfo("nita")); // 6
ExecutorService executorService = Executors.newFixedThreadPool(1);


void printUserInfo(String username) {

CompletableFuture.supplyAsync(() -> notifyApi.getUser(username), executorService)

.thenCompose(user -> {

CompletableFuture<Long> feedCountCF = CompletableFuture.supplyAsync(

() -> notifyApi.getFeedCount(username), executorService);



CompletableFuture<Long> friendCountCF = CompletableFuture.supplyAsync(

() -> notifyApi.getFriendCount(username), executorService);



return feedCountCF.thenCombineAsync(friendCountCF, (feedCount, friendCount) -> {

return new UserInfo(user.getName(), feedCount, friendCount);

}, executorService);

})

.thenAccept(userInfo -> {

System.out.println("User Info");

System.out.println("name = " + userInfo.getName());

System.out.println("feedCount = " + userInfo.getFeedCount());

System.out.println("friendCount = " + userInfo.getFriendCount());

});

}



printUserInfo("fay"); // 1

printUserInfo("murphy"); // 2

printUserInfo("nichole"); // 3
ExecutorService executorService = Executors.newFixedThreadPool(3);


void printUserInfo(String username) {

//


}





printUserInfo("fay"); // 1

printUserInfo("murphy"); // 2

printUserInfo("nichole"); // 3



printUserInfo("phillip"); // 4

printUserInfo("adrienne"); // 5

printUserInfo("nita"); // 6
class NotificationPublisher implements Runnable {



Iterator<Notification> notifies;



NotifyObserversRunnable(Iterator<Notification> notifies) {

this.notifies = notifies;

}



@Override

public void run() {

boolean running = true;

while (running) {

if (countObservers() > 0) {

try {

Notification notification = notifies.next();



setChanged();

notifyObservers(notification);

} catch (Exception error) {

running = false;



setChanged();

notifyObservers(error);

}

}



if (Thread.interrupted()) {

running = false;

}



Thread.yield();

}

}



}
class NotificationPublisher implements Runnable {

final Iterator<Notification> notifies;

final AtomicBoolean running = new AtomicBoolean(true);


NotifyObserversRunnable(Iterator<Notification> notifies) {

this.notifies = notifies;

}



public void run() {

if (countObservers() > 0) {

CompletableFuture<Object> completableFuture = new CompletableFuture<>();

completableFuture.thenAcceptAsync(event -> {

if (running.get()) {

setChanged();

notifyObservers(event);

}

schedule(20);

}, notifyExecutor).exceptionally(throwable -> {

cancel();

setChanged();

notifyObservers(throwable);

return null;

});

try {

completableFuture.complete(notifies.next());

} catch (Exception error) {

completableFuture.completeExceptionally(error);

}

}

schedule(50);

}


void schedule(long millis) {

if (running.get()) {

serviceExecutor.schedule(this, millis, TimeUnit.MILLISECONDS);

}

}


void cancel() {

running.set(false);

}

}
class AsyncMultipleNotificationStreamObservable extends Observable {



final static ScheduledExecutorService serviceExecutor = Executors.newScheduledThreadPool(2);

final static ScheduledExecutorService notifyExecutor = Executors.newScheduledThreadPool(1);



NotifyObserversRunnable feedPublisher;

NotifyObserversRunnable friendPublisher;



public AsyncMultipleNotificationStreamObservable(NotificationStream stream) {

this.feedPublisher = new NotifyObserversRunnable(requireNonNull(stream).feedNotifies());

this.friendPublisher = new NotifyObserversRunnable(requireNonNull(stream).friendRecommendationNotifies());

}



public void subscribe() {

serviceExecutor.schedule(feedPublisher, 10, TimeUnit.MILLISECONDS);

serviceExecutor.schedule(friendPublisher, 10, TimeUnit.MILLISECONDS);

}



public void unsubscribe() {

feedPublisher.cancel();

friendPublisher.cancel();

}
}
SocketChannel channel = SocketChannel.open();

channel.configureBlocking(true);



Socket socket = channel.socket();

socket.connect(new InetSocketAddress("www.naver.com", 80));



BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));

PrintStream output = new PrintStream(socket.getOutputStream());



output.println("GET / HTTP/1.0");

output.println();



StringBuilder response = new StringBuilder();

String line = input.readLine();

while (Objects.nonNull(line)) {

response.append(line).append("n");

line = input.readLine();



// body 

if ("".equals(line)) {

while (Objects.nonNull(line)) {

line = input.readLine();

}

}

}



input.close();

output.close();
PollSelectorImpl
WindowsSelectorImpl
KQueueSelectorImpl
EPollSelectorImpl
Selector selector = Selector.open();



SocketChannel socketChannel = SocketChannel.open();

socketChannel.configureBlocking(false);



socketChannel.connect(new InetSocketAddress("www.naver.com", 80));

socketChannel.register(selector, SelectionKey.OP_CONNECT);



while (socketChannel.isOpen()) {

if (selector.select() > 0) {

Set<SelectionKey> selectionKeys = selector.selectedKeys();

Iterator<SelectionKey> iterator = selectionKeys.iterator();



while (iterator.hasNext()) {

SelectionKey selectionKey = iterator.next();



if (selectionKey.isConnectable()) {

SocketChannel channel = (SocketChannel) selectionKey.channel();


//


} else if (selectionKey.isWritable()) {

SocketChannel channel = (SocketChannel) selectionKey.channel();



// 



} else if (selectionKey.isReadable()) {

SocketChannel channel = (SocketChannel) selectionKey.channel();



// 



}

// 

iterator.remove();

}

}

}
AsynchronousChannelGroup asynchronousChannelGroup = AsynchronousChannelGroup.withFixedThreadPool(1);
AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open(asynchronousChannelGroup);
SocketAddress socketAddress = new InetSocketAddress(“www.naver.com”, 80);



asynchronousSocketChannel.connect(socketAddress, null, new CompletionHandler<Void, Void>() {

@Override

public void completed(Void v, Void attachment) {

//


asynchronousSocketChannel.write(writeBuffer, null, new CompletionHandler<Integer, Void>() {

@Override

public void completed(Integer length, Void attachment) {
//


asynchronousSocketChannel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {

@Override

public void completed(Integer length, Void attachment) {
//
}

@Override

public void failed(Throwable error, Void attachment) {

// 

}

});

}

@Override

public void failed(Throwable error, Void attachment) {

// 

}

});

}

@Override

public void failed(Throwable exc, Void attachment) {

// 

}

});
✔ Netty(http://netty.io)
✔ gRPG(http://www.grpc.io)
✔ AsyncHttpClient(https://hc.apache.org/)
public FriendRecommendationNotify getRecommendationNotify(String username) throws ServiceOperationException {

try {

HttpGet request = new HttpGet(String.format(url, username, processing.name()));

HttpResponse response = httpClient.execute(request);

if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {

throw new ServiceOperationException();

}



return objectMapper.readValue(response.getEntity().getContent(), FriendRecommendationNotify.class);

} catch (IOException error) {

throw new ServiceOperationException(error);

}

}
public CompletableFuture<FriendRecommendationNotify> getRecommendationNotifyAsync(String username) {

CompletableFuture<FriendRecommendationNotify> completableFuture = new CompletableFuture<>();



HttpGet request = new HttpGet(String.format(url, username, processing.name()));

httpAsyncClient.execute(request, new FutureCallback<HttpResponse>() {



@Override

public void completed(HttpResponse response) {

try {

int statusCode = response.getStatusLine().getStatusCode();

InputStream content = response.getEntity().getContent();



if (statusCode != HttpStatus.SC_OK) {

completableFuture.completeExceptionally(new ServiceOperationException());

}



completableFuture.complete(objectMapper.readValue(content, FriendRecommendationNotify.class));

} catch (IOException error) {

completableFuture.completeExceptionally(new ServiceOperationException(error));

}

}



@Override

public void failed(Exception error) {

completableFuture.completeExceptionally(error);

}



@Override

public void cancelled() {

completableFuture.cancel(true);

}



});



return completableFuture;

}
@Test

public void getRecommendationNotify() throws Exception {

StopWatch stopWatch = new StopWatch("recommendationNotify");

stopWatch.start();



IntStream.rangeClosed(1, 10)

.mapToObj(value -> friendService.getRecommendationNotify("user-" + value))

.forEach(System.out::println);



stopWatch.stop();

System.out.println();

System.out.println(stopWatch.prettyPrint());

}



@Test

public void getRecommendationNotifyAsync() throws Exception {

StopWatch stopWatch = new StopWatch("recommendationNotifyAsync");

stopWatch.start();



IntStream.rangeClosed(1, 10)

.mapToObj(value -> friendService.getRecommendationNotifyAsync("user-" + value))

.collect(Collectors.toList())

.stream()

.map(CompletableFuture::join)

.forEach(System.out::println);



stopWatch.stop();

System.out.println();

System.out.println(stopWatch.prettyPrint());

}
class NotificationPublisher implements Runnable {



final Supplier<CompletableFuture<Notification>> notifies;

final AtomicBoolean running = new AtomicBoolean(true);



NotifyObserversRunnable(Supplier<CompletableFuture<Notification>> notifies) {

this.notifies = notifies;

}



@Override

public void run() {

if (countObservers() > 0) {

notifies.get().thenAcceptAsync(event -> {

if (running.get()) {

setChanged();

notifyObservers(event);

}



schedule(20);

}, notifyExecutor).exceptionally(throwable -> {

cancel();

setChanged();

notifyObservers(throwable);



return null;

});

} else {

schedule(50);

}

}



void schedule(long millis) {

if (running.get()) {

serviceExecutor.schedule(this, millis, TimeUnit.MILLISECONDS);

}

}



void cancel() {

running.set(false);

}

}
class NonBlockingAsyncMultipleNotificationStreamObservable extends Observable {



final static ScheduledExecutorService serviceExecutor = Executors.newScheduledThreadPool(1);

final static ScheduledExecutorService notifyExecutor = Executors.newScheduledThreadPool(1);



NotifyObserversRunnable feed;

NotifyObserversRunnable friend;



public NonBlockingAsyncMultipleNotificationStreamObservable(NotificationStream stream) {

requireNonNull(stream);



this.feed = new NotifyObserversRunnable(stream::feedNotifyAsync);

this.friend = new NotifyObserversRunnable(stream::friendRecommendationNotifyAsync);

}



public void subscribe() {

serviceExecutor.execute(feed);

serviceExecutor.execute(friend);

}



public void unsubscribe() {

if (nonNull(feed)) feed.cancel();

if (nonNull(friend)) friend.cancel();



deleteObservers();

}

}
class NonBlockingAsyncMultipleNotificationStreamObserver implements Observer, AsyncListener {



final NonBlockingAsyncMultipleNotificationStreamObservable streamObservable;

final AsyncContext asyncContext;



public NonBlockingAsyncMultipleNotificationStreamObserver(N…Observable streamObservable, AsyncContext asyncContext) {

this.streamObservable = Objects.requireNonNull(streamObservable);

this.asyncContext = Objects.requireNonNull(asyncContext);

}



@Override

public void update(Observable observable, Object event) {

if (event instanceof Notification) {

handler((Notification) event);

}



if (event instanceof Throwable) {

handlerError((Throwable) event);

}

}



private void handler(Notification notification) {

try {

log.info("revised notification/stream/non-blocking-async-concurrency : " + notification.getEvent());



ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();

outputStream.write(("event: " + notification.getEvent() + "n").getBytes());

outputStream.write(("data: " + notification.getData() + "nn").getBytes());

outputStream.flush();

} catch (IOException error) {

throw new DataWriteException(error);

}

}



private void handlerError(Throwable error) {

log.error("error notification/stream/non-blocking-async-concurrency : " + error.getMessage());



streamObservable.unsubscribe();

asyncContext.complete();

}

}
✔ 비봉쇄 입/출력을 위한 새로운 인터페이스
•ReadListener - 사용 가능한 데이터를 읽을 수 있는 방법
•WriteListener - 데이터 쓰기가 가능한 때를 알 수 있는 방법
✔ ServletInputStream 에 추가된 기능
•isFinished()
•isReady()
•setReadListener()
✔ ServletOutputStream 에 추가된 기능
•isReady()
•setWriterListener()
ServletInputStream servletInputStream = request.getInputStream();

servletInputStream.setReadListener(new ReadListener() {



@Override

public void onDataAvailable() throws IOException {

// 

}



@Override

public void onAllDataRead() throws IOException {

// 

}



@Override

public void onError(Throwable throwable) {

// 

}



});
ServletOutputStream servletOutputStream = response.getOutputStream();

servletOutputStream.setWriteListener(new WriteListener() {



@Override

public void onWritePossible() throws IOException {

// 

}



@Override

public void onError(Throwable throwable) {

// 

}



});
class NonBlockingAsyncMultipleNotificationStreamObserver implements Observer, AsyncListener, WriteListener {



final NonBlockingAsyncMultipleNotificationStreamObservable streamObservable;

final AsyncContext asyncContext;

final ServletOutputStream outputStream;



final AtomicInteger counter = new AtomicInteger(1);



public NonBlockingAsyncMultipleNotificationStreamObserver(N…Observable streamObservable, AsyncContext asyncContext) {

this.streamObservable = Objects.requireNonNull(streamObservable);

this.asyncContext = Objects.requireNonNull(asyncContext);

this.outputStream = asyncContext.getResponse().getOutputStream();

}

//


@Override

public void onWritePossible() throws IOException {

// ignore

}



@Override

public void onError(Throwable throwable) {

handlerError(throwable);

}



private void handler(Notification notification) {

String content = notification.toContent();



ioExecutor.schedule(() -> write(content.getBytes()), 10, TimeUnit.MILLISECONDS);

}

private void handlerError(Throwable error) {

log.error("error notification/stream/non-blocking-async-concurrency : " + error.getMessage());



streamObservable.unsubscribe();

asyncContext.complete();

}
// …
//
final static ScheduledExecutorService ioExecutor = Executors.newScheduledThreadPool(1);


private void write(byte[] data) {

if (outputStream.isReady()) {

try {

outputStream.write(data);



ioExecutor.schedule(this::flush, 10, TimeUnit.MILLISECONDS);

} catch (IOException error) {

handlerError(error);

}

} else {

ioExecutor.schedule(() -> write(data), 10, TimeUnit.MILLISECONDS);

}

}



private void flush() {

if (outputStream.isReady()) {

try {

outputStream.flush();

} catch (IOException error) {

handlerError(error);

}

} else {

ioExecutor.schedule(this::flush, 10, TimeUnit.MILLISECONDS);

}

}



}
Ý
Ý
Ý
Ý
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/O
Reactive Web - Servlet & Async, Non-blocking I/O

Reactive Web - Servlet & Async, Non-blocking I/O

  • 3.
  • 4.
    Servlet 2.2 JSP 1.1 Servlet2.3 JSP 1.2 JSTL 1.0 Servlet 2.4 JSP 2.0 JSTL 1.1 Servlet 2.5 JSP 2.1 EL 2.1 JSTL 1.2 Servlet 3.0 JSP 2.2 EL 2.2 JSTL 1.2 Servlet 3.1 JSP 2.3 EL 3.0 JSTL 1.2
  • 5.
  • 6.
    Ý GET /hello?name=arwan HTTP/1.1 <html>… </html> Tomcat Undertow Jetty HttpServletRequest
 HttpServletResponse @WebServlet(urlPatterns = {"/hello"}) class HelloServlet extends HttpServlet { … }
  • 9.
  • 10.
  • 12.
  • 13.
    var es =new EventSource(‘/notification/stream'); es.onmessage = function (event) { // }; es.addEventListener(‘feed-notify', function(event) { // ‘feed-notify' console.log(event.data); }, false); es.onerror = function (event) { // };
  • 14.
    @WebServlet("/notification/stream")
 public class NotificationStreamServletextends HttpServlet {
 final static Log log = LogFactory.getLog(NotificationStreamServlet.class); 
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { log.info("in notification/stream");
 
 response.setCharacterEncoding("utf-8");
 response.setContentType("text/event-stream");
 
 try {
 NotificationStream notificationStream = new NotificationStream(obtainUsername(request));
 Iterator<Notification> notifies = notificationStream.feedNotifies();
 
 while (notifies.hasNext()) {
 Notification notification = notifies.next();
 
 ServletOutputStream outputStream = response.getOutputStream();
 outputStream.write(("event: " + notification.getEvent() + "n").getBytes());
 outputStream.write(("data: " + notification.getData() + "nn").getBytes());
 outputStream.flush();
 }
 } catch (Exception error) {
 log.error("error notification/stream : " + error.getMessage());
 }
 
 log.info("out notification/stream”); }
 
 String obtainUsername(HttpServletRequest request) {
 return request.getParameter("username");
 }
 }
  • 15.
    class FeedService {
 
 finalString FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";
 final ObjectMapper MAPPER = new ObjectMapper();
 
 FeedNotify getFeedNotify(String username) {
 try {
 URL url = new URL(String.format(FEED_NOTIFY_URL, username));
 
 HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
 urlConnection.setRequestMethod("GET");
 urlConnection.setRequestProperty("Accept", "application/json");
 urlConnection.setConnectTimeout(3000);
 urlConnection.setReadTimeout(3000);
 
 return MAPPER.readValue(urlConnection.getInputStream(), FeedNotify.class);
 } catch (IOException error) {
 throw new ServiceOperationException(error);
 }
 }
 
 } // FeedService , 
 FeedService feedService = new FeedService();
 
 String username = "guest";
 Iterator<Notification> feedNotifies = Stream.generate(() -> feedService.getFeedNotify(username))
 .map(Notification::of)
 .iterator();
 
 
 while (feedNotifies.hasNext()) {
 Notification next = feedNotifies.next();
 
 System.out.println(next);
 }
  • 19.
  • 26.
    @WebServlet(urlPatterns = "/async",asyncSupported = true)
 public class SimpleAsyncServlet extends HttpServlet {
 protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception { 
 // 
 AsyncContext asyncContext = request.startAsync(request, response);
 // 
 asyncContext.setTimeout(60 * 1000); 
 // 
 asyncContext.addListener(new AsyncListener() {
 // public void onComplete(AsyncEvent event) throws IOException { … } // AsyncContext.setTimeout() 
 public void onTimeout(AsyncEvent event) throws IOException { … }
 // public void onError(AsyncEvent event) throws IOException { … } // 
 public void onStartAsync(AsyncEvent event) throws IOException { … }
 });
 // 
 asyncContext.start(new Runnable() {
 public void run() {
 // 
 asyncContext.complete(); // 
 }
 });
 }
 }
  • 27.
    @WebServlet(urlPatterns = "/notification/stream/async",asyncSupported = true)
 public class AsyncNotificationStreamServlet extends HttpServlet {
 
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
 log.info("in notification/stream/async");
 
 response.setCharacterEncoding("utf-8");
 response.setContentType("text/event-stream");
 
 AsyncContext asyncContext = request.startAsync();
 asyncContext.setTimeout(30000);
 asyncContext.addListener(new AsyncListener() {
 public void onComplete(AsyncEvent event) throws IOException { … }
 public void onTimeout(AsyncEvent event) throws IOException { … }
 public void onError(AsyncEvent event) throws IOException { … }
 public void onStartAsync(AsyncEvent event) throws IOException { … }
 });
 asyncContext.start(() -> {
 try {
 NotificationStream notificationStream = new NotificationStream(obtainUsername(request));
 Iterator<Notification> notifies = notificationStream.feedNotifies();
 
 while (notifies.hasNext() && !asyncDone.get()) {
 Notification notification = notifies.next();
 
 ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();
 outputStream.write(("event: " + notification.getEvent() + "n").getBytes());
 outputStream.write(("data: " + notification.getData() + "nn").getBytes());
 outputStream.flush();
 }
 } catch (Exception error) {
 log.error("error notification/stream/async - " + error.getMessage());
 } finally {
 asyncContext.complete();
 }
 });
 
 log.info("out notification/stream/async");
 } }
  • 32.
  • 35.
    class NotifyApi {
 
 staticLog LOG = LogFactory.getLog(NotifyApi.class);
 
 final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";
 final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";
 
 final ObjectMapper MAPPER = new ObjectMapper();
 
 FeedNotify getFeedNotify(String username) {
 LOG.info("FeedNotify .");
 
 try {
 URL url = new URL(String.format(FEED_NOTIFY_URL, username));
 HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
 FeedNotify feedNotify = MAPPER.readValue(urlConnection.getInputStream(), FeedNotify.class);
 
 LOG.info("FeedNotify .");
 
 return feedNotify;
 } catch (IOException error) {
 throw new ServiceOperationException(error);
 }
 }
 
 FriendRecommendationNotify getFriendRecommendationNotify(String username) {
 LOG.info("FriendRecommendationNotify .");
 try {
 URL url = new URL(String.format(FRIEND_NOTIFY_URL, username));
 // 
 
 LOG.info("FriendRecommendationNotify .");
 
 return notify;
 } catch (IOException error) {
 throw new ServiceOperationException(error);
 }
 }
 }
  • 36.
    public class SynchronousCallExample{
 
 public static void main(String[] args) {
 
 
 NotifyApi notifyApi = new NotifyApi();
 String username = "guest";
 
 
 FeedNotify feedNotify = notifyApi.getFeedNotify(username);
 
 // feedNotify 
 
 
 FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username);
 
 // friendNotify 
 
 }
 
 }
  • 38.
  • 40.
    public class AsynchronousCallByThreadExample{
 
 static Log LOG = LogFactory.getLog(AsynchronousCallByThreadExample.class);
 
 public static void main(String[] args) throws Exception {
 
 NotifyApi notifyApi = new NotifyApi();
 String username = "guest";
 
 Thread feedThread = new Thread(new Runnable() {
 
 @Override
 public void run() {
 
 FeedNotify feedNotify = notifyApi.getFeedNotify(username);
 LOG.info(feedNotify);
 
 }
 
 });
 feedThread.start();
 
 
 Thread friendThread = new Thread(() -> {
 
 FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username);
 LOG.info(friendNotify);
 
 });
 friendThread.start();
 
 }
 
 }
  • 43.
  • 44.
    public class AsynchronousCallByExecutorExample{
 
 static Log LOG = LogFactory.getLog(AsynchronousCallByExecutorExample.class);
 
 public static void main(String[] args) throws Exception {
 
 NotifyApi notifyApi = new NotifyApi();
 String username = "guest";
 
 
 Executor executor = Executors.newFixedThreadPool(4);
 
 
 executor.execute(new Runnable() { 
 @Override
 public void run() {
 FeedNotify feedNotify = notifyApi.getFeedNotify(username);
 LOG.info(feedNotify);
 } 
 });
 
 executor.execute(() -> { 
 FriendRecommendationNotify friendNotify = notifyApi.getFriendRecommendationNotify(username);
 LOG.info(friendNotify); 
 });
 
 }
 
 }
  • 46.
    ExecutorService executorService =Executors.newCachedThreadPool();
 
 Future<String> future = executorService.submit(new Callable<String>() { 
 @Override
 public String call() throws Exception {
 
 // 
 
 return "asynchronous call";
 } 
 });
 
 String result = future.get(1, TimeUnit.SECONDS);
  • 47.
    class NotifyApi {
 
 staticLog LOG = LogFactory.getLog(NotifyApi.class);
 
 final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";
 final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";
 
 final ExecutorService executorService = Executors.newCachedThreadPool();
 final ObjectMapper objectMapper = new ObjectMapper();
 
 FeedNotify getFeedNotify(String username) { … }
 
 Future<FeedNotify> getFeedNotifyForFuture(String username) { 
 return executorService.submit(new Callable<FeedNotify>() { 
 @Override
 public FeedNotify call() throws Exception {
 return getFeedNotify(username);
 } 
 });
 }
 
 FriendRecommendationNotify getFriendRecommendationNotify(String username) { … }
 
 Future<FriendRecommendationNotify> getFriendRecommendationNotifyForFuture(String username) {
 return executorService.submit(() -> getFriendRecommendationNotify(username));
 }
 
 }
  • 48.
    NotifyApi notifyApi =new NotifyApi();
 String username = "guest";
 
 
 Future<FeedNotify> feedFuture = notifyApi.getFeedNotifyForFuture(username);
 Future<FriendRecommendationNotify> friendFuture = notifyApi.getFriendRecommendationNotifyForFuture(username);
 
 for (;;) {
 if (feedFuture.isDone()) {
 FeedNotify feedNotify = feedFuture.get();
 
 // feedNotify 
 
 break;
 }
 
 // 
 // feedFuture.cancel(true); 
 LOG.info("FeedNotify .");
 Thread.sleep(100);
 }
 
 
 FriendRecommendationNotify friendNotify = friendFuture.get(1, TimeUnit.SECONDS);
 
 // friendNotify
  • 50.
  • 51.
    NotifyApi notifyApi =new NotifyApi();
 String username = "elton";
 
 
 Future<User> userFuture = notifyApi.getUserForFuture(username);
 for (;;) {
 if (userFuture.isDone()) {
 User user = userFuture.get();
 
 FeedNotify feedNotify = notifyApi.getFeedNotifyForFuture(user) .get(1, TimeUnit.SECONDS);
 
 // 
 
 break;
 }
 
 LOG.info("User .");
 Thread.sleep(100);
 }
  • 52.
    NotifyApi notifyApi =new NotifyApi();
 String username = "guest"; notifyApi.getFeedNotify(username, new CompletionHandler<FeedNotify>() {
 
 @Override
 public void onSuccess(FeedNotify result) {
 // 
 }
 
 @Override
 public void onFailure(Throwable ex) {
 // 
 } 
 });
  • 53.
    class NotifyApi {
 //
 interface CompletionHandler<R> extends SuccessCallback<R>, FailureCallback { } // 
 interface SuccessCallback<R> { 
 void onSuccess(R result); 
 } // 
 interface FailureCallback { 
 void onFailure(Throwable ex); 
 } 
 }
  • 54.
    class NotifyApi {
 
 staticLog LOG = LogFactory.getLog(NotifyApi.class);
 
 final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";
 final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";
 
 final ExecutorService executorService = Executors.newCachedThreadPool();
 final ObjectMapper objectMapper = new ObjectMapper();
 
 FeedNotify getFeedNotify(String username) { … }
 
 void getFeedNotify(String username, CompletionHandler<FeedNotify> completionHandler) {
 executorService.execute(new Runnable() { 
 @Override
 public void run() {
 try {
 completionHandler.onSuccess(getFeedNotify(username));
 } catch (Exception error) {
 completionHandler.onFailure(error);
 }
 } 
 });
 }
 
 FriendRecommendationNotify getFriendRecommendationNotify(String username) { … }
 
 void getFriendRecommendationNotify(String username, CompletionHandler<FriendRecommendationNotify> handler) { … }
 
 }
  • 55.
    NotifyApi notifyApi =new NotifyApi();
 String username = "guest";
 
 
 notifyApi.getFeedNotify(username, new CompletionHandler<FeedNotify>() {
 
 @Override
 public void onSuccess(FeedNotify result) {
 // 
 }
 
 @Override
 public void onFailure(Throwable ex) {
 // 
 } 
 });
 
 
 notifyApi.getFriendRecommendationNotify(username, new CompletionHandler<FriendRecommendationNotify>() { 
 @Override
 public void onSuccess(FriendRecommendationNotify result) {
 } 
 @Override
 public void onFailure(Throwable ex) {
 } 
 }); 

  • 57.
    execute callback getFeedNotify(username) HTTP / executecallback getFriendRecommendationNotify(username) HTTP /
  • 58.
    NotifyApi notifyApi =new NotifyApi();
 String username = "elton";
 
 
 notifyApi.getUser(username, new NotifyApi.CompletionHandler<User>() {
 
 @Override
 public void onSuccess(User user) {
 
 notifyApi.getFeedNotify(user, new NotifyApi.CompletionHandler<FeedNotify>() {
 
 @Override
 public void onSuccess(FeedNotify feedNotify) {
 // 
 }
 
 @Override
 public void onFailure(Throwable error) {
 // 
 }
 
 });
 
 }
 
 @Override
 public void onFailure(Throwable error) {
 // 
 }
 
 });
  • 59.
    CompletableFuture.supplyAsync(() -> {
 
 //
 
 return "async task";
 }).thenApply(result -> {
 
 // 
 
 return result.length();
 }).exceptionally(error -> {
 
 // 
 
 return Integer.MIN_VALUE;
 }).thenAccept(length -> {
 
 // 
 
 LOG.info("Length : " + length);
 });
  • 60.
    class NotifyApi {
 
 staticLog LOG = LogFactory.getLog(NotifyApi.class);
 
 final String FEED_NOTIFY_URL = "http://localhost:9000/user/%s/feed";
 final String FRIEND_NOTIFY_URL = "http://localhost:9000/user/%s/friend-recommendation";
 
 final ExecutorService executorService = Executors.newCachedThreadPool();
 final ObjectMapper objectMapper = new ObjectMapper();
 
 FeedNotify getFeedNotify(String username) { … }
 
 CompletableFuture<FeedNotify> getFeedNotifyForCF(String username) { return CompletableFuture.supplyAsync(new Supplier<FeedNotify>() { 
 @Override
 public FeedNotify get() {
 return getFeedNotify(username);
 } 
 }, executorService);
 }
 
 FriendRecommendationNotify getFriendRecommendationNotify(String username) { … }
 
 CompletableFuture<FriendRecommendationNotify> getFriendRecommendationNotifyForCF(String username) {
 return CompletableFuture.supplyAsync(() -> getFriendRecommendationNotify(username), executorService);
 }
 
 }
  • 61.
    NotifyApi notifyApi =new NotifyApi();
 String username = "guest";
 
 
 notifyApi.getFeedNotifyForCF(username).thenAccept(feedNotify -> {
 // feedNotify 
 });
 
 notifyApi.getFriendRecommendationNotifyForCF(username).thenAccept(friendNotify -> {
 // friendNotify 
 });
 
 
 // 2 
 notifyApi.getUserForCF(username)
 .thenCompose(notifyApi::getFeedNotifyForCF)
 .thenAccept(notifications -> {
 // 
 });
  • 63.
  • 64.
    class NotificationStreamObservable {
 
 ExecutorServiceexecutorService = Executors.newFixedThreadPool(1);
 Future<?> future = new FutureTask<>(() -> {}, null);
 
 NotifyApi notifyApi = new NotifyApi();
 
 String username;
 List<NotificationStreamObserver> observers = new CopyOnWriteArrayList<>();
 
 NotificationStreamObservable(String username) {
 this.username = username;
 }
 
 void register(NotificationStreamObserver observer) {
 observers.add(observer);
 }
 
 void unregister(NotificationStreamObserver observer) {
 observers.remove(observer);
 }
 
 void subscribe() {
 future = executorService.submit(() -> {
 boolean running = true;
 while (running) {
 Notification feedNotify = Notification.of(notifyApi.getFeedNotify(username));
 observers.forEach(observer -> observer.onNotification(feedNotify)); // 
 }
 });
 }
 
 void unsubscribe() {
 future.cancel(true);
 observers.clear();
 }
 }
  • 65.
    interface NotificationStreamObserver {
 
 voidonNotification(Notification notification);
 
 } 
 String username = "guest";
 
 NotificationStreamObservable observable = new NotificationStreamObservable(username);
 observable.register(notification -> {
 
 // 
 
 });
 observable.subscribe();
 
 
 // , 
 // observable.unsubscribe();
  • 68.
    @WebServlet(urlPatterns = "/notification/stream/async",asyncSupported = true)
 public class AsyncNotificationStreamServlet extends HttpServlet {
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
 log.info("in notification/stream/async");
 
 response.setCharacterEncoding("utf-8");
 response.setContentType("text/event-stream");
 
 AtomicBoolean asyncDone = new AtomicBoolean(false);
 
 AsyncContext asyncContext = request.startAsync();
 asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);
 asyncContext.addListener(new AsyncListener() {
 
 @Override
 public void onComplete(AsyncEvent event) throws IOException { }
 
 @Override
 public void onTimeout(AsyncEvent event) throws IOException { }
 
 @Override
 public void onError(AsyncEvent event) throws IOException { }
 
 @Override
 public void onStartAsync(AsyncEvent event) throws IOException { }
 
 }); 
 asyncContext.start(() -> {
 // 
 });
 
 log.info("out notification/stream/async");
 } }
  • 69.
    @WebServlet(urlPatterns = "/notification/stream/async",asyncSupported = true)
 public class AsyncNotificationStreamServlet extends HttpServlet {
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
 log.info("in notification/stream/async");
 
 response.setCharacterEncoding("utf-8");
 response.setContentType("text/event-stream");
 
 AtomicBoolean asyncDone = new AtomicBoolean(false);
 
 AsyncContext asyncContext = request.startAsync();
 asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);
 asyncContext.addListener(new AsyncListener() {
 
 @Override
 public void onComplete(AsyncEvent event) throws IOException { }
 
 @Override
 public void onTimeout(AsyncEvent event) throws IOException { }
 
 @Override
 public void onError(AsyncEvent event) throws IOException { }
 
 @Override
 public void onStartAsync(AsyncEvent event) throws IOException { }
 
 }); 
 asyncContext.start(() -> {
 // 
 });
 
 log.info("out notification/stream/async");
 } }
  • 70.
  • 71.
    public class NotificationStreamObservableextends Observable {
 
 final static ExecutorService executorService = Executors.newCachedThreadPool();
 
 Iterator<Notification> feedNotifies;
 Future<?> feedFuture;
 
 public NotificationStreamObservable(NotificationStream stream) {
 this.feedNotifies = Objects.requireNonNull(stream.feedNotifies());
 }
 
 public void subscribe() {
 feedFuture = executorService.submit(() -> {
 boolean running = true;
 while (running) {
 if (countObservers() > 0) {
 try {
 Notification notification = feedNotifies.next();
 
 setChanged();
 notifyObservers(notification);
 } catch (Exception error) {
 running = false;
 
 setChanged();
 notifyObservers(error);
 }
 }
 if (Thread.interrupted()) {
 running = false;
 }
 Thread.yield();
 }
 });
 }
 
 public void unsubscribe() {
 feedFuture.cancel(true);
 deleteObservers();
 } }
  • 72.
    class NotificationStreamObserver implementsObserver, AsyncListener {
 
 final AsyncContext asyncContext;
 
 public NotificationStreamObserver(AsyncContext asyncContext) {
 this.asyncContext = Objects.requireNonNull(asyncContext);
 }
 
 @Override
 public void update(Observable observable, Object event) {
 if (event instanceof Notification) {
 handler((Notification) event);
 }
 
 // 
 if (event instanceof Throwable) {
 handlerError(observable, (Throwable) event);
 }
 }
 
 @Override
 public void onComplete(AsyncEvent event) throws IOException {
 log.info("complete notification/stream/async-observer");
 }
 
 @Override
 public void onTimeout(AsyncEvent event) throws IOException {
 handlerError(new TimeoutException("timeout"));
 }
 
 @Override
 public void onError(AsyncEvent event) throws IOException {
 handlerError(event.getThrowable());
 }
 
 @Override
 public void onStartAsync(AsyncEvent event) throws IOException { }
 // handle, handleError }
  • 73.
    @WebServlet(urlPatterns = "/notification/stream/observer",asyncSupported = true)
 public class ObserverNotificationStreamServlet extends HttpServlet {
 
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response) throws Exception {
 log.info("in notification/stream/observer");
 
 response.setCharacterEncoding("utf-8");
 response.setContentType("text/event-stream");
 
 
 AsyncContext asyncContext = request.startAsync(request, response);
 asyncContext.setTimeout((new Random().nextInt(5) + 5) * 1000);
 
 NotificationStream notificationStream = new NotificationStream(obtainUsername(request));
 NotificationStreamObservable streamObservable = new NotificationStreamObservable(notificationStream);
 
 NotificationStreamObserver streamObserver = new NotificationStreamObserver(asyncContext);
 streamObservable.addObserver(streamObserver);
 asyncContext.addListener(streamObserver);
 
 streamObservable.subscribe();
 
 log.info("out notification/stream/observer");
 }
 
 String obtainUsername(HttpServletRequest request) {
 String username = request.getParameter("username");
 if (StringUtils.hasText(username)) {
 return username;
 }
 
 return "anonymous";
 } 
 }
  • 77.
  • 78.
    public class NotificationStreamObservableextends Observable {
 
 final static ExecutorService executorService = Executors.newCachedThreadPool();
 
 Iterator<Notification> feedNotifies;
 Future<?> feedFuture;
 
 public NotificationStreamObservable(NotificationStream stream) {
 this.feedNotifies = requireNonNull(stream).feedNotifies();
 }
 
 public void subscribe() {
 feedFuture = executorService.submit(() -> {
 boolean running = true;
 while (running) {
 if (countObservers() > 0) {
 try {
 Notification notification = feedNotifies.next(); 
 setChanged();
 notifyObservers(notification);
 } catch (Exception error) {
 running = false;
 
 setChanged();
 notifyObservers(error);
 }
 } // 
 }
 });
 }
 // }
  • 79.
    public class NotificationStreamObservableextends Observable {
 
 final static ExecutorService executorService = Executors.newCachedThreadPool();
 
 Iterator<Notification> feedNotifies;
 Future<?> feedFuture; Iterator<Notification> friendNotifies;
 Future<?> friendFuture;
 
 public NotificationStreamObservable(NotificationStream stream) {
 this.feedNotifies = requireNonNull(stream).feedNotifies();
 this.friendNotifies = requireNonNull(stream).friendRecommendationNotifies();
 }
 
 public void subscribe() {
 feedFuture = executorService.submit(() -> {
 boolean running = true;
 while (running) {
 if (countObservers() > 0) {
 try {
 Notification feedNotify = feedNotifies.next();
 setChanged();
 notifyObservers(feedNotify); Notification friendNotify = friendNotifies.next();
 setChanged();
 notifyObservers(friendNotify);
 } catch (Exception error) {
 running = false;
 
 setChanged();
 notifyObservers(error);
 }
 } // 
 }
 });
 }
 // }
  • 80.
  • 81.
    class NotificationPublisher implementsRunnable {
 
 final Iterator<Notification> notifies;
 final AtomicBoolean running = new AtomicBoolean(true);
 
 NotificationPublisher(Iterator<Notification> notifies) {
 this.notifies = notifies;
 }
 
 @Override
 public void run() {
 while (running.get()) {
 if (countObservers() > 0) {
 try {
 Notification notification = notifies.next();
 
 setChanged();
 notifyObservers(notification);
 } catch (Exception error) {
 cancel();
 
 setChanged();
 notifyObservers(error);
 }
 }
 
 if (Thread.interrupted()) {
 cancel();
 }
 
 Thread.yield();
 }
 }
 
 void cancel() {
 running.set(false);
 }
 
 }
  • 82.
    class MultipleNotificationStreamObservable extendsObservable {
 
 final static ExecutorService executor = Executors.newCachedThreadPool();
 
 NotificationPublisher feedPublisher;
 NotificationPublisher friendPublisher;
 
 public MultipleNotificationStreamObservable(NotificationStream stream) {
 this.feedPublisher = new NotificationPublisher(requireNonNull(stream).feedNotifies());
 this.friendPublisher = new NotificationPublisher(requireNonNull(stream).friendRecommendationNotifies());
 }
 
 public void subscribe() {
 executor.execute(feedPublisher);
 executor.execute(friendPublisher);
 }
 
 public void unsubscribe() {
 feedPublisher.cancel();
 friendPublisher.cancel();
 
 deleteObservers();
 }
 
 }
  • 86.
    class MultipleNotificationStreamObserver implementsObserver, AsyncListener {
 
 @Override
 public void update(Observable observable, Object event) { 
 synchronized (this) {
 if (event instanceof Notification) {
 handler((Notification) event);
 }
 
 // 
 if (event instanceof Throwable) {
 handlerError((Throwable) event);
 }
 } 
 }
 
 // }
  • 87.
    class MultipleNotificationStreamObserver implementsObserver, AsyncListener {
 
 ReentrantLock reentrantLock = new ReentrantLock();
 
 @Override
 public void update(Observable observable, Object event) {
 
 reentrantLock.lock();
 
 if (event instanceof Notification) {
 handler((Notification) event);
 }
 
 // 
 if (event instanceof Throwable) {
 handlerError((Throwable) event);
 }
 
 reentrantLock.unlock(); 
 }
 
 // }
  • 89.
    BlockingQueue<Object> notifyQueue =new LinkedBlockingQueue<>(); class SequentialNotifyObserversPublisher implements Runnable {
 
 final Iterator<Notification> notifies;
 final AtomicBoolean running = new AtomicBoolean(true);
 
 SequentialNotifyObserversRunnable(Iterator<Notification> notifies) {
 this.notifies = notifies;
 }
 
 @Override
 public void run() {
 while (running.get()) {
 if (countObservers() > 0) {
 try {
 notifyQueue.put(notifies.next());
 } catch (Exception error) {
 cancel();
 
 try {
 notifyQueue.put(error);
 } catch (InterruptedException ignore) { }
 }
 }
 
 if (Thread.interrupted()) {
 cancel();
 }
 
 Thread.yield();
 }
 }
 
 void cancel() {
 running.set(false);
 }
 
 }
  • 90.
    class MultipleNotificationStreamObservable extendsObservable {
 
 final static ExecutorService executor = Executors.newFixedThreadPool(3);
 
 BlockingQueue<Object> notifyQueue = new LinkedBlockingQueue<>();
 Future<?> notifyFuture;
 
 SequentialNotifyObserversPublisher sequentialFeed;
 SequentialNotifyObserversPublisher sequentialFriend;
 
 public MultipleNotificationStreamObservable(NotificationStream stream) {
 this.sequentialFeed = new SequentialNotifyObserversRunnable(stream.feedNotifies());
 this.sequentialFriend = new SequentialNotifyObserversRunnable(stream.friendRecommendationNotifies());
 }
 
 public void subscribe() {
 executor.execute(sequentialFeed);
 executor.execute(sequentialFriend);
 
 notifyFuture = executor.submit(() -> {
 boolean running = true;
 while (running) {
 if (countObservers() > 0) {
 try {
 Object event = notifyQueue.take();
 
 setChanged();
 notifyObservers(event);
 } catch (InterruptedException e) {
 running = false;
 }
 }
 if (Thread.interrupted()) {
 running = false;
 }
 Thread.yield();
 }
 });
 }
 // 
 }
  • 97.
    void printUserInfo(String username){
 NotifyApi notifyApi = new NotifyApi();
 
 NotifyApi.User user = notifyApi.getUser(username);
 Long feedCount = notifyApi.getFeedCount(username);
 Long friendCount = notifyApi.getFriendCount(username);
 
 UserInfo userInfo = new UserInfo(user.getName(), feedCount, friendCount);
 
 System.out.println("User Info");
 System.out.println("name = " + userInfo.getName());
 System.out.println("feedCount = " + userInfo.getFeedCount());
 System.out.println("friendCount = " + userInfo.getFriendCount());
 } 
 
 printUserInfo("fay"); // 1
 printUserInfo("murphy"); // 2
 printUserInfo("nichole"); // 3
  • 98.
    void printUserInfo(String username){ … } ExecutorService executorService = Executors.newFixedThreadPool(3); 
 executorService.execute(() -> printUserInfo("fay")); // 1
 executorService.execute(() -> printUserInfo("murphy")); // 2
 executorService.execute(() -> printUserInfo("nichole")); // 3
  • 99.
    void printUserInfo(String username){ … } ExecutorService executorService = Executors.newFixedThreadPool(3); 
 executorService.execute(() -> printUserInfo("fay")); // 1
 executorService.execute(() -> printUserInfo("murphy")); // 2
 executorService.execute(() -> printUserInfo("nichole")); // 3 
 executorService.execute(() -> printUserInfo("phillip")); // 4
 executorService.execute(() -> printUserInfo("adrienne")); // 5
 executorService.execute(() -> printUserInfo("nita")); // 6
  • 100.
    ExecutorService executorService =Executors.newFixedThreadPool(1); 
 void printUserInfo(String username) {
 CompletableFuture.supplyAsync(() -> notifyApi.getUser(username), executorService)
 .thenCompose(user -> {
 CompletableFuture<Long> feedCountCF = CompletableFuture.supplyAsync(
 () -> notifyApi.getFeedCount(username), executorService);
 
 CompletableFuture<Long> friendCountCF = CompletableFuture.supplyAsync(
 () -> notifyApi.getFriendCount(username), executorService);
 
 return feedCountCF.thenCombineAsync(friendCountCF, (feedCount, friendCount) -> {
 return new UserInfo(user.getName(), feedCount, friendCount);
 }, executorService);
 })
 .thenAccept(userInfo -> {
 System.out.println("User Info");
 System.out.println("name = " + userInfo.getName());
 System.out.println("feedCount = " + userInfo.getFeedCount());
 System.out.println("friendCount = " + userInfo.getFriendCount());
 });
 }
 
 printUserInfo("fay"); // 1
 printUserInfo("murphy"); // 2
 printUserInfo("nichole"); // 3
  • 102.
    ExecutorService executorService =Executors.newFixedThreadPool(3); 
 void printUserInfo(String username) {
 // 
 }
 
 
 printUserInfo("fay"); // 1
 printUserInfo("murphy"); // 2
 printUserInfo("nichole"); // 3
 
 printUserInfo("phillip"); // 4
 printUserInfo("adrienne"); // 5
 printUserInfo("nita"); // 6
  • 103.
    class NotificationPublisher implementsRunnable {
 
 Iterator<Notification> notifies;
 
 NotifyObserversRunnable(Iterator<Notification> notifies) {
 this.notifies = notifies;
 }
 
 @Override
 public void run() {
 boolean running = true;
 while (running) {
 if (countObservers() > 0) {
 try {
 Notification notification = notifies.next();
 
 setChanged();
 notifyObservers(notification);
 } catch (Exception error) {
 running = false;
 
 setChanged();
 notifyObservers(error);
 }
 }
 
 if (Thread.interrupted()) {
 running = false;
 }
 
 Thread.yield();
 }
 }
 
 }
  • 104.
    class NotificationPublisher implementsRunnable {
 final Iterator<Notification> notifies;
 final AtomicBoolean running = new AtomicBoolean(true); 
 NotifyObserversRunnable(Iterator<Notification> notifies) {
 this.notifies = notifies;
 }
 
 public void run() {
 if (countObservers() > 0) {
 CompletableFuture<Object> completableFuture = new CompletableFuture<>();
 completableFuture.thenAcceptAsync(event -> {
 if (running.get()) {
 setChanged();
 notifyObservers(event);
 }
 schedule(20);
 }, notifyExecutor).exceptionally(throwable -> {
 cancel();
 setChanged();
 notifyObservers(throwable);
 return null;
 });
 try {
 completableFuture.complete(notifies.next());
 } catch (Exception error) {
 completableFuture.completeExceptionally(error);
 }
 }
 schedule(50);
 } 
 void schedule(long millis) {
 if (running.get()) {
 serviceExecutor.schedule(this, millis, TimeUnit.MILLISECONDS);
 }
 } 
 void cancel() {
 running.set(false);
 }
 }
  • 105.
    class AsyncMultipleNotificationStreamObservable extendsObservable {
 
 final static ScheduledExecutorService serviceExecutor = Executors.newScheduledThreadPool(2);
 final static ScheduledExecutorService notifyExecutor = Executors.newScheduledThreadPool(1);
 
 NotifyObserversRunnable feedPublisher;
 NotifyObserversRunnable friendPublisher;
 
 public AsyncMultipleNotificationStreamObservable(NotificationStream stream) {
 this.feedPublisher = new NotifyObserversRunnable(requireNonNull(stream).feedNotifies());
 this.friendPublisher = new NotifyObserversRunnable(requireNonNull(stream).friendRecommendationNotifies());
 }
 
 public void subscribe() {
 serviceExecutor.schedule(feedPublisher, 10, TimeUnit.MILLISECONDS);
 serviceExecutor.schedule(friendPublisher, 10, TimeUnit.MILLISECONDS);
 }
 
 public void unsubscribe() {
 feedPublisher.cancel();
 friendPublisher.cancel();
 } }
  • 112.
    SocketChannel channel =SocketChannel.open();
 channel.configureBlocking(true);
 
 Socket socket = channel.socket();
 socket.connect(new InetSocketAddress("www.naver.com", 80));
 
 BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 PrintStream output = new PrintStream(socket.getOutputStream());
 
 output.println("GET / HTTP/1.0");
 output.println();
 
 StringBuilder response = new StringBuilder();
 String line = input.readLine();
 while (Objects.nonNull(line)) {
 response.append(line).append("n");
 line = input.readLine();
 
 // body 
 if ("".equals(line)) {
 while (Objects.nonNull(line)) {
 line = input.readLine();
 }
 }
 }
 
 input.close();
 output.close();
  • 115.
  • 116.
    Selector selector =Selector.open();
 
 SocketChannel socketChannel = SocketChannel.open();
 socketChannel.configureBlocking(false);
 
 socketChannel.connect(new InetSocketAddress("www.naver.com", 80));
 socketChannel.register(selector, SelectionKey.OP_CONNECT);
 
 while (socketChannel.isOpen()) {
 if (selector.select() > 0) {
 Set<SelectionKey> selectionKeys = selector.selectedKeys();
 Iterator<SelectionKey> iterator = selectionKeys.iterator();
 
 while (iterator.hasNext()) {
 SelectionKey selectionKey = iterator.next();
 
 if (selectionKey.isConnectable()) {
 SocketChannel channel = (SocketChannel) selectionKey.channel(); 
 // 
 } else if (selectionKey.isWritable()) {
 SocketChannel channel = (SocketChannel) selectionKey.channel();
 
 // 
 
 } else if (selectionKey.isReadable()) {
 SocketChannel channel = (SocketChannel) selectionKey.channel();
 
 // 
 
 }
 // 
 iterator.remove();
 }
 }
 }
  • 118.
    AsynchronousChannelGroup asynchronousChannelGroup =AsynchronousChannelGroup.withFixedThreadPool(1); AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open(asynchronousChannelGroup); SocketAddress socketAddress = new InetSocketAddress(“www.naver.com”, 80);
 
 asynchronousSocketChannel.connect(socketAddress, null, new CompletionHandler<Void, Void>() {
 @Override
 public void completed(Void v, Void attachment) {
 // 
 asynchronousSocketChannel.write(writeBuffer, null, new CompletionHandler<Integer, Void>() {
 @Override
 public void completed(Integer length, Void attachment) { // 
 asynchronousSocketChannel.read(readBuffer, null, new CompletionHandler<Integer, Void>() {
 @Override
 public void completed(Integer length, Void attachment) { // }
 @Override
 public void failed(Throwable error, Void attachment) {
 // 
 }
 });
 }
 @Override
 public void failed(Throwable error, Void attachment) {
 // 
 }
 });
 }
 @Override
 public void failed(Throwable exc, Void attachment) {
 // 
 }
 });
  • 121.
    ✔ Netty(http://netty.io) ✔ gRPG(http://www.grpc.io) ✔AsyncHttpClient(https://hc.apache.org/)
  • 122.
    public FriendRecommendationNotify getRecommendationNotify(Stringusername) throws ServiceOperationException {
 try {
 HttpGet request = new HttpGet(String.format(url, username, processing.name()));
 HttpResponse response = httpClient.execute(request);
 if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
 throw new ServiceOperationException();
 }
 
 return objectMapper.readValue(response.getEntity().getContent(), FriendRecommendationNotify.class);
 } catch (IOException error) {
 throw new ServiceOperationException(error);
 }
 }
  • 123.
    public CompletableFuture<FriendRecommendationNotify> getRecommendationNotifyAsync(Stringusername) {
 CompletableFuture<FriendRecommendationNotify> completableFuture = new CompletableFuture<>();
 
 HttpGet request = new HttpGet(String.format(url, username, processing.name()));
 httpAsyncClient.execute(request, new FutureCallback<HttpResponse>() {
 
 @Override
 public void completed(HttpResponse response) {
 try {
 int statusCode = response.getStatusLine().getStatusCode();
 InputStream content = response.getEntity().getContent();
 
 if (statusCode != HttpStatus.SC_OK) {
 completableFuture.completeExceptionally(new ServiceOperationException());
 }
 
 completableFuture.complete(objectMapper.readValue(content, FriendRecommendationNotify.class));
 } catch (IOException error) {
 completableFuture.completeExceptionally(new ServiceOperationException(error));
 }
 }
 
 @Override
 public void failed(Exception error) {
 completableFuture.completeExceptionally(error);
 }
 
 @Override
 public void cancelled() {
 completableFuture.cancel(true);
 }
 
 });
 
 return completableFuture;
 }
  • 124.
    @Test
 public void getRecommendationNotify()throws Exception {
 StopWatch stopWatch = new StopWatch("recommendationNotify");
 stopWatch.start();
 
 IntStream.rangeClosed(1, 10)
 .mapToObj(value -> friendService.getRecommendationNotify("user-" + value))
 .forEach(System.out::println);
 
 stopWatch.stop();
 System.out.println();
 System.out.println(stopWatch.prettyPrint());
 }
 
 @Test
 public void getRecommendationNotifyAsync() throws Exception {
 StopWatch stopWatch = new StopWatch("recommendationNotifyAsync");
 stopWatch.start();
 
 IntStream.rangeClosed(1, 10)
 .mapToObj(value -> friendService.getRecommendationNotifyAsync("user-" + value))
 .collect(Collectors.toList())
 .stream()
 .map(CompletableFuture::join)
 .forEach(System.out::println);
 
 stopWatch.stop();
 System.out.println();
 System.out.println(stopWatch.prettyPrint());
 }
  • 125.
    class NotificationPublisher implementsRunnable {
 
 final Supplier<CompletableFuture<Notification>> notifies;
 final AtomicBoolean running = new AtomicBoolean(true);
 
 NotifyObserversRunnable(Supplier<CompletableFuture<Notification>> notifies) {
 this.notifies = notifies;
 }
 
 @Override
 public void run() {
 if (countObservers() > 0) {
 notifies.get().thenAcceptAsync(event -> {
 if (running.get()) {
 setChanged();
 notifyObservers(event);
 }
 
 schedule(20);
 }, notifyExecutor).exceptionally(throwable -> {
 cancel();
 setChanged();
 notifyObservers(throwable);
 
 return null;
 });
 } else {
 schedule(50);
 }
 }
 
 void schedule(long millis) {
 if (running.get()) {
 serviceExecutor.schedule(this, millis, TimeUnit.MILLISECONDS);
 }
 }
 
 void cancel() {
 running.set(false);
 }
 }
  • 126.
    class NonBlockingAsyncMultipleNotificationStreamObservable extendsObservable {
 
 final static ScheduledExecutorService serviceExecutor = Executors.newScheduledThreadPool(1);
 final static ScheduledExecutorService notifyExecutor = Executors.newScheduledThreadPool(1);
 
 NotifyObserversRunnable feed;
 NotifyObserversRunnable friend;
 
 public NonBlockingAsyncMultipleNotificationStreamObservable(NotificationStream stream) {
 requireNonNull(stream);
 
 this.feed = new NotifyObserversRunnable(stream::feedNotifyAsync);
 this.friend = new NotifyObserversRunnable(stream::friendRecommendationNotifyAsync);
 }
 
 public void subscribe() {
 serviceExecutor.execute(feed);
 serviceExecutor.execute(friend);
 }
 
 public void unsubscribe() {
 if (nonNull(feed)) feed.cancel();
 if (nonNull(friend)) friend.cancel();
 
 deleteObservers();
 }
 }
  • 130.
    class NonBlockingAsyncMultipleNotificationStreamObserver implementsObserver, AsyncListener {
 
 final NonBlockingAsyncMultipleNotificationStreamObservable streamObservable;
 final AsyncContext asyncContext;
 
 public NonBlockingAsyncMultipleNotificationStreamObserver(N…Observable streamObservable, AsyncContext asyncContext) {
 this.streamObservable = Objects.requireNonNull(streamObservable);
 this.asyncContext = Objects.requireNonNull(asyncContext);
 }
 
 @Override
 public void update(Observable observable, Object event) {
 if (event instanceof Notification) {
 handler((Notification) event);
 }
 
 if (event instanceof Throwable) {
 handlerError((Throwable) event);
 }
 }
 
 private void handler(Notification notification) {
 try {
 log.info("revised notification/stream/non-blocking-async-concurrency : " + notification.getEvent());
 
 ServletOutputStream outputStream = asyncContext.getResponse().getOutputStream();
 outputStream.write(("event: " + notification.getEvent() + "n").getBytes());
 outputStream.write(("data: " + notification.getData() + "nn").getBytes());
 outputStream.flush();
 } catch (IOException error) {
 throw new DataWriteException(error);
 }
 }
 
 private void handlerError(Throwable error) {
 log.error("error notification/stream/non-blocking-async-concurrency : " + error.getMessage());
 
 streamObservable.unsubscribe();
 asyncContext.complete();
 }
 }
  • 131.
    ✔ 비봉쇄 입/출력을위한 새로운 인터페이스 •ReadListener - 사용 가능한 데이터를 읽을 수 있는 방법 •WriteListener - 데이터 쓰기가 가능한 때를 알 수 있는 방법 ✔ ServletInputStream 에 추가된 기능 •isFinished() •isReady() •setReadListener() ✔ ServletOutputStream 에 추가된 기능 •isReady() •setWriterListener()
  • 132.
    ServletInputStream servletInputStream =request.getInputStream();
 servletInputStream.setReadListener(new ReadListener() {
 
 @Override
 public void onDataAvailable() throws IOException {
 // 
 }
 
 @Override
 public void onAllDataRead() throws IOException {
 // 
 }
 
 @Override
 public void onError(Throwable throwable) {
 // 
 }
 
 }); ServletOutputStream servletOutputStream = response.getOutputStream();
 servletOutputStream.setWriteListener(new WriteListener() {
 
 @Override
 public void onWritePossible() throws IOException {
 // 
 }
 
 @Override
 public void onError(Throwable throwable) {
 // 
 }
 
 });
  • 133.
    class NonBlockingAsyncMultipleNotificationStreamObserver implementsObserver, AsyncListener, WriteListener {
 
 final NonBlockingAsyncMultipleNotificationStreamObservable streamObservable;
 final AsyncContext asyncContext;
 final ServletOutputStream outputStream;
 
 final AtomicInteger counter = new AtomicInteger(1);
 
 public NonBlockingAsyncMultipleNotificationStreamObserver(N…Observable streamObservable, AsyncContext asyncContext) {
 this.streamObservable = Objects.requireNonNull(streamObservable);
 this.asyncContext = Objects.requireNonNull(asyncContext);
 this.outputStream = asyncContext.getResponse().getOutputStream();
 }
 // 
 @Override
 public void onWritePossible() throws IOException {
 // ignore
 }
 
 @Override
 public void onError(Throwable throwable) {
 handlerError(throwable);
 }
 
 private void handler(Notification notification) {
 String content = notification.toContent();
 
 ioExecutor.schedule(() -> write(content.getBytes()), 10, TimeUnit.MILLISECONDS);
 }
 private void handlerError(Throwable error) {
 log.error("error notification/stream/non-blocking-async-concurrency : " + error.getMessage());
 
 streamObservable.unsubscribe();
 asyncContext.complete();
 } // …
  • 134.
    // final static ScheduledExecutorServiceioExecutor = Executors.newScheduledThreadPool(1); 
 private void write(byte[] data) {
 if (outputStream.isReady()) {
 try {
 outputStream.write(data);
 
 ioExecutor.schedule(this::flush, 10, TimeUnit.MILLISECONDS);
 } catch (IOException error) {
 handlerError(error);
 }
 } else {
 ioExecutor.schedule(() -> write(data), 10, TimeUnit.MILLISECONDS);
 }
 }
 
 private void flush() {
 if (outputStream.isReady()) {
 try {
 outputStream.flush();
 } catch (IOException error) {
 handlerError(error);
 }
 } else {
 ioExecutor.schedule(this::flush, 10, TimeUnit.MILLISECONDS);
 }
 }
 
 }
  • 136.