From 99adb5b0cfa9f54962eb82c536a3b714e4a377e7 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 23 Aug 2015 11:48:57 +0530 Subject: [PATCH 01/80] Work on #74 Initial logging server example --- reactor/pom.xml | 18 ++++ .../main/java/com/iluwatar/reactor/App.java | 42 ++++++++ .../java/com/iluwatar/reactor/AppClient.java | 62 ++++++++++++ .../java/com/iluwatar/reactor/NioReactor.java | 96 +++++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 reactor/pom.xml create mode 100644 reactor/src/main/java/com/iluwatar/reactor/App.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/AppClient.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/NioReactor.java diff --git a/reactor/pom.xml b/reactor/pom.xml new file mode 100644 index 000000000..0f3271a9c --- /dev/null +++ b/reactor/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.5.0 + + reactor + + + junit + junit + test + + + diff --git a/reactor/src/main/java/com/iluwatar/reactor/App.java b/reactor/src/main/java/com/iluwatar/reactor/App.java new file mode 100644 index 000000000..d5cd05fec --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/App.java @@ -0,0 +1,42 @@ +package com.iluwatar.reactor; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +import com.iluwatar.reactor.NioReactor.NioChannelEventHandler; + +public class App { + + public static void main(String[] args) { + try { + new NioReactor(6666, new LoggingServer()).start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + static class LoggingServer implements NioChannelEventHandler { + + @Override + public void onReadable(SocketChannel channel) { + ByteBuffer requestBuffer = ByteBuffer.allocate(1024); + try { + int byteCount = channel.read(requestBuffer); + if (byteCount > 0) { + byte[] logRequestContents = new byte[byteCount]; + byte[] array = requestBuffer.array(); + System.arraycopy(array, 0, logRequestContents, 0, byteCount); + doLogging(new String(logRequestContents)); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void doLogging(String log) { + // do logging at server side + System.out.println(log); + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java new file mode 100644 index 000000000..a5d871462 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java @@ -0,0 +1,62 @@ +package com.iluwatar.reactor; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; + +public class AppClient { + + public static void main(String[] args) { + new LoggingClient("Client 1", 6666).start(); + } + + + /* + * A logging client that sends logging requests to logging server + */ + static class LoggingClient { + + private int serverPort; + private String clientName; + + public LoggingClient(String clientName, int serverPort) { + this.clientName = clientName; + this.serverPort = serverPort; + } + + public void start() { + Socket socket = null; + try { + socket = new Socket(InetAddress.getLocalHost(), serverPort); + OutputStream outputStream = socket.getOutputStream(); + PrintWriter writer = new PrintWriter(outputStream); + writeLogs(writer); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private void writeLogs(PrintWriter writer) { + for (int i = 0; i < 10; i++) { + writer.println(clientName + " - Log request: " + i); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + writer.flush(); + } + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java new file mode 100644 index 000000000..b2952397c --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java @@ -0,0 +1,96 @@ +package com.iluwatar.reactor; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Set; + +public class NioReactor { + + private int port; + private Selector selector; + private ServerSocketChannel serverSocketChannel; + private NioChannelEventHandler nioEventhandler; + + public NioReactor(int port, NioChannelEventHandler handler) { + this.port = port; + this.nioEventhandler = handler; + } + + + public void start() throws IOException { + startReactor(); + requestLoop(); + } + + private void startReactor() throws IOException { + selector = Selector.open(); + serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.socket().bind(new InetSocketAddress(port)); + serverSocketChannel.configureBlocking(false); + SelectionKey acceptorKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); + acceptorKey.attach(new Acceptor()); + System.out.println("Reactor started listening on port: " + port); + } + + private void requestLoop() throws IOException { + while (true) { + selector.select(); + Set keys = selector.selectedKeys(); + for (SelectionKey key : keys) { + dispatchEvent(key); + } + keys.clear(); + } + } + + private void dispatchEvent(SelectionKey key) throws IOException { + Object handler = key.attachment(); + if (handler != null) { + ((EventHandler)handler).handle(); + } + } + + interface EventHandler { + void handle() throws IOException; + } + + private class Acceptor implements EventHandler { + + public void handle() throws IOException { + // non-blocking accept as acceptor will only be called when accept event is available + SocketChannel clientChannel = serverSocketChannel.accept(); + if (clientChannel != null) { + new ChannelHandler(clientChannel).handle(); + } + System.out.println("Connection established with a client"); + } + } + + public static interface NioChannelEventHandler { + void onReadable(SocketChannel channel); + } + + private class ChannelHandler implements EventHandler { + + private SocketChannel clientChannel; + private SelectionKey selectionKey; + + public ChannelHandler(SocketChannel clientChannel) throws IOException { + this.clientChannel = clientChannel; + clientChannel.configureBlocking(false); + selectionKey = clientChannel.register(selector, 0); + selectionKey.attach(this); + selectionKey.interestOps(SelectionKey.OP_READ); + selector.wakeup(); + } + + public void handle() throws IOException { + // only read events are supported. + nioEventhandler.onReadable(clientChannel); + } + } +} From d3f2ea22ac422ddac4d7d9733c45e3ebc46e6001 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 23 Aug 2015 18:51:24 +0530 Subject: [PATCH 02/80] Work on #74, enhanced reactor to allow multiple channels --- .../main/java/com/iluwatar/reactor/App.java | 31 +++++++- .../java/com/iluwatar/reactor/AppClient.java | 9 ++- .../java/com/iluwatar/reactor/NioReactor.java | 79 ++++++++++++------- 3 files changed, 84 insertions(+), 35 deletions(-) diff --git a/reactor/src/main/java/com/iluwatar/reactor/App.java b/reactor/src/main/java/com/iluwatar/reactor/App.java index d5cd05fec..4c7b06e9d 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/App.java @@ -1,7 +1,11 @@ package com.iluwatar.reactor; import java.io.IOException; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectableChannel; +import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import com.iluwatar.reactor.NioReactor.NioChannelEventHandler; @@ -10,12 +14,35 @@ public class App { public static void main(String[] args) { try { - new NioReactor(6666, new LoggingServer()).start(); + NioReactor reactor = new NioReactor(); + + reactor + .registerChannel(tcpChannel(6666)) + .registerChannel(tcpChannel(6667)) + .start(); + + reactor.registerHandler(new LoggingServer()); } catch (IOException e) { e.printStackTrace(); } } - + + private static SelectableChannel udpChannel(int port) throws IOException { + DatagramChannel channel = DatagramChannel.open(); + channel.socket().bind(new InetSocketAddress(port)); + channel.configureBlocking(false); + System.out.println("Bound UDP socket at port: " + port); + return channel; + } + + private static SelectableChannel tcpChannel(int port) throws IOException { + ServerSocketChannel channel = ServerSocketChannel.open(); + channel.socket().bind(new InetSocketAddress(port)); + channel.configureBlocking(false); + System.out.println("Bound TCP socket at port: " + port); + return channel; + } + static class LoggingServer implements NioChannelEventHandler { @Override diff --git a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java index a5d871462..1181745fb 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java @@ -9,14 +9,15 @@ import java.net.Socket; public class AppClient { public static void main(String[] args) { - new LoggingClient("Client 1", 6666).start(); + new Thread(new LoggingClient("Client 1", 6666)).start(); + new Thread(new LoggingClient("Client 2", 6667)).start(); } /* * A logging client that sends logging requests to logging server */ - static class LoggingClient { + static class LoggingClient implements Runnable { private int serverPort; private String clientName; @@ -26,7 +27,7 @@ public class AppClient { this.serverPort = serverPort; } - public void start() { + public void run() { Socket socket = null; try { socket = new Socket(InetAddress.getLocalHost(), serverPort); @@ -51,7 +52,7 @@ public class AppClient { for (int i = 0; i < 10; i++) { writer.println(clientName + " - Log request: " + i); try { - Thread.sleep(1000); + Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java index b2952397c..734ea086f 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java @@ -1,44 +1,63 @@ package com.iluwatar.reactor; import java.io.IOException; -import java.net.InetSocketAddress; +import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.util.List; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +/* + * Abstractions + * --------------- + * + * 1 - Dispatcher + * 2 - Synchronous Event De-multiplexer + * 3 - Event + * 4 - Event Handler & concrete event handler (application business logic) + * 5 - Selector + */ public class NioReactor { - private int port; private Selector selector; - private ServerSocketChannel serverSocketChannel; - private NioChannelEventHandler nioEventhandler; - - public NioReactor(int port, NioChannelEventHandler handler) { - this.port = port; - this.nioEventhandler = handler; + private Acceptor acceptor; + private List eventHandlers = new CopyOnWriteArrayList<>(); + + public NioReactor() throws IOException { + this.acceptor = new Acceptor(); + this.selector = Selector.open(); } + public NioReactor registerChannel(SelectableChannel channel) throws IOException { + SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT); + key.attach(acceptor); + return this; + } + + public void registerHandler(NioChannelEventHandler handler) { + eventHandlers.add(handler); + } public void start() throws IOException { - startReactor(); - requestLoop(); + new Thread( new Runnable() { + @Override + public void run() { + try { + System.out.println("Reactor started, waiting for events..."); + eventLoop(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); } - private void startReactor() throws IOException { - selector = Selector.open(); - serverSocketChannel = ServerSocketChannel.open(); - serverSocketChannel.socket().bind(new InetSocketAddress(port)); - serverSocketChannel.configureBlocking(false); - SelectionKey acceptorKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); - acceptorKey.attach(new Acceptor()); - System.out.println("Reactor started listening on port: " + port); - } - - private void requestLoop() throws IOException { + private void eventLoop() throws IOException { while (true) { - selector.select(); + selector.select(1000); Set keys = selector.selectedKeys(); for (SelectionKey key : keys) { dispatchEvent(key); @@ -50,21 +69,21 @@ public class NioReactor { private void dispatchEvent(SelectionKey key) throws IOException { Object handler = key.attachment(); if (handler != null) { - ((EventHandler)handler).handle(); + ((EventHandler)handler).handle(key.channel()); } } interface EventHandler { - void handle() throws IOException; + void handle(SelectableChannel channel) throws IOException; } private class Acceptor implements EventHandler { - public void handle() throws IOException { + public void handle(SelectableChannel channel) throws IOException { // non-blocking accept as acceptor will only be called when accept event is available - SocketChannel clientChannel = serverSocketChannel.accept(); + SocketChannel clientChannel = ((ServerSocketChannel)channel).accept(); if (clientChannel != null) { - new ChannelHandler(clientChannel).handle(); + new ChannelHandler(clientChannel).handle(clientChannel); } System.out.println("Connection established with a client"); } @@ -88,9 +107,11 @@ public class NioReactor { selector.wakeup(); } - public void handle() throws IOException { + public void handle(SelectableChannel channel) throws IOException { // only read events are supported. - nioEventhandler.onReadable(clientChannel); + for (NioChannelEventHandler eventHandler : eventHandlers) { + eventHandler.onReadable(clientChannel); + } } } } From ec8203a196106ac6cbb732775af153ff4b15dfd6 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 26 Aug 2015 20:12:17 +0530 Subject: [PATCH 03/80] Ongoing work on #74 introduced better abstractions in reactor - tcp and udp mode --- .../iluwatar/reactor/AbstractNioChannel.java | 75 ++++++++ .../main/java/com/iluwatar/reactor/App.java | 61 ++----- .../java/com/iluwatar/reactor/AppClient.java | 63 ++++++- .../com/iluwatar/reactor/ChannelHandler.java | 9 + .../java/com/iluwatar/reactor/Dispatcher.java | 8 + .../com/iluwatar/reactor/LoggingHandler.java | 24 +++ .../iluwatar/reactor/NioDatagramChannel.java | 47 +++++ .../java/com/iluwatar/reactor/NioReactor.java | 161 +++++++++++------- .../reactor/NioServerSocketChannel.java | 52 ++++++ .../reactor/SameThreadDispatcher.java | 14 ++ .../reactor/ThreadPoolDispatcher.java | 27 +++ reactor/todo.txt | 4 + 12 files changed, 429 insertions(+), 116 deletions(-) create mode 100644 reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java create mode 100644 reactor/todo.txt diff --git a/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java new file mode 100644 index 000000000..9f6040ade --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java @@ -0,0 +1,75 @@ +package com.iluwatar.reactor; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public abstract class AbstractNioChannel { + + private SelectableChannel channel; + private ChannelHandler handler; + private Map> channelToPendingWrites = new ConcurrentHashMap<>(); + private NioReactor reactor; + + public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { + this.handler = handler; + this.channel = channel; + } + + public void setReactor(NioReactor reactor) { + this.reactor = reactor; + } + + public SelectableChannel getChannel() { + return channel; + } + + public abstract int getInterestedOps(); + + public abstract ByteBuffer read(SelectionKey key) throws IOException; + + public void setHandler(ChannelHandler handler) { + this.handler = handler; + } + + public ChannelHandler getHandler() { + return handler; + } + + // Called from the context of reactor thread + public void write(SelectionKey key) throws IOException { + Queue pendingWrites = channelToPendingWrites.get(key.channel()); + while (true) { + ByteBuffer pendingWrite = pendingWrites.poll(); + if (pendingWrite == null) { + System.out.println("No more pending writes"); + reactor.changeOps(key, SelectionKey.OP_READ); + break; + } + + doWrite(pendingWrite, key); + } + } + + protected abstract void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException; + + public void write(ByteBuffer buffer, SelectionKey key) { + Queue pendingWrites = this.channelToPendingWrites.get(key.channel()); + if (pendingWrites == null) { + synchronized (this.channelToPendingWrites) { + pendingWrites = this.channelToPendingWrites.get(key.channel()); + if (pendingWrites == null) { + pendingWrites = new ConcurrentLinkedQueue<>(); + this.channelToPendingWrites.put(key.channel(), pendingWrites); + } + } + } + pendingWrites.add(buffer); + reactor.changeOps(key, SelectionKey.OP_WRITE); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/App.java b/reactor/src/main/java/com/iluwatar/reactor/App.java index 4c7b06e9d..36aa5290d 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/App.java @@ -1,69 +1,32 @@ package com.iluwatar.reactor; import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.nio.channels.SelectableChannel; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; - -import com.iluwatar.reactor.NioReactor.NioChannelEventHandler; public class App { public static void main(String[] args) { try { - NioReactor reactor = new NioReactor(); - + NioReactor reactor = new NioReactor(new ThreadPoolDispatcher(2)); + LoggingHandler loggingHandler = new LoggingHandler(); reactor - .registerChannel(tcpChannel(6666)) - .registerChannel(tcpChannel(6667)) + .registerChannel(tcpChannel(6666, loggingHandler)) + .registerChannel(tcpChannel(6667, loggingHandler)) + .registerChannel(udpChannel(6668, loggingHandler)) .start(); - - reactor.registerHandler(new LoggingServer()); } catch (IOException e) { e.printStackTrace(); } } - private static SelectableChannel udpChannel(int port) throws IOException { - DatagramChannel channel = DatagramChannel.open(); - channel.socket().bind(new InetSocketAddress(port)); - channel.configureBlocking(false); - System.out.println("Bound UDP socket at port: " + port); + private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { + NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); + channel.bind(); return channel; } - - private static SelectableChannel tcpChannel(int port) throws IOException { - ServerSocketChannel channel = ServerSocketChannel.open(); - channel.socket().bind(new InetSocketAddress(port)); - channel.configureBlocking(false); - System.out.println("Bound TCP socket at port: " + port); + + private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { + NioDatagramChannel channel = new NioDatagramChannel(port, handler); + channel.bind(); return channel; } - - static class LoggingServer implements NioChannelEventHandler { - - @Override - public void onReadable(SocketChannel channel) { - ByteBuffer requestBuffer = ByteBuffer.allocate(1024); - try { - int byteCount = channel.read(requestBuffer); - if (byteCount > 0) { - byte[] logRequestContents = new byte[byteCount]; - byte[] array = requestBuffer.array(); - System.arraycopy(array, 0, logRequestContents, 0, byteCount); - doLogging(new String(logRequestContents)); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void doLogging(String log) { - // do logging at server side - System.out.println(log); - } - } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java index 1181745fb..3d7323a55 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java @@ -1,16 +1,22 @@ package com.iluwatar.reactor; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; +import java.net.DatagramPacket; +import java.net.DatagramSocket; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketException; public class AppClient { public static void main(String[] args) { - new Thread(new LoggingClient("Client 1", 6666)).start(); - new Thread(new LoggingClient("Client 2", 6667)).start(); +// new Thread(new LoggingClient("Client 1", 6666)).start(); +// new Thread(new LoggingClient("Client 2", 6667)).start(); + new Thread(new UDPLoggingClient(6668)).start(); } @@ -33,7 +39,7 @@ public class AppClient { socket = new Socket(InetAddress.getLocalHost(), serverPort); OutputStream outputStream = socket.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); - writeLogs(writer); + writeLogs(writer, socket.getInputStream()); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); @@ -48,8 +54,8 @@ public class AppClient { } } - private void writeLogs(PrintWriter writer) { - for (int i = 0; i < 10; i++) { + private void writeLogs(PrintWriter writer, InputStream inputStream) throws IOException { + for (int i = 0; i < 1; i++) { writer.println(clientName + " - Log request: " + i); try { Thread.sleep(100); @@ -57,6 +63,53 @@ public class AppClient { e.printStackTrace(); } writer.flush(); + byte[] data = new byte[1024]; + int read = inputStream.read(data, 0, data.length); + if (read == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(data, 0, read)); + } + } + } + } + + static class UDPLoggingClient implements Runnable { + private int port; + + public UDPLoggingClient(int port) { + this.port = port; + } + + @Override + public void run() { + DatagramSocket socket = null; + try { + socket = new DatagramSocket(); + for (int i = 0; i < 1; i++) { + String message = "UDP Client" + " - Log request: " + i; + try { + DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, new InetSocketAddress(InetAddress.getLocalHost(), port)); + socket.send(packet); + + byte[] data = new byte[1024]; + DatagramPacket reply = new DatagramPacket(data, data.length); + socket.receive(reply); + if (reply.getLength() == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(reply.getData(), 0, reply.getLength())); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } catch (SocketException e1) { + e1.printStackTrace(); + } finally { + if (socket != null) { + socket.close(); + } } } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java new file mode 100644 index 000000000..055e8edd6 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java @@ -0,0 +1,9 @@ +package com.iluwatar.reactor; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; + +public interface ChannelHandler { + + void handleChannelRead(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key); +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java new file mode 100644 index 000000000..1bc14c55f --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java @@ -0,0 +1,8 @@ +package com.iluwatar.reactor; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; + +public interface Dispatcher { + void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key); +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java new file mode 100644 index 000000000..3744c3d5a --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java @@ -0,0 +1,24 @@ +package com.iluwatar.reactor; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; + +public class LoggingHandler implements ChannelHandler { + + @Override + public void handleChannelRead(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) { + byte[] data = readBytes.array(); + doLogging(data); + sendEchoReply(channel, data, key); + } + + private void sendEchoReply(AbstractNioChannel channel, byte[] data, SelectionKey key) { + ByteBuffer buffer = ByteBuffer.wrap("Data logged successfully".getBytes()); + channel.write(buffer, key); + } + + private void doLogging(byte[] data) { + // assuming UTF-8 :( + System.out.println(new String(data)); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java new file mode 100644 index 000000000..2f655f192 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java @@ -0,0 +1,47 @@ +package com.iluwatar.reactor; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; + +public class NioDatagramChannel extends AbstractNioChannel { + + private int port; + + public NioDatagramChannel(int port, ChannelHandler handler) throws IOException { + super(handler, DatagramChannel.open()); + this.port = port; + } + + @Override + public int getInterestedOps() { + return SelectionKey.OP_READ; + } + + @Override + public ByteBuffer read(SelectionKey key) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + getChannel().receive(buffer); + return buffer; + } + + @Override + public DatagramChannel getChannel() { + return (DatagramChannel) super.getChannel(); + } + + public void bind() throws IOException { + getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + getChannel().configureBlocking(false); + System.out.println("Bound UDP socket at port: " + port); + } + + @Override + protected void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException { + pendingWrite.flip(); + getChannel().write(pendingWrite); + } +} \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java index 734ea086f..05aa609d1 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java @@ -1,46 +1,39 @@ package com.iluwatar.reactor; import java.io.IOException; -import java.nio.channels.SelectableChannel; +import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.util.List; +import java.util.Iterator; +import java.util.Queue; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ConcurrentLinkedQueue; /* * Abstractions * --------------- - * - * 1 - Dispatcher * 2 - Synchronous Event De-multiplexer - * 3 - Event - * 4 - Event Handler & concrete event handler (application business logic) - * 5 - Selector */ public class NioReactor { private Selector selector; - private Acceptor acceptor; - private List eventHandlers = new CopyOnWriteArrayList<>(); + private Dispatcher dispatcher; + private Queue pendingChanges = new ConcurrentLinkedQueue<>(); - public NioReactor() throws IOException { - this.acceptor = new Acceptor(); + public NioReactor(Dispatcher dispatcher) throws IOException { + this.dispatcher = dispatcher; this.selector = Selector.open(); } - public NioReactor registerChannel(SelectableChannel channel) throws IOException { - SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT); - key.attach(acceptor); + public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { + SelectionKey key = channel.getChannel().register(selector, channel.getInterestedOps()); + key.attach(channel); + channel.setReactor(this); return this; } - public void registerHandler(NioChannelEventHandler handler) { - eventHandlers.add(handler); - } - public void start() throws IOException { new Thread( new Runnable() { @Override @@ -52,66 +45,110 @@ public class NioReactor { e.printStackTrace(); } } - }).start(); + }, "Reactor Main").start(); } private void eventLoop() throws IOException { while (true) { - selector.select(1000); + // honor any pending requests first + processPendingChanges(); + + selector.select(); + Set keys = selector.selectedKeys(); - for (SelectionKey key : keys) { - dispatchEvent(key); + + Iterator iterator = keys.iterator(); + + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + if (!key.isValid()) { + iterator.remove(); + continue; + } + processKey(key); } keys.clear(); } } - private void dispatchEvent(SelectionKey key) throws IOException { - Object handler = key.attachment(); - if (handler != null) { - ((EventHandler)handler).handle(key.channel()); + private void processPendingChanges() { + Iterator iterator = pendingChanges.iterator(); + while (iterator.hasNext()) { + Command command = iterator.next(); + System.out.println("Processing pending change: " + command); + command.execute(); + iterator.remove(); } } - interface EventHandler { - void handle(SelectableChannel channel) throws IOException; - } - - private class Acceptor implements EventHandler { - - public void handle(SelectableChannel channel) throws IOException { - // non-blocking accept as acceptor will only be called when accept event is available - SocketChannel clientChannel = ((ServerSocketChannel)channel).accept(); - if (clientChannel != null) { - new ChannelHandler(clientChannel).handle(clientChannel); - } - System.out.println("Connection established with a client"); + private void processKey(SelectionKey key) throws IOException { + if (key.isAcceptable()) { + acceptConnection(key); + } else if (key.isReadable()) { + System.out.println("Key is readable"); + read(key); + } else if (key.isWritable()) { + System.out.println("Key is writable"); + write(key); } } - - public static interface NioChannelEventHandler { - void onReadable(SocketChannel channel); + + private void write(SelectionKey key) throws IOException { + AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); + channel.write(key); } - - private class ChannelHandler implements EventHandler { - - private SocketChannel clientChannel; - private SelectionKey selectionKey; - public ChannelHandler(SocketChannel clientChannel) throws IOException { - this.clientChannel = clientChannel; - clientChannel.configureBlocking(false); - selectionKey = clientChannel.register(selector, 0); - selectionKey.attach(this); - selectionKey.interestOps(SelectionKey.OP_READ); - selector.wakeup(); - } - - public void handle(SelectableChannel channel) throws IOException { - // only read events are supported. - for (NioChannelEventHandler eventHandler : eventHandlers) { - eventHandler.onReadable(clientChannel); + private void read(SelectionKey key) { + ByteBuffer readBytes; + try { + readBytes = ((AbstractNioChannel)key.attachment()).read(key); + dispatchReadEvent(key, readBytes); + } catch (IOException e) { + try { + key.channel().close(); + } catch (IOException e1) { + e1.printStackTrace(); } } } -} + + private void dispatchReadEvent(SelectionKey key, ByteBuffer readBytes) { + dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readBytes, key); + } + + private void acceptConnection(SelectionKey key) throws IOException { + ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); + SocketChannel socketChannel = serverSocketChannel.accept(); + socketChannel.configureBlocking(false); + SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); + readKey.attach(key.attachment()); + } + + interface Command { + void execute(); + } + + public void changeOps(SelectionKey key, int interestedOps) { + pendingChanges.add(new ChangeKeyOpsCommand(key, interestedOps)); + selector.wakeup(); + } + + class ChangeKeyOpsCommand implements Command { + private SelectionKey key; + private int interestedOps; + + public ChangeKeyOpsCommand(SelectionKey key, int interestedOps) { + this.key = key; + this.interestedOps = interestedOps; + } + + public void execute() { + key.interestOps(interestedOps); + } + + @Override + public String toString() { + return "Change of ops to: " + interestedOps; + } + } +} \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java new file mode 100644 index 000000000..66affdb8d --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java @@ -0,0 +1,52 @@ +package com.iluwatar.reactor; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +public class NioServerSocketChannel extends AbstractNioChannel { + + private int port; + + public NioServerSocketChannel(int port, ChannelHandler handler) throws IOException { + super(handler, ServerSocketChannel.open()); + this.port = port; + } + + @Override + public int getInterestedOps() { + return SelectionKey.OP_ACCEPT; + } + + @Override + public ServerSocketChannel getChannel() { + return (ServerSocketChannel) super.getChannel(); + } + + @Override + public ByteBuffer read(SelectionKey key) throws IOException { + SocketChannel socketChannel = (SocketChannel) key.channel(); + ByteBuffer buffer = ByteBuffer.allocate(1024); + int read = socketChannel.read(buffer); + if (read == -1) { + throw new IOException("Socket closed"); + } + return buffer; + } + + public void bind() throws IOException { + ((ServerSocketChannel)getChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + ((ServerSocketChannel)getChannel()).configureBlocking(false); + System.out.println("Bound TCP socket at port: " + port); + } + + @Override + protected void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException { + System.out.println("Writing on channel"); + ((SocketChannel)key.channel()).write(pendingWrite); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java new file mode 100644 index 000000000..9b8029de4 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java @@ -0,0 +1,14 @@ +package com.iluwatar.reactor; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; + +public class SameThreadDispatcher implements Dispatcher { + + @Override + public void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) { + if (channel.getHandler() != null) { + channel.getHandler().handleChannelRead(channel, readBytes, key); + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java new file mode 100644 index 000000000..2f44e4372 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java @@ -0,0 +1,27 @@ +package com.iluwatar.reactor; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ThreadPoolDispatcher extends SameThreadDispatcher { + + private ExecutorService exectorService; + + public ThreadPoolDispatcher(int poolSize) { + this.exectorService = Executors.newFixedThreadPool(poolSize); + } + + @Override + public void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) { + exectorService.execute(new Runnable() { + + @Override + public void run() { + ThreadPoolDispatcher.super.onChannelReadEvent(channel, readBytes, key); + } + }); + } + +} diff --git a/reactor/todo.txt b/reactor/todo.txt new file mode 100644 index 000000000..af06a1892 --- /dev/null +++ b/reactor/todo.txt @@ -0,0 +1,4 @@ +* Make UDP channel work (connect is required) +* Cleanup +* Document - Javadoc +* Better design?? Get review of @iluwatar From b94c1d37d2248513d98778a4f95ee8290e3806d8 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 2 Sep 2015 12:28:52 +0530 Subject: [PATCH 04/80] Work on #74, server mode works with both UDP and TCP channels --- .../iluwatar/reactor/AbstractNioChannel.java | 17 ++++--- .../java/com/iluwatar/reactor/AppClient.java | 4 +- .../com/iluwatar/reactor/ChannelHandler.java | 3 +- .../java/com/iluwatar/reactor/Dispatcher.java | 3 +- .../com/iluwatar/reactor/LoggingHandler.java | 25 ++++++++--- .../iluwatar/reactor/NioDatagramChannel.java | 45 ++++++++++++++++--- .../java/com/iluwatar/reactor/NioReactor.java | 14 +++--- .../reactor/NioServerSocketChannel.java | 5 ++- .../reactor/SameThreadDispatcher.java | 5 +-- .../reactor/ThreadPoolDispatcher.java | 5 +-- 10 files changed, 83 insertions(+), 43 deletions(-) diff --git a/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java index 9f6040ade..f55cea073 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java @@ -1,7 +1,6 @@ package com.iluwatar.reactor; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.util.Map; @@ -13,7 +12,7 @@ public abstract class AbstractNioChannel { private SelectableChannel channel; private ChannelHandler handler; - private Map> channelToPendingWrites = new ConcurrentHashMap<>(); + private Map> channelToPendingWrites = new ConcurrentHashMap<>(); private NioReactor reactor; public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { @@ -31,7 +30,7 @@ public abstract class AbstractNioChannel { public abstract int getInterestedOps(); - public abstract ByteBuffer read(SelectionKey key) throws IOException; + public abstract Object read(SelectionKey key) throws IOException; public void setHandler(ChannelHandler handler) { this.handler = handler; @@ -43,9 +42,9 @@ public abstract class AbstractNioChannel { // Called from the context of reactor thread public void write(SelectionKey key) throws IOException { - Queue pendingWrites = channelToPendingWrites.get(key.channel()); + Queue pendingWrites = channelToPendingWrites.get(key.channel()); while (true) { - ByteBuffer pendingWrite = pendingWrites.poll(); + Object pendingWrite = pendingWrites.poll(); if (pendingWrite == null) { System.out.println("No more pending writes"); reactor.changeOps(key, SelectionKey.OP_READ); @@ -56,10 +55,10 @@ public abstract class AbstractNioChannel { } } - protected abstract void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException; + protected abstract void doWrite(Object pendingWrite, SelectionKey key) throws IOException; - public void write(ByteBuffer buffer, SelectionKey key) { - Queue pendingWrites = this.channelToPendingWrites.get(key.channel()); + public void write(Object data, SelectionKey key) { + Queue pendingWrites = this.channelToPendingWrites.get(key.channel()); if (pendingWrites == null) { synchronized (this.channelToPendingWrites) { pendingWrites = this.channelToPendingWrites.get(key.channel()); @@ -69,7 +68,7 @@ public abstract class AbstractNioChannel { } } } - pendingWrites.add(buffer); + pendingWrites.add(data); reactor.changeOps(key, SelectionKey.OP_WRITE); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java index 3d7323a55..188b64ea8 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java @@ -14,8 +14,8 @@ import java.net.SocketException; public class AppClient { public static void main(String[] args) { -// new Thread(new LoggingClient("Client 1", 6666)).start(); -// new Thread(new LoggingClient("Client 2", 6667)).start(); + new Thread(new LoggingClient("Client 1", 6666)).start(); + new Thread(new LoggingClient("Client 2", 6667)).start(); new Thread(new UDPLoggingClient(6668)).start(); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java index 055e8edd6..e84c506f9 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java @@ -1,9 +1,8 @@ package com.iluwatar.reactor; -import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; public interface ChannelHandler { - void handleChannelRead(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key); + void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java index 1bc14c55f..15fe7774c 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java @@ -1,8 +1,7 @@ package com.iluwatar.reactor; -import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; public interface Dispatcher { - void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key); + void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java index 3744c3d5a..fc7efaeed 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java @@ -3,16 +3,31 @@ package com.iluwatar.reactor; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; +import com.iluwatar.reactor.NioDatagramChannel.DatagramPacket; + public class LoggingHandler implements ChannelHandler { @Override - public void handleChannelRead(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) { - byte[] data = readBytes.array(); - doLogging(data); - sendEchoReply(channel, data, key); + public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { + if (readObject instanceof ByteBuffer) { + byte[] data = ((ByteBuffer)readObject).array(); + doLogging(data); + sendReply(channel, data, key); + } else if (readObject instanceof DatagramPacket) { + DatagramPacket datagram = (DatagramPacket)readObject; + byte[] data = datagram.getData().array(); + doLogging(data); + sendReply(channel, datagram, key); + } } - private void sendEchoReply(AbstractNioChannel channel, byte[] data, SelectionKey key) { + private void sendReply(AbstractNioChannel channel, DatagramPacket datagram, SelectionKey key) { + DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap("Data logged successfully".getBytes())); + replyPacket.setReceiver(datagram.getSender()); + channel.write(replyPacket, key); + } + + private void sendReply(AbstractNioChannel channel, byte[] data, SelectionKey key) { ByteBuffer buffer = ByteBuffer.wrap("Data logged successfully".getBytes()); channel.write(buffer, key); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java index 2f655f192..4d1690792 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java @@ -3,6 +3,7 @@ package com.iluwatar.reactor; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.nio.channels.SelectionKey; @@ -22,10 +23,12 @@ public class NioDatagramChannel extends AbstractNioChannel { } @Override - public ByteBuffer read(SelectionKey key) throws IOException { + public Object read(SelectionKey key) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1024); - getChannel().receive(buffer); - return buffer; + SocketAddress sender = getChannel().receive(buffer); + DatagramPacket packet = new DatagramPacket(buffer); + packet.setSender(sender); + return packet; } @Override @@ -40,8 +43,38 @@ public class NioDatagramChannel extends AbstractNioChannel { } @Override - protected void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException { - pendingWrite.flip(); - getChannel().write(pendingWrite); + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; + getChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); + } + + static class DatagramPacket { + private SocketAddress sender; + private ByteBuffer data; + private SocketAddress receiver; + + public DatagramPacket(ByteBuffer data) { + this.data = data; + } + + public SocketAddress getSender() { + return sender; + } + + public void setSender(SocketAddress sender) { + this.sender = sender; + } + + public SocketAddress getReceiver() { + return receiver; + } + + public void setReceiver(SocketAddress receiver) { + this.receiver = receiver; + } + + public ByteBuffer getData() { + return data; + } } } \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java index 05aa609d1..f10ea4b82 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java @@ -1,7 +1,6 @@ package com.iluwatar.reactor; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; @@ -75,7 +74,6 @@ public class NioReactor { Iterator iterator = pendingChanges.iterator(); while (iterator.hasNext()) { Command command = iterator.next(); - System.out.println("Processing pending change: " + command); command.execute(); iterator.remove(); } @@ -85,10 +83,8 @@ public class NioReactor { if (key.isAcceptable()) { acceptConnection(key); } else if (key.isReadable()) { - System.out.println("Key is readable"); read(key); } else if (key.isWritable()) { - System.out.println("Key is writable"); write(key); } } @@ -99,10 +95,10 @@ public class NioReactor { } private void read(SelectionKey key) { - ByteBuffer readBytes; + Object readObject; try { - readBytes = ((AbstractNioChannel)key.attachment()).read(key); - dispatchReadEvent(key, readBytes); + readObject = ((AbstractNioChannel)key.attachment()).read(key); + dispatchReadEvent(key, readObject); } catch (IOException e) { try { key.channel().close(); @@ -112,8 +108,8 @@ public class NioReactor { } } - private void dispatchReadEvent(SelectionKey key, ByteBuffer readBytes) { - dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readBytes, key); + private void dispatchReadEvent(SelectionKey key, Object readObject) { + dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readObject, key); } private void acceptConnection(SelectionKey key) throws IOException { diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java index 66affdb8d..ebd8f0ef3 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java @@ -45,8 +45,9 @@ public class NioServerSocketChannel extends AbstractNioChannel { } @Override - protected void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException { + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; System.out.println("Writing on channel"); - ((SocketChannel)key.channel()).write(pendingWrite); + ((SocketChannel)key.channel()).write(pendingBuffer); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java index 9b8029de4..024441b7c 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java @@ -1,14 +1,13 @@ package com.iluwatar.reactor; -import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; public class SameThreadDispatcher implements Dispatcher { @Override - public void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) { + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { if (channel.getHandler() != null) { - channel.getHandler().handleChannelRead(channel, readBytes, key); + channel.getHandler().handleChannelRead(channel, readObject, key); } } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java index 2f44e4372..e9e4ac34c 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java @@ -1,6 +1,5 @@ package com.iluwatar.reactor; -import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -14,12 +13,12 @@ public class ThreadPoolDispatcher extends SameThreadDispatcher { } @Override - public void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) { + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { exectorService.execute(new Runnable() { @Override public void run() { - ThreadPoolDispatcher.super.onChannelReadEvent(channel, readBytes, key); + ThreadPoolDispatcher.super.onChannelReadEvent(channel, readObject, key); } }); } From 940a62bc01833ec11bba664af5914687e5a9e59e Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 2 Sep 2015 15:08:34 +0530 Subject: [PATCH 05/80] Work on #74, added unit test cases --- .../main/java/com/iluwatar/reactor/App.java | 27 +++++++++++----- .../java/com/iluwatar/reactor/AppClient.java | 31 +++++++++++++++---- .../java/com/iluwatar/reactor/Dispatcher.java | 1 + .../java/com/iluwatar/reactor/NioReactor.java | 24 ++++++++++++-- .../reactor/SameThreadDispatcher.java | 5 +++ .../reactor/ThreadPoolDispatcher.java | 17 ++++++++-- .../java/com/iluwatar/reactor/AppTest.java | 27 ++++++++++++++++ 7 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 reactor/src/test/java/com/iluwatar/reactor/AppTest.java diff --git a/reactor/src/main/java/com/iluwatar/reactor/App.java b/reactor/src/main/java/com/iluwatar/reactor/App.java index 36aa5290d..7ce27a78b 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/App.java @@ -4,19 +4,32 @@ import java.io.IOException; public class App { + private NioReactor reactor; + public static void main(String[] args) { try { - NioReactor reactor = new NioReactor(new ThreadPoolDispatcher(2)); - LoggingHandler loggingHandler = new LoggingHandler(); - reactor - .registerChannel(tcpChannel(6666, loggingHandler)) - .registerChannel(tcpChannel(6667, loggingHandler)) - .registerChannel(udpChannel(6668, loggingHandler)) - .start(); + new App().start(); } catch (IOException e) { e.printStackTrace(); } } + + public void start() throws IOException { + reactor = new NioReactor(new ThreadPoolDispatcher(2)); + + LoggingHandler loggingHandler = new LoggingHandler(); + + reactor + .registerChannel(tcpChannel(6666, loggingHandler)) + .registerChannel(tcpChannel(6667, loggingHandler)) + .registerChannel(udpChannel(6668, loggingHandler)) + .start(); + } + + public void stop() { + reactor.stop(); + } + private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); diff --git a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java index 188b64ea8..2ffb6c0de 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/AppClient.java @@ -10,15 +10,34 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; public class AppClient { - + private ExecutorService service = Executors.newFixedThreadPool(3); + public static void main(String[] args) { - new Thread(new LoggingClient("Client 1", 6666)).start(); - new Thread(new LoggingClient("Client 2", 6667)).start(); - new Thread(new UDPLoggingClient(6668)).start(); + new AppClient().start(); } + public void start() { + service.execute(new LoggingClient("Client 1", 6666)); + service.execute(new LoggingClient("Client 2", 6667)); + service.execute(new UDPLoggingClient(6668)); + } + + public void stop() { + service.shutdown(); + if (!service.isTerminated()) { + service.shutdownNow(); + try { + service.awaitTermination(1000, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } /* * A logging client that sends logging requests to logging server @@ -55,7 +74,7 @@ public class AppClient { } private void writeLogs(PrintWriter writer, InputStream inputStream) throws IOException { - for (int i = 0; i < 1; i++) { + for (int i = 0; i < 4; i++) { writer.println(clientName + " - Log request: " + i); try { Thread.sleep(100); @@ -86,7 +105,7 @@ public class AppClient { DatagramSocket socket = null; try { socket = new DatagramSocket(); - for (int i = 0; i < 1; i++) { + for (int i = 0; i < 4; i++) { String message = "UDP Client" + " - Log request: " + i; try { DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, new InetSocketAddress(InetAddress.getLocalHost(), port)); diff --git a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java index 15fe7774c..7c05a6c1d 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java @@ -4,4 +4,5 @@ import java.nio.channels.SelectionKey; public interface Dispatcher { void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); + void stop(); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java index f10ea4b82..6ee0cb989 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java @@ -9,6 +9,9 @@ import java.util.Iterator; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; /* * Abstractions @@ -20,6 +23,7 @@ public class NioReactor { private Selector selector; private Dispatcher dispatcher; private Queue pendingChanges = new ConcurrentLinkedQueue<>(); + private ExecutorService reactorService = Executors.newSingleThreadExecutor(); public NioReactor(Dispatcher dispatcher) throws IOException { this.dispatcher = dispatcher; @@ -34,7 +38,7 @@ public class NioReactor { } public void start() throws IOException { - new Thread( new Runnable() { + reactorService.execute(new Runnable() { @Override public void run() { try { @@ -44,11 +48,27 @@ public class NioReactor { e.printStackTrace(); } } - }, "Reactor Main").start(); + }); + } + + public void stop() { + reactorService.shutdownNow(); + selector.wakeup(); + try { + reactorService.awaitTermination(4, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + dispatcher.stop(); } private void eventLoop() throws IOException { while (true) { + + if (Thread.interrupted()) { + break; + } + // honor any pending requests first processPendingChanges(); diff --git a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java index 024441b7c..c27050a15 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java @@ -10,4 +10,9 @@ public class SameThreadDispatcher implements Dispatcher { channel.getHandler().handleChannelRead(channel, readObject, key); } } + + @Override + public void stop() { + // no-op + } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java index e9e4ac34c..600cb4da4 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java @@ -3,18 +3,19 @@ package com.iluwatar.reactor; import java.nio.channels.SelectionKey; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; public class ThreadPoolDispatcher extends SameThreadDispatcher { - private ExecutorService exectorService; + private ExecutorService executorService; public ThreadPoolDispatcher(int poolSize) { - this.exectorService = Executors.newFixedThreadPool(poolSize); + this.executorService = Executors.newFixedThreadPool(poolSize); } @Override public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { - exectorService.execute(new Runnable() { + executorService.execute(new Runnable() { @Override public void run() { @@ -22,5 +23,15 @@ public class ThreadPoolDispatcher extends SameThreadDispatcher { } }); } + + @Override + public void stop() { + executorService.shutdownNow(); + try { + executorService.awaitTermination(1000, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/reactor/src/test/java/com/iluwatar/reactor/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/AppTest.java new file mode 100644 index 000000000..17ce0b912 --- /dev/null +++ b/reactor/src/test/java/com/iluwatar/reactor/AppTest.java @@ -0,0 +1,27 @@ +package com.iluwatar.reactor; + +import java.io.IOException; + +import org.junit.Test; + +public class AppTest { + + @Test + public void testApp() throws IOException { + App app = new App(); + app.start(); + + AppClient client = new AppClient(); + client.start(); + + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + client.stop(); + + app.stop(); + } +} From e5ea9f5c0d1ee162a3733bf086421a51b396b5f0 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 2 Sep 2015 15:21:09 +0530 Subject: [PATCH 06/80] Work on #74, added reactor to parent pom --- pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5a154164e..ce298d61b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,7 @@ half-sync-half-async step-builder layers + reactor @@ -196,4 +197,4 @@ - \ No newline at end of file + From 7ac262b880f9ab5b52dd000a5728c965109b9d8c Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 4 Sep 2015 17:43:01 +0530 Subject: [PATCH 07/80] Work on #74, repackaged and added javadocs --- .../iluwatar/reactor/AbstractNioChannel.java | 74 ------ .../main/java/com/iluwatar/reactor/App.java | 45 ---- .../com/iluwatar/reactor/ChannelHandler.java | 8 - .../java/com/iluwatar/reactor/Dispatcher.java | 8 - .../com/iluwatar/reactor/LoggingHandler.java | 39 --- .../iluwatar/reactor/NioDatagramChannel.java | 80 ------ .../java/com/iluwatar/reactor/NioReactor.java | 170 ------------ .../reactor/SameThreadDispatcher.java | 18 -- .../reactor/ThreadPoolDispatcher.java | 37 --- .../java/com/iluwatar/reactor/app/App.java | 106 ++++++++ .../iluwatar/reactor/{ => app}/AppClient.java | 2 +- .../iluwatar/reactor/app/LoggingHandler.java | 62 +++++ .../reactor/framework/AbstractNioChannel.java | 150 +++++++++++ .../reactor/framework/ChannelHandler.java | 25 ++ .../reactor/framework/Dispatcher.java | 38 +++ .../reactor/framework/NioDatagramChannel.java | 156 +++++++++++ .../reactor/framework/NioReactor.java | 242 ++++++++++++++++++ .../NioServerSocketChannel.java | 38 ++- .../framework/SameThreadDispatcher.java | 43 ++++ .../framework/ThreadPoolDispatcher.java | 55 ++++ .../iluwatar/reactor/{ => app}/AppTest.java | 2 +- reactor/todo.txt | 9 + 22 files changed, 925 insertions(+), 482 deletions(-) delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/App.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/NioReactor.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java delete mode 100644 reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/app/App.java rename reactor/src/main/java/com/iluwatar/reactor/{ => app}/AppClient.java (99%) create mode 100644 reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java rename reactor/src/main/java/com/iluwatar/reactor/{ => framework}/NioServerSocketChannel.java (52%) create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java create mode 100644 reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java rename reactor/src/test/java/com/iluwatar/reactor/{ => app}/AppTest.java (91%) diff --git a/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java deleted file mode 100644 index f55cea073..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/AbstractNioChannel.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.iluwatar.reactor; - -import java.io.IOException; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; - -public abstract class AbstractNioChannel { - - private SelectableChannel channel; - private ChannelHandler handler; - private Map> channelToPendingWrites = new ConcurrentHashMap<>(); - private NioReactor reactor; - - public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { - this.handler = handler; - this.channel = channel; - } - - public void setReactor(NioReactor reactor) { - this.reactor = reactor; - } - - public SelectableChannel getChannel() { - return channel; - } - - public abstract int getInterestedOps(); - - public abstract Object read(SelectionKey key) throws IOException; - - public void setHandler(ChannelHandler handler) { - this.handler = handler; - } - - public ChannelHandler getHandler() { - return handler; - } - - // Called from the context of reactor thread - public void write(SelectionKey key) throws IOException { - Queue pendingWrites = channelToPendingWrites.get(key.channel()); - while (true) { - Object pendingWrite = pendingWrites.poll(); - if (pendingWrite == null) { - System.out.println("No more pending writes"); - reactor.changeOps(key, SelectionKey.OP_READ); - break; - } - - doWrite(pendingWrite, key); - } - } - - protected abstract void doWrite(Object pendingWrite, SelectionKey key) throws IOException; - - public void write(Object data, SelectionKey key) { - Queue pendingWrites = this.channelToPendingWrites.get(key.channel()); - if (pendingWrites == null) { - synchronized (this.channelToPendingWrites) { - pendingWrites = this.channelToPendingWrites.get(key.channel()); - if (pendingWrites == null) { - pendingWrites = new ConcurrentLinkedQueue<>(); - this.channelToPendingWrites.put(key.channel(), pendingWrites); - } - } - } - pendingWrites.add(data); - reactor.changeOps(key, SelectionKey.OP_WRITE); - } -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/App.java b/reactor/src/main/java/com/iluwatar/reactor/App.java deleted file mode 100644 index 7ce27a78b..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/App.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.iluwatar.reactor; - -import java.io.IOException; - -public class App { - - private NioReactor reactor; - - public static void main(String[] args) { - try { - new App().start(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void start() throws IOException { - reactor = new NioReactor(new ThreadPoolDispatcher(2)); - - LoggingHandler loggingHandler = new LoggingHandler(); - - reactor - .registerChannel(tcpChannel(6666, loggingHandler)) - .registerChannel(tcpChannel(6667, loggingHandler)) - .registerChannel(udpChannel(6668, loggingHandler)) - .start(); - } - - public void stop() { - reactor.stop(); - } - - - private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { - NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); - channel.bind(); - return channel; - } - - private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { - NioDatagramChannel channel = new NioDatagramChannel(port, handler); - channel.bind(); - return channel; - } -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java deleted file mode 100644 index e84c506f9..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/ChannelHandler.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.iluwatar.reactor; - -import java.nio.channels.SelectionKey; - -public interface ChannelHandler { - - void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java deleted file mode 100644 index 7c05a6c1d..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/Dispatcher.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.iluwatar.reactor; - -import java.nio.channels.SelectionKey; - -public interface Dispatcher { - void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); - void stop(); -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java deleted file mode 100644 index fc7efaeed..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/LoggingHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.iluwatar.reactor; - -import java.nio.ByteBuffer; -import java.nio.channels.SelectionKey; - -import com.iluwatar.reactor.NioDatagramChannel.DatagramPacket; - -public class LoggingHandler implements ChannelHandler { - - @Override - public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { - if (readObject instanceof ByteBuffer) { - byte[] data = ((ByteBuffer)readObject).array(); - doLogging(data); - sendReply(channel, data, key); - } else if (readObject instanceof DatagramPacket) { - DatagramPacket datagram = (DatagramPacket)readObject; - byte[] data = datagram.getData().array(); - doLogging(data); - sendReply(channel, datagram, key); - } - } - - private void sendReply(AbstractNioChannel channel, DatagramPacket datagram, SelectionKey key) { - DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap("Data logged successfully".getBytes())); - replyPacket.setReceiver(datagram.getSender()); - channel.write(replyPacket, key); - } - - private void sendReply(AbstractNioChannel channel, byte[] data, SelectionKey key) { - ByteBuffer buffer = ByteBuffer.wrap("Data logged successfully".getBytes()); - channel.write(buffer, key); - } - - private void doLogging(byte[] data) { - // assuming UTF-8 :( - System.out.println(new String(data)); - } -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java deleted file mode 100644 index 4d1690792..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/NioDatagramChannel.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.iluwatar.reactor; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.nio.channels.SelectionKey; - -public class NioDatagramChannel extends AbstractNioChannel { - - private int port; - - public NioDatagramChannel(int port, ChannelHandler handler) throws IOException { - super(handler, DatagramChannel.open()); - this.port = port; - } - - @Override - public int getInterestedOps() { - return SelectionKey.OP_READ; - } - - @Override - public Object read(SelectionKey key) throws IOException { - ByteBuffer buffer = ByteBuffer.allocate(1024); - SocketAddress sender = getChannel().receive(buffer); - DatagramPacket packet = new DatagramPacket(buffer); - packet.setSender(sender); - return packet; - } - - @Override - public DatagramChannel getChannel() { - return (DatagramChannel) super.getChannel(); - } - - public void bind() throws IOException { - getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); - getChannel().configureBlocking(false); - System.out.println("Bound UDP socket at port: " + port); - } - - @Override - protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { - DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; - getChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); - } - - static class DatagramPacket { - private SocketAddress sender; - private ByteBuffer data; - private SocketAddress receiver; - - public DatagramPacket(ByteBuffer data) { - this.data = data; - } - - public SocketAddress getSender() { - return sender; - } - - public void setSender(SocketAddress sender) { - this.sender = sender; - } - - public SocketAddress getReceiver() { - return receiver; - } - - public void setReceiver(SocketAddress receiver) { - this.receiver = receiver; - } - - public ByteBuffer getData() { - return data; - } - } -} \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java deleted file mode 100644 index 6ee0cb989..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/NioReactor.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.iluwatar.reactor; - -import java.io.IOException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.nio.channels.ServerSocketChannel; -import java.nio.channels.SocketChannel; -import java.util.Iterator; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -/* - * Abstractions - * --------------- - * 2 - Synchronous Event De-multiplexer - */ -public class NioReactor { - - private Selector selector; - private Dispatcher dispatcher; - private Queue pendingChanges = new ConcurrentLinkedQueue<>(); - private ExecutorService reactorService = Executors.newSingleThreadExecutor(); - - public NioReactor(Dispatcher dispatcher) throws IOException { - this.dispatcher = dispatcher; - this.selector = Selector.open(); - } - - public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { - SelectionKey key = channel.getChannel().register(selector, channel.getInterestedOps()); - key.attach(channel); - channel.setReactor(this); - return this; - } - - public void start() throws IOException { - reactorService.execute(new Runnable() { - @Override - public void run() { - try { - System.out.println("Reactor started, waiting for events..."); - eventLoop(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - } - - public void stop() { - reactorService.shutdownNow(); - selector.wakeup(); - try { - reactorService.awaitTermination(4, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - dispatcher.stop(); - } - - private void eventLoop() throws IOException { - while (true) { - - if (Thread.interrupted()) { - break; - } - - // honor any pending requests first - processPendingChanges(); - - selector.select(); - - Set keys = selector.selectedKeys(); - - Iterator iterator = keys.iterator(); - - while (iterator.hasNext()) { - SelectionKey key = iterator.next(); - if (!key.isValid()) { - iterator.remove(); - continue; - } - processKey(key); - } - keys.clear(); - } - } - - private void processPendingChanges() { - Iterator iterator = pendingChanges.iterator(); - while (iterator.hasNext()) { - Command command = iterator.next(); - command.execute(); - iterator.remove(); - } - } - - private void processKey(SelectionKey key) throws IOException { - if (key.isAcceptable()) { - acceptConnection(key); - } else if (key.isReadable()) { - read(key); - } else if (key.isWritable()) { - write(key); - } - } - - private void write(SelectionKey key) throws IOException { - AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); - channel.write(key); - } - - private void read(SelectionKey key) { - Object readObject; - try { - readObject = ((AbstractNioChannel)key.attachment()).read(key); - dispatchReadEvent(key, readObject); - } catch (IOException e) { - try { - key.channel().close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } - - private void dispatchReadEvent(SelectionKey key, Object readObject) { - dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readObject, key); - } - - private void acceptConnection(SelectionKey key) throws IOException { - ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); - SocketChannel socketChannel = serverSocketChannel.accept(); - socketChannel.configureBlocking(false); - SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); - readKey.attach(key.attachment()); - } - - interface Command { - void execute(); - } - - public void changeOps(SelectionKey key, int interestedOps) { - pendingChanges.add(new ChangeKeyOpsCommand(key, interestedOps)); - selector.wakeup(); - } - - class ChangeKeyOpsCommand implements Command { - private SelectionKey key; - private int interestedOps; - - public ChangeKeyOpsCommand(SelectionKey key, int interestedOps) { - this.key = key; - this.interestedOps = interestedOps; - } - - public void execute() { - key.interestOps(interestedOps); - } - - @Override - public String toString() { - return "Change of ops to: " + interestedOps; - } - } -} \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java deleted file mode 100644 index c27050a15..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/SameThreadDispatcher.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iluwatar.reactor; - -import java.nio.channels.SelectionKey; - -public class SameThreadDispatcher implements Dispatcher { - - @Override - public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { - if (channel.getHandler() != null) { - channel.getHandler().handleChannelRead(channel, readObject, key); - } - } - - @Override - public void stop() { - // no-op - } -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java deleted file mode 100644 index 600cb4da4..000000000 --- a/reactor/src/main/java/com/iluwatar/reactor/ThreadPoolDispatcher.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.iluwatar.reactor; - -import java.nio.channels.SelectionKey; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -public class ThreadPoolDispatcher extends SameThreadDispatcher { - - private ExecutorService executorService; - - public ThreadPoolDispatcher(int poolSize) { - this.executorService = Executors.newFixedThreadPool(poolSize); - } - - @Override - public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { - executorService.execute(new Runnable() { - - @Override - public void run() { - ThreadPoolDispatcher.super.onChannelReadEvent(channel, readObject, key); - } - }); - } - - @Override - public void stop() { - executorService.shutdownNow(); - try { - executorService.awaitTermination(1000, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - -} diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java new file mode 100644 index 000000000..d7b280465 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -0,0 +1,106 @@ +package com.iluwatar.reactor.app; + +import java.io.IOException; + +import com.iluwatar.reactor.framework.AbstractNioChannel; +import com.iluwatar.reactor.framework.ChannelHandler; +import com.iluwatar.reactor.framework.NioDatagramChannel; +import com.iluwatar.reactor.framework.NioReactor; +import com.iluwatar.reactor.framework.NioServerSocketChannel; +import com.iluwatar.reactor.framework.ThreadPoolDispatcher; + +/** + * This application demonstrates Reactor pattern. It represents a Distributed Logging Service + * where it can listen on multiple TCP or UDP sockets for incoming log requests. + * + *

+ * INTENT + *
+ * The Reactor design pattern handles service requests that are delivered concurrently to an + * application by one or more clients. The application can register specific handlers for processing + * which are called by reactor on specific events. + * + *

+ * PROBLEM + *
+ * Server applications in a distributed system must handle multiple clients that send them service + * requests. Following forces need to be resolved: + *

    + *
  • Availability
  • + *
  • Efficiency
  • + *
  • Programming Simplicity
  • + *
  • Adaptability
  • + *
+ * + *

+ * The application utilizes single thread to listen for requests on all ports. It does not create + * a separate thread for each client, which provides better scalability under load (number of clients + * increase). + * + *

+ * The example uses Java NIO framework to implement the Reactor. + * + * @author npathai + * + */ +public class App { + + private NioReactor reactor; + + /** + * App entry. + */ + public static void main(String[] args) { + try { + new App().start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Starts the NIO reactor. + * @throws IOException if any channel fails to bind. + */ + public void start() throws IOException { + /* + * The application can customize its event dispatching mechanism. + */ + reactor = new NioReactor(new ThreadPoolDispatcher(2)); + + /* + * This represents application specific business logic that dispatcher will call + * on appropriate events. These events are read and write event in our example. + */ + LoggingHandler loggingHandler = new LoggingHandler(); + + /* + * Our application binds to multiple I/O channels and uses same logging handler to handle + * incoming log requests. + */ + reactor + .registerChannel(tcpChannel(6666, loggingHandler)) + .registerChannel(tcpChannel(6667, loggingHandler)) + .registerChannel(udpChannel(6668, loggingHandler)) + .start(); + } + + /** + * Stops the NIO reactor. This is a blocking call. + */ + public void stop() { + reactor.stop(); + } + + private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { + NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); + channel.bind(); + return channel; + } + + private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { + NioDatagramChannel channel = new NioDatagramChannel(port, handler); + channel.bind(); + return channel; + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java similarity index 99% rename from reactor/src/main/java/com/iluwatar/reactor/AppClient.java rename to reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java index 2ffb6c0de..e5a7dd145 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -1,4 +1,4 @@ -package com.iluwatar.reactor; +package com.iluwatar.reactor.app; import java.io.IOException; import java.io.InputStream; diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java new file mode 100644 index 000000000..6fa95de2d --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -0,0 +1,62 @@ +package com.iluwatar.reactor.app; + +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; + +import com.iluwatar.reactor.framework.AbstractNioChannel; +import com.iluwatar.reactor.framework.ChannelHandler; +import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; + +/** + * Logging server application logic. It logs the incoming requests on standard console and returns + * a canned acknowledgement back to the remote peer. + * + * @author npathai + */ +public class LoggingHandler implements ChannelHandler { + + private static final byte[] ACK = "Data logged successfully".getBytes(); + + /** + * Decodes the received data and logs it on standard console. + */ + @Override + public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { + /* + * As this channel is attached to both TCP and UDP channels we need to check whether + * the data received is a ByteBuffer (from TCP channel) or a DatagramPacket (from UDP channel). + */ + if (readObject instanceof ByteBuffer) { + byte[] data = ((ByteBuffer)readObject).array(); + doLogging(data); + sendReply(channel, data, key); + } else if (readObject instanceof DatagramPacket) { + DatagramPacket datagram = (DatagramPacket)readObject; + byte[] data = datagram.getData().array(); + doLogging(data); + sendReply(channel, datagram, key); + } else { + throw new IllegalStateException("Unknown data received"); + } + } + + private void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) { + /* + * Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming message. + */ + DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap(ACK)); + replyPacket.setReceiver(incomingPacket.getSender()); + + channel.write(replyPacket, key); + } + + private void sendReply(AbstractNioChannel channel, byte[] data, SelectionKey key) { + ByteBuffer buffer = ByteBuffer.wrap(ACK); + channel.write(buffer, key); + } + + private void doLogging(byte[] data) { + // assuming UTF-8 :( + System.out.println(new String(data)); + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java new file mode 100644 index 000000000..a4b18179a --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -0,0 +1,150 @@ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * This represents the Handle of Reactor pattern. These are resources managed by OS + * which can be submitted to {@link NioReactor}. + * + *

+ * This class serves has the responsibility of reading the data when a read event occurs and + * writing the data back when the channel is writable. It leaves the reading and writing of + * data on the concrete implementation. It provides a block writing mechanism wherein when + * any {@link ChannelHandler} wants to write data back, it queues the data in pending write queue + * and clears it in block manner. This provides better throughput. + * + * @author npathai + * + */ +public abstract class AbstractNioChannel { + + private SelectableChannel channel; + private ChannelHandler handler; + private Map> channelToPendingWrites = new ConcurrentHashMap<>(); + private NioReactor reactor; + + /** + * Creates a new channel. + * @param handler which will handle events occurring on this channel. + * @param channel a NIO channel to be wrapped. + */ + public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { + this.handler = handler; + this.channel = channel; + } + + /** + * Injects the reactor in this channel. + */ + void setReactor(NioReactor reactor) { + this.reactor = reactor; + } + + /** + * @return the wrapped NIO channel. + */ + public SelectableChannel getChannel() { + return channel; + } + + /** + * The operation in which the channel is interested, this operation is be provided to {@link Selector}. + * + * @return interested operation. + * @see SelectionKey + */ + public abstract int getInterestedOps(); + + /** + * Requests the channel to bind. + * + * @throws IOException if any I/O error occurs. + */ + public abstract void bind() throws IOException; + + /** + * Reads the data using the key and returns the read data. + * @param key the key which is readable. + * @return data read. + * @throws IOException if any I/O error occurs. + */ + public abstract Object read(SelectionKey key) throws IOException; + + /** + * @return the handler associated with this channel. + */ + public ChannelHandler getHandler() { + return handler; + } + + /* + * Called from the context of reactor thread when the key becomes writable. + * The channel writes the whole pending block of data at once. + */ + void flush(SelectionKey key) throws IOException { + Queue pendingWrites = channelToPendingWrites.get(key.channel()); + while (true) { + Object pendingWrite = pendingWrites.poll(); + if (pendingWrite == null) { + // We don't have anything more to write so channel is interested in reading more data + reactor.changeOps(key, SelectionKey.OP_READ); + break; + } + + // ask the concrete channel to make sense of data and write it to java channel + doWrite(pendingWrite, key); + } + } + + /** + * Writes the data to the channel. + * + * @param pendingWrite data which was queued for writing in batch mode. + * @param key the key which is writable. + * @throws IOException if any I/O error occurs. + */ + protected abstract void doWrite(Object pendingWrite, SelectionKey key) throws IOException; + + /** + * Queues the data for writing. The data is not guaranteed to be written on underlying channel + * when this method returns. It will be written when the channel is flushed. + * + *

+ * This method is used by the {@link ChannelHandler} to send reply back to the client. + *
+ * Example: + *

+	 * 
+	 * {@literal @}Override
+	 * public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) {
+	 *   byte[] data = ((ByteBuffer)readObject).array();
+	 *   ByteBuffer buffer = ByteBuffer.wrap("Server reply".getBytes());
+	 *   channel.write(buffer, key);
+	 * }
+	 * 
+	 * 
+	 * @param data the data to be written on underlying channel.
+	 * @param key the key which is writable.
+	 */
+	public void write(Object data, SelectionKey key) {
+		Queue pendingWrites = this.channelToPendingWrites.get(key.channel());
+		if (pendingWrites == null) {
+			synchronized (this.channelToPendingWrites) {
+				pendingWrites = this.channelToPendingWrites.get(key.channel());
+				if (pendingWrites == null) {
+					pendingWrites = new ConcurrentLinkedQueue<>();
+					this.channelToPendingWrites.put(key.channel(), pendingWrites);
+				}
+			}
+		}
+		pendingWrites.add(data);
+		reactor.changeOps(key, SelectionKey.OP_WRITE);
+	}
+}
diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java
new file mode 100644
index 000000000..e1df57020
--- /dev/null
+++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java
@@ -0,0 +1,25 @@
+package com.iluwatar.reactor.framework;
+
+import java.nio.channels.SelectionKey;
+
+/**
+ * Represents the EventHandler of Reactor pattern. It handles the incoming events dispatched
+ * to it by the {@link Dispatcher}. This is where the application logic resides.
+ * 
+ * 

+ * A {@link ChannelHandler} is associated with one or many {@link AbstractNioChannel}s, and whenever + * an event occurs on any of the associated channels, the handler is notified of the event. + * + * @author npathai + */ +public interface ChannelHandler { + + /** + * Called when the {@code channel} has received some data from remote peer. + * + * @param channel the channel from which the data is received. + * @param readObject the data read. + * @param key the key from which the data is received. + */ + void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java new file mode 100644 index 000000000..120a11085 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java @@ -0,0 +1,38 @@ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; + +/** + * Represents the event dispatching strategy. When {@link NioReactor} senses any event on the + * registered {@link AbstractNioChannel}s then it de-multiplexes the event type, read or write + * or connect, and then calls the {@link Dispatcher} to dispatch the event. This decouples the I/O + * processing from application specific processing. + *
+ * Dispatcher should call the {@link ChannelHandler} associated with the channel on which event occurred. + * + *

+ * The application can customize the way in which event is dispatched such as using the reactor thread to + * dispatch event to channels or use a worker pool to do the non I/O processing. + * + * @see SameThreadDispatcher + * @see ThreadPoolDispatcher + * + * @author npathai + */ +public interface Dispatcher { + /** + * This hook method is called when read event occurs on particular channel. The data read + * is provided in readObject. The implementation should dispatch this read event + * to the associated {@link ChannelHandler} of channel. + * + * @param channel on which read event occurred + * @param readObject object read by channel + * @param key on which event occurred + */ + void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); + + /** + * Stops the dispatching events and cleans up any acquired resources such as threads. + */ + void stop(); +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java new file mode 100644 index 000000000..2666f05b8 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -0,0 +1,156 @@ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; + +/** + * A wrapper over {@link DatagramChannel} which can read and write data on a DatagramChannel. + * + * @author npathai + */ +public class NioDatagramChannel extends AbstractNioChannel { + + private int port; + + /** + * Creates a {@link DatagramChannel} which will bind at provided port and use handler to handle + * incoming events on this channel. + *

+ * Note the constructor does not bind the socket, {@link #bind()} method should be called for binding + * the socket. + * + * @param port the port to be bound to listen for incoming datagram requests. + * @param handler the handler to be used for handling incoming requests on this channel. + * @throws IOException if any I/O error occurs. + */ + public NioDatagramChannel(int port, ChannelHandler handler) throws IOException { + super(handler, DatagramChannel.open()); + this.port = port; + } + + @Override + public int getInterestedOps() { + /* there is no need to accept connections in UDP, so the channel shows interest in + * reading data. + */ + return SelectionKey.OP_READ; + } + + /** + * Reads and returns a {@link DatagramPacket} from the underlying channel. + * @return the datagram packet read having the sender address. + */ + @Override + public DatagramPacket read(SelectionKey key) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + SocketAddress sender = getChannel().receive(buffer); + + /* + * It is required to create a DatagramPacket because we need to preserve which + * socket address acts as destination for sending reply packets. + */ + DatagramPacket packet = new DatagramPacket(buffer); + packet.setSender(sender); + + return packet; + } + + /** + * @return the underlying datagram channel. + */ + @Override + public DatagramChannel getChannel() { + return (DatagramChannel) super.getChannel(); + } + + /** + * Binds UDP socket on the provided port. + * + * @throws IOException if any I/O error occurs. + */ + @Override + public void bind() throws IOException { + getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + getChannel().configureBlocking(false); + System.out.println("Bound UDP socket at port: " + port); + } + + /** + * Writes the pending {@link DatagramPacket} to the underlying channel sending data to + * the intended receiver of the packet. + */ + @Override + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; + getChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); + } + + /** + * Write the outgoing {@link DatagramPacket} to the channel. The intended receiver of the + * datagram packet must be set in the data using {@link DatagramPacket#setReceiver(SocketAddress)}. + */ + @Override + public void write(Object data, SelectionKey key) { + super.write(data, key); + } + + /** + * Container of data used for {@link NioDatagramChannel} to communicate with remote peer. + */ + public static class DatagramPacket { + private SocketAddress sender; + private ByteBuffer data; + private SocketAddress receiver; + + /** + * Creates a container with underlying data. + * + * @param data the underlying message to be written on channel. + */ + public DatagramPacket(ByteBuffer data) { + this.data = data; + } + + /** + * @return the sender address. + */ + public SocketAddress getSender() { + return sender; + } + + /** + * Sets the sender address of this packet. + * @param sender the sender address. + */ + public void setSender(SocketAddress sender) { + this.sender = sender; + } + + /** + * @return the receiver address. + */ + public SocketAddress getReceiver() { + return receiver; + } + + /** + * Sets the intended receiver address. This must be set when writing to the channel. + * @param receiver the receiver address. + */ + public void setReceiver(SocketAddress receiver) { + this.receiver = receiver; + } + + /** + * @return the underlying message that will be written on channel. + */ + public ByteBuffer getData() { + return data; + } + } +} \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java new file mode 100644 index 000000000..b92f4a9ba --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -0,0 +1,242 @@ +package com.iluwatar.reactor.framework; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +/** + * This class acts as Synchronous Event De-multiplexer and Initiation Dispatcher of Reactor pattern. + * Multiple handles i.e. {@link AbstractNioChannel}s can be registered to the reactor and it blocks + * for events from all these handles. Whenever an event occurs on any of the registered handles, + * it synchronously de-multiplexes the event which can be any of read, write or accept, and + * dispatches the event to the appropriate {@link ChannelHandler} using the {@link Dispatcher}. + * + *

+ * Implementation: + * A NIO reactor runs in its own thread when it is started using {@link #start()} method. + * {@link NioReactor} uses {@link Selector} as a mechanism for achieving Synchronous Event De-multiplexing. + * + *

+ * NOTE: This is one of the way to implement NIO reactor and it does not take care of all possible edge cases + * which may be required in a real application. This implementation is meant to demonstrate the fundamental + * concepts that lie behind Reactor pattern. + * + * @author npathai + * + */ +public class NioReactor { + + private Selector selector; + private Dispatcher dispatcher; + /** + * All the work of altering the SelectionKey operations and Selector operations are performed in + * the context of main event loop of reactor. So when any channel needs to change its readability + * or writability, a new command is added in the command queue and then the event loop picks up + * the command and executes it in next iteration. + */ + private Queue pendingCommands = new ConcurrentLinkedQueue<>(); + private ExecutorService reactorMain = Executors.newSingleThreadExecutor(); + + /** + * Creates a reactor which will use provided {@code dispatcher} to dispatch events. + * The application can provide various implementations of dispatcher which suits its + * needs. + * + * @param dispatcher a non-null dispatcher used to dispatch events on registered channels. + * @throws IOException if any I/O error occurs. + */ + public NioReactor(Dispatcher dispatcher) throws IOException { + this.dispatcher = dispatcher; + this.selector = Selector.open(); + } + + /** + * Starts the reactor event loop in a new thread. + * + * @throws IOException if any I/O error occurs. + */ + public void start() throws IOException { + reactorMain.execute(new Runnable() { + @Override + public void run() { + try { + System.out.println("Reactor started, waiting for events..."); + eventLoop(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } + + /** + * Stops the reactor and related resources such as dispatcher. + */ + public void stop() { + reactorMain.shutdownNow(); + selector.wakeup(); + try { + reactorMain.awaitTermination(4, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + dispatcher.stop(); + } + + /** + * Registers a new channel (handle) with this reactor after which the reactor will wait for events + * on this channel. While registering the channel the reactor uses {@link AbstractNioChannel#getInterestedOps()} + * to know about the interested operation of this channel. + * + * @param channel a new handle on which reactor will wait for events. The channel must be bound + * prior to being registered. + * @return this + * @throws IOException if any I/O error occurs. + */ + public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { + SelectionKey key = channel.getChannel().register(selector, channel.getInterestedOps()); + key.attach(channel); + channel.setReactor(this); + return this; + } + + private void eventLoop() throws IOException { + while (true) { + + // Honor interrupt request + if (Thread.interrupted()) { + break; + } + + // honor any pending commands first + processPendingCommands(); + + /* + * Synchronous event de-multiplexing happens here, this is blocking call which + * returns when it is possible to initiate non-blocking operation on any of the + * registered channels. + */ + selector.select(); + + /* + * Represents the events that have occurred on registered handles. + */ + Set keys = selector.selectedKeys(); + + Iterator iterator = keys.iterator(); + + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + if (!key.isValid()) { + iterator.remove(); + continue; + } + processKey(key); + } + keys.clear(); + } + } + + private void processPendingCommands() { + Iterator iterator = pendingCommands.iterator(); + while (iterator.hasNext()) { + Runnable command = iterator.next(); + command.run(); + iterator.remove(); + } + } + + /* + * Initiation dispatcher logic, it checks the type of event and notifier application + * specific event handler to handle the event. + */ + private void processKey(SelectionKey key) throws IOException { + if (key.isAcceptable()) { + onChannelAcceptable(key); + } else if (key.isReadable()) { + onChannelReadable(key); + } else if (key.isWritable()) { + onChannelWritable(key); + } + } + + private void onChannelWritable(SelectionKey key) throws IOException { + AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); + channel.flush(key); + } + + private void onChannelReadable(SelectionKey key) { + try { + // reads the incoming data in context of reactor main loop. Can this be improved? + Object readObject = ((AbstractNioChannel)key.attachment()).read(key); + + dispatchReadEvent(key, readObject); + } catch (IOException e) { + try { + key.channel().close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + + /* + * Uses the application provided dispatcher to dispatch events to respective handlers. + */ + private void dispatchReadEvent(SelectionKey key, Object readObject) { + dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readObject, key); + } + + private void onChannelAcceptable(SelectionKey key) throws IOException { + ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); + SocketChannel socketChannel = serverSocketChannel.accept(); + socketChannel.configureBlocking(false); + SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); + readKey.attach(key.attachment()); + } + + /** + * Queues the change of operations request of a channel, which will change the interested + * operations of the channel sometime in future. + *

+ * This is a non-blocking method and does not guarantee that the operations are changed when + * this method returns. + * + * @param key the key for which operations are to be changed. + * @param interestedOps the new interest operations. + */ + public void changeOps(SelectionKey key, int interestedOps) { + pendingCommands.add(new ChangeKeyOpsCommand(key, interestedOps)); + selector.wakeup(); + } + + /** + * A command that changes the interested operations of the key provided. + */ + class ChangeKeyOpsCommand implements Runnable { + private SelectionKey key; + private int interestedOps; + + public ChangeKeyOpsCommand(SelectionKey key, int interestedOps) { + this.key = key; + this.interestedOps = interestedOps; + } + + public void run() { + key.interestOps(interestedOps); + } + + @Override + public String toString() { + return "Change of ops to: " + interestedOps; + } + } +} \ No newline at end of file diff --git a/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java similarity index 52% rename from reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java rename to reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index ebd8f0ef3..92fa9234f 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -1,4 +1,4 @@ -package com.iluwatar.reactor; +package com.iluwatar.reactor.framework; import java.io.IOException; import java.net.InetAddress; @@ -8,25 +8,51 @@ import java.nio.channels.SelectionKey; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +/** + * A wrapper over {@link NioServerSocketChannel} which can read and write data on a {@link SocketChannel}. + * + * @author npathai + */ public class NioServerSocketChannel extends AbstractNioChannel { private int port; + /** + * Creates a {@link ServerSocketChannel} which will bind at provided port and use + * handler to handle incoming events on this channel. + *

+ * Note the constructor does not bind the socket, {@link #bind()} method should be called for binding + * the socket. + * + * @param port the port to be bound to listen for incoming requests. + * @param handler the handler to be used for handling incoming requests on this channel. + * @throws IOException if any I/O error occurs. + */ public NioServerSocketChannel(int port, ChannelHandler handler) throws IOException { super(handler, ServerSocketChannel.open()); this.port = port; } + @Override public int getInterestedOps() { + // being a server socket channel it is interested in accepting connection from remote clients. return SelectionKey.OP_ACCEPT; } + /** + * @return the underlying {@link ServerSocketChannel}. + */ @Override public ServerSocketChannel getChannel() { return (ServerSocketChannel) super.getChannel(); } + /** + * Reads and returns {@link ByteBuffer} from the underlying {@link SocketChannel} represented by + * the key. Due to the fact that there is a dedicated channel for each client connection + * we don't need to store the sender. + */ @Override public ByteBuffer read(SelectionKey key) throws IOException { SocketChannel socketChannel = (SocketChannel) key.channel(); @@ -38,12 +64,22 @@ public class NioServerSocketChannel extends AbstractNioChannel { return buffer; } + /** + * Binds TCP socket on the provided port. + * + * @throws IOException if any I/O error occurs. + */ + @Override public void bind() throws IOException { ((ServerSocketChannel)getChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); ((ServerSocketChannel)getChannel()).configureBlocking(false); System.out.println("Bound TCP socket at port: " + port); } + /** + * Writes the pending {@link ByteBuffer} to the underlying channel sending data to + * the intended receiver of the packet. + */ @Override protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java new file mode 100644 index 000000000..2300d7c74 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java @@ -0,0 +1,43 @@ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; + +/** + * Dispatches the events in the context of caller thread. This implementation is a good fit for + * small applications where there are limited clients. Using this implementation limits the scalability + * because the I/O thread performs the application specific processing. + * + *

+ * For real applications use {@link ThreadPoolDispatcher}. + * + * @see ThreadPoolDispatcher + * + * @author npathai + */ +public class SameThreadDispatcher implements Dispatcher { + + /** + * Dispatches the read event in the context of caller thread. + *
+ * Note this is a blocking call. It returns only after the associated handler has handled the + * read event. + */ + @Override + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { + if (channel.getHandler() != null) { + /* + * Calls the associated handler to notify the read event where application specific code + * resides. + */ + channel.getHandler().handleChannelRead(channel, readObject, key); + } + } + + /** + * No resources to free. + */ + @Override + public void stop() { + // no-op + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java new file mode 100644 index 000000000..b514d1824 --- /dev/null +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java @@ -0,0 +1,55 @@ +package com.iluwatar.reactor.framework; + +import java.nio.channels.SelectionKey; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * An implementation that uses a pool of worker threads to dispatch the events. This provides + * for better scalability as the application specific processing is not performed in the context + * of I/O thread. + * + * @author npathai + * + */ +public class ThreadPoolDispatcher extends SameThreadDispatcher { + + private ExecutorService executorService; + + /** + * Creates a pooled dispatcher with tunable pool size. + * + * @param poolSize number of pooled threads + */ + public ThreadPoolDispatcher(int poolSize) { + this.executorService = Executors.newFixedThreadPool(poolSize); + } + + /** + * Submits the work of dispatching the read event to worker pool, where it gets picked + * up by worker threads. + *
+ * Note that this is a non-blocking call and returns immediately. It is not guaranteed + * that the event has been handled by associated handler. + */ + @Override + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { + executorService.execute(() -> + ThreadPoolDispatcher.super.onChannelReadEvent(channel, readObject, key)); + } + + /** + * Stops the pool of workers. + */ + @Override + public void stop() { + executorService.shutdownNow(); + try { + executorService.awaitTermination(1000, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/reactor/src/test/java/com/iluwatar/reactor/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java similarity index 91% rename from reactor/src/test/java/com/iluwatar/reactor/AppTest.java rename to reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java index 17ce0b912..9447aac01 100644 --- a/reactor/src/test/java/com/iluwatar/reactor/AppTest.java +++ b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java @@ -1,4 +1,4 @@ -package com.iluwatar.reactor; +package com.iluwatar.reactor.app; import java.io.IOException; diff --git a/reactor/todo.txt b/reactor/todo.txt index af06a1892..a59af62b9 100644 --- a/reactor/todo.txt +++ b/reactor/todo.txt @@ -2,3 +2,12 @@ * Cleanup * Document - Javadoc * Better design?? Get review of @iluwatar + + +Design view: + +Handles ---> AbstractNioChannel +Selector ---> Synchronous Event Demultiplexer +NioReactor ---> Initiation Dispatcher + + From 363d2c38456a152ee09858d42e9f3b5644d5b2b0 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 6 Sep 2015 14:01:29 +0530 Subject: [PATCH 08/80] Work on #74, improved documentation and minor changes --- .../java/com/iluwatar/reactor/app/App.java | 17 +- .../com/iluwatar/reactor/app/AppClient.java | 153 +++++++++++------- .../iluwatar/reactor/app/LoggingHandler.java | 18 +-- .../reactor/framework/AbstractNioChannel.java | 12 +- .../reactor/framework/ChannelHandler.java | 8 +- .../reactor/framework/Dispatcher.java | 7 +- .../reactor/framework/NioDatagramChannel.java | 5 +- .../reactor/framework/NioReactor.java | 25 ++- .../framework/NioServerSocketChannel.java | 8 +- .../framework/SameThreadDispatcher.java | 2 +- .../framework/ThreadPoolDispatcher.java | 6 +- 11 files changed, 144 insertions(+), 117 deletions(-) diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java index d7b280465..947173494 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -10,8 +10,8 @@ import com.iluwatar.reactor.framework.NioServerSocketChannel; import com.iluwatar.reactor.framework.ThreadPoolDispatcher; /** - * This application demonstrates Reactor pattern. It represents a Distributed Logging Service - * where it can listen on multiple TCP or UDP sockets for incoming log requests. + * This application demonstrates Reactor pattern. The example demonstrated is a Distributed Logging Service + * where it listens on multiple TCP or UDP sockets for incoming log requests. * *

* INTENT @@ -49,13 +49,10 @@ public class App { /** * App entry. + * @throws IOException */ - public static void main(String[] args) { - try { - new App().start(); - } catch (IOException e) { - e.printStackTrace(); - } + public static void main(String[] args) throws IOException { + new App().start(); } /** @@ -70,12 +67,12 @@ public class App { /* * This represents application specific business logic that dispatcher will call - * on appropriate events. These events are read and write event in our example. + * on appropriate events. These events are read events in our example. */ LoggingHandler loggingHandler = new LoggingHandler(); /* - * Our application binds to multiple I/O channels and uses same logging handler to handle + * Our application binds to multiple channels and uses same logging handler to handle * incoming log requests. */ reactor diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java index e5a7dd145..033711569 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -9,24 +9,43 @@ import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; -import java.net.SocketException; +import java.net.UnknownHostException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +/** + * Represents the clients of Reactor pattern. Multiple clients are run concurrently and send logging + * requests to Reactor. + * + * @author npathai + */ public class AppClient { - private ExecutorService service = Executors.newFixedThreadPool(3); - - public static void main(String[] args) { - new AppClient().start(); + private ExecutorService service = Executors.newFixedThreadPool(4); + + /** + * App client entry. + * @throws IOException if any I/O error occurs. + */ + public static void main(String[] args) throws IOException { + AppClient appClient = new AppClient(); + appClient.start(); } - public void start() { - service.execute(new LoggingClient("Client 1", 6666)); - service.execute(new LoggingClient("Client 2", 6667)); - service.execute(new UDPLoggingClient(6668)); + /** + * Starts the logging clients. + * @throws IOException if any I/O error occurs. + */ + public void start() throws IOException { + service.execute(new TCPLoggingClient("Client 1", 6666)); + service.execute(new TCPLoggingClient("Client 2", 6667)); + service.execute(new UDPLoggingClient("Client 3", 6668)); + service.execute(new UDPLoggingClient("Client 4", 6668)); } - + + /** + * Stops logging clients. This is a blocking call. + */ public void stop() { service.shutdown(); if (!service.isTerminated()) { @@ -39,49 +58,49 @@ public class AppClient { } } - /* - * A logging client that sends logging requests to logging server + private static void artificialDelayOf(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * A logging client that sends requests to Reactor on TCP socket. */ - static class LoggingClient implements Runnable { + static class TCPLoggingClient implements Runnable { private int serverPort; private String clientName; - public LoggingClient(String clientName, int serverPort) { + /** + * Creates a new TCP logging client. + * + * @param clientName the name of the client to be sent in logging requests. + * @param port the port on which client will send logging requests. + */ + public TCPLoggingClient(String clientName, int serverPort) { this.clientName = clientName; this.serverPort = serverPort; } public void run() { - Socket socket = null; - try { - socket = new Socket(InetAddress.getLocalHost(), serverPort); + try (Socket socket = new Socket(InetAddress.getLocalHost(), serverPort)) { OutputStream outputStream = socket.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); - writeLogs(writer, socket.getInputStream()); + sendLogRequests(writer, socket.getInputStream()); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); - } finally { - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } } } - private void writeLogs(PrintWriter writer, InputStream inputStream) throws IOException { + private void sendLogRequests(PrintWriter writer, InputStream inputStream) throws IOException { for (int i = 0; i < 4; i++) { writer.println(clientName + " - Log request: " + i); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } writer.flush(); + byte[] data = new byte[1024]; int read = inputStream.read(data, 0, data.length); if (read == 0) { @@ -89,46 +108,56 @@ public class AppClient { } else { System.out.println(new String(data, 0, read)); } + + artificialDelayOf(100); } } - } - - static class UDPLoggingClient implements Runnable { - private int port; - public UDPLoggingClient(int port) { - this.port = port; + } + + /** + * A logging client that sends requests to Reactor on UDP socket. + */ + static class UDPLoggingClient implements Runnable { + private String clientName; + private InetSocketAddress remoteAddress; + + /** + * Creates a new UDP logging client. + * + * @param clientName the name of the client to be sent in logging requests. + * @param port the port on which client will send logging requests. + * @throws UnknownHostException if localhost is unknown + */ + public UDPLoggingClient(String clientName, int port) throws UnknownHostException { + this.clientName = clientName; + this.remoteAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); } - + @Override public void run() { - DatagramSocket socket = null; - try { - socket = new DatagramSocket(); + try (DatagramSocket socket = new DatagramSocket()) { for (int i = 0; i < 4; i++) { - String message = "UDP Client" + " - Log request: " + i; - try { - DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, new InetSocketAddress(InetAddress.getLocalHost(), port)); - socket.send(packet); - - byte[] data = new byte[1024]; - DatagramPacket reply = new DatagramPacket(data, data.length); - socket.receive(reply); - if (reply.getLength() == 0) { - System.out.println("Read zero bytes"); - } else { - System.out.println(new String(reply.getData(), 0, reply.getLength())); - } - } catch (IOException e) { - e.printStackTrace(); + + String message = clientName + " - Log request: " + i; + DatagramPacket request = new DatagramPacket(message.getBytes(), + message.getBytes().length, remoteAddress); + + socket.send(request); + + byte[] data = new byte[1024]; + DatagramPacket reply = new DatagramPacket(data, data.length); + socket.receive(reply); + if (reply.getLength() == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(reply.getData(), 0, reply.getLength())); } + + artificialDelayOf(100); } - } catch (SocketException e1) { + } catch (IOException e1) { e1.printStackTrace(); - } finally { - if (socket != null) { - socket.close(); - } } } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java index 6fa95de2d..eed26b078 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -9,7 +9,7 @@ import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; /** * Logging server application logic. It logs the incoming requests on standard console and returns - * a canned acknowledgement back to the remote peer. + * a canned acknowledgement back to the remote peer. * * @author npathai */ @@ -23,17 +23,15 @@ public class LoggingHandler implements ChannelHandler { @Override public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { /* - * As this channel is attached to both TCP and UDP channels we need to check whether + * As this handler is attached with both TCP and UDP channels we need to check whether * the data received is a ByteBuffer (from TCP channel) or a DatagramPacket (from UDP channel). */ if (readObject instanceof ByteBuffer) { - byte[] data = ((ByteBuffer)readObject).array(); - doLogging(data); - sendReply(channel, data, key); + doLogging(((ByteBuffer)readObject)); + sendReply(channel, key); } else if (readObject instanceof DatagramPacket) { DatagramPacket datagram = (DatagramPacket)readObject; - byte[] data = datagram.getData().array(); - doLogging(data); + doLogging(datagram.getData()); sendReply(channel, datagram, key); } else { throw new IllegalStateException("Unknown data received"); @@ -50,13 +48,13 @@ public class LoggingHandler implements ChannelHandler { channel.write(replyPacket, key); } - private void sendReply(AbstractNioChannel channel, byte[] data, SelectionKey key) { + private void sendReply(AbstractNioChannel channel, SelectionKey key) { ByteBuffer buffer = ByteBuffer.wrap(ACK); channel.write(buffer, key); } - private void doLogging(byte[] data) { + private void doLogging(ByteBuffer data) { // assuming UTF-8 :( - System.out.println(new String(data)); + System.out.println(new String(data.array(), 0, data.limit())); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java index a4b18179a..24862644d 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -55,7 +55,7 @@ public abstract class AbstractNioChannel { } /** - * The operation in which the channel is interested, this operation is be provided to {@link Selector}. + * The operation in which the channel is interested, this operation is provided to {@link Selector}. * * @return interested operation. * @see SelectionKey @@ -63,15 +63,17 @@ public abstract class AbstractNioChannel { public abstract int getInterestedOps(); /** - * Requests the channel to bind. + * Binds the channel on provided port. * * @throws IOException if any I/O error occurs. */ public abstract void bind() throws IOException; /** - * Reads the data using the key and returns the read data. - * @param key the key which is readable. + * Reads the data using the key and returns the read data. The underlying channel should be fetched using + * {@link SelectionKey#channel()}. + * + * @param key the key on which read event occurred. * @return data read. * @throws IOException if any I/O error occurs. */ @@ -106,7 +108,7 @@ public abstract class AbstractNioChannel { /** * Writes the data to the channel. * - * @param pendingWrite data which was queued for writing in batch mode. + * @param pendingWrite the data to be written on channel. * @param key the key which is writable. * @throws IOException if any I/O error occurs. */ diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java index e1df57020..0aae9db75 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java @@ -7,7 +7,7 @@ import java.nio.channels.SelectionKey; * to it by the {@link Dispatcher}. This is where the application logic resides. * *

- * A {@link ChannelHandler} is associated with one or many {@link AbstractNioChannel}s, and whenever + * A {@link ChannelHandler} can be associated with one or many {@link AbstractNioChannel}s, and whenever * an event occurs on any of the associated channels, the handler is notified of the event. * * @author npathai @@ -15,11 +15,11 @@ import java.nio.channels.SelectionKey; public interface ChannelHandler { /** - * Called when the {@code channel} has received some data from remote peer. + * Called when the {@code channel} receives some data from remote peer. * - * @param channel the channel from which the data is received. + * @param channel the channel from which the data was received. * @param readObject the data read. - * @param key the key from which the data is received. + * @param key the key on which read event occurred. */ void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java index 120a11085..c563ef9d3 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java @@ -5,7 +5,7 @@ import java.nio.channels.SelectionKey; /** * Represents the event dispatching strategy. When {@link NioReactor} senses any event on the * registered {@link AbstractNioChannel}s then it de-multiplexes the event type, read or write - * or connect, and then calls the {@link Dispatcher} to dispatch the event. This decouples the I/O + * or connect, and then calls the {@link Dispatcher} to dispatch the read events. This decouples the I/O * processing from application specific processing. *
* Dispatcher should call the {@link ChannelHandler} associated with the channel on which event occurred. @@ -24,6 +24,9 @@ public interface Dispatcher { * This hook method is called when read event occurs on particular channel. The data read * is provided in readObject. The implementation should dispatch this read event * to the associated {@link ChannelHandler} of channel. + * + *

+ * The type of readObject depends on the channel on which data was received. * * @param channel on which read event occurred * @param readObject object read by channel @@ -32,7 +35,7 @@ public interface Dispatcher { void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); /** - * Stops the dispatching events and cleans up any acquired resources such as threads. + * Stops dispatching events and cleans up any acquired resources such as threads. */ void stop(); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java index 2666f05b8..f338ce4a3 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -48,12 +48,13 @@ public class NioDatagramChannel extends AbstractNioChannel { @Override public DatagramPacket read(SelectionKey key) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1024); - SocketAddress sender = getChannel().receive(buffer); + SocketAddress sender = ((DatagramChannel)key.channel()).receive(buffer); /* * It is required to create a DatagramPacket because we need to preserve which * socket address acts as destination for sending reply packets. */ + buffer.flip(); DatagramPacket packet = new DatagramPacket(buffer); packet.setSender(sender); @@ -91,7 +92,7 @@ public class NioDatagramChannel extends AbstractNioChannel { } /** - * Write the outgoing {@link DatagramPacket} to the channel. The intended receiver of the + * Writes the outgoing {@link DatagramPacket} to the channel. The intended receiver of the * datagram packet must be set in the data using {@link DatagramPacket#setReceiver(SocketAddress)}. */ @Override diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java index b92f4a9ba..273898ae3 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -22,11 +22,11 @@ import java.util.concurrent.TimeUnit; *

* Implementation: * A NIO reactor runs in its own thread when it is started using {@link #start()} method. - * {@link NioReactor} uses {@link Selector} as a mechanism for achieving Synchronous Event De-multiplexing. + * {@link NioReactor} uses {@link Selector} for realizing Synchronous Event De-multiplexing. * *

- * NOTE: This is one of the way to implement NIO reactor and it does not take care of all possible edge cases - * which may be required in a real application. This implementation is meant to demonstrate the fundamental + * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible edge cases + * which are required in a real application. This implementation is meant to demonstrate the fundamental * concepts that lie behind Reactor pattern. * * @author npathai @@ -64,16 +64,13 @@ public class NioReactor { * @throws IOException if any I/O error occurs. */ public void start() throws IOException { - reactorMain.execute(new Runnable() { - @Override - public void run() { + reactorMain.execute(() -> { try { System.out.println("Reactor started, waiting for events..."); eventLoop(); } catch (IOException e) { e.printStackTrace(); } - } }); } @@ -92,11 +89,11 @@ public class NioReactor { } /** - * Registers a new channel (handle) with this reactor after which the reactor will wait for events - * on this channel. While registering the channel the reactor uses {@link AbstractNioChannel#getInterestedOps()} + * Registers a new channel (handle) with this reactor. Reactor will start waiting for events on this channel + * and notify of any events. While registering the channel the reactor uses {@link AbstractNioChannel#getInterestedOps()} * to know about the interested operation of this channel. * - * @param channel a new handle on which reactor will wait for events. The channel must be bound + * @param channel a new channel on which reactor will wait for events. The channel must be bound * prior to being registered. * @return this * @throws IOException if any I/O error occurs. @@ -111,7 +108,7 @@ public class NioReactor { private void eventLoop() throws IOException { while (true) { - // Honor interrupt request + // honor interrupt request if (Thread.interrupted()) { break; } @@ -189,7 +186,7 @@ public class NioReactor { } /* - * Uses the application provided dispatcher to dispatch events to respective handlers. + * Uses the application provided dispatcher to dispatch events to application handler. */ private void dispatchReadEvent(SelectionKey key, Object readObject) { dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readObject, key); @@ -207,10 +204,10 @@ public class NioReactor { * Queues the change of operations request of a channel, which will change the interested * operations of the channel sometime in future. *

- * This is a non-blocking method and does not guarantee that the operations are changed when + * This is a non-blocking method and does not guarantee that the operations have changed when * this method returns. * - * @param key the key for which operations are to be changed. + * @param key the key for which operations have to be changed. * @param interestedOps the new interest operations. */ public void changeOps(SelectionKey key, int interestedOps) { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index 92fa9234f..ae54af643 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -24,8 +24,8 @@ public class NioServerSocketChannel extends AbstractNioChannel { * Note the constructor does not bind the socket, {@link #bind()} method should be called for binding * the socket. * - * @param port the port to be bound to listen for incoming requests. - * @param handler the handler to be used for handling incoming requests on this channel. + * @param port the port on which channel will be bound to accept incoming connection requests. + * @param handler the handler that will handle incoming requests on this channel. * @throws IOException if any I/O error occurs. */ public NioServerSocketChannel(int port, ChannelHandler handler) throws IOException { @@ -36,7 +36,7 @@ public class NioServerSocketChannel extends AbstractNioChannel { @Override public int getInterestedOps() { - // being a server socket channel it is interested in accepting connection from remote clients. + // being a server socket channel it is interested in accepting connection from remote peers. return SelectionKey.OP_ACCEPT; } @@ -58,6 +58,7 @@ public class NioServerSocketChannel extends AbstractNioChannel { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int read = socketChannel.read(buffer); + buffer.flip(); if (read == -1) { throw new IOException("Socket closed"); } @@ -83,7 +84,6 @@ public class NioServerSocketChannel extends AbstractNioChannel { @Override protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; - System.out.println("Writing on channel"); ((SocketChannel)key.channel()).write(pendingBuffer); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java index 2300d7c74..b5392ac8f 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java @@ -8,7 +8,7 @@ import java.nio.channels.SelectionKey; * because the I/O thread performs the application specific processing. * *

- * For real applications use {@link ThreadPoolDispatcher}. + * For better performance use {@link ThreadPoolDispatcher}. * * @see ThreadPoolDispatcher * diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java index b514d1824..8624b878e 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java @@ -7,8 +7,8 @@ import java.util.concurrent.TimeUnit; /** * An implementation that uses a pool of worker threads to dispatch the events. This provides - * for better scalability as the application specific processing is not performed in the context - * of I/O thread. + * better scalability as the application specific processing is not performed in the context + * of I/O (reactor) thread. * * @author npathai * @@ -46,7 +46,7 @@ public class ThreadPoolDispatcher extends SameThreadDispatcher { public void stop() { executorService.shutdownNow(); try { - executorService.awaitTermination(1000, TimeUnit.SECONDS); + executorService.awaitTermination(4, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } From 57ff154e0a9dac01a7a929eee3de0629c261c4ab Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 6 Sep 2015 14:09:00 +0530 Subject: [PATCH 09/80] Changed version --- reactor/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reactor/pom.xml b/reactor/pom.xml index 0f3271a9c..516a4b93c 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.5.0 + 1.7.0 reactor From 30f60651952056e775dd7194c882e23263df4b9e Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 6 Sep 2015 14:10:31 +0530 Subject: [PATCH 10/80] Removed todo file --- reactor/todo.txt | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 reactor/todo.txt diff --git a/reactor/todo.txt b/reactor/todo.txt deleted file mode 100644 index a59af62b9..000000000 --- a/reactor/todo.txt +++ /dev/null @@ -1,13 +0,0 @@ -* Make UDP channel work (connect is required) -* Cleanup -* Document - Javadoc -* Better design?? Get review of @iluwatar - - -Design view: - -Handles ---> AbstractNioChannel -Selector ---> Synchronous Event Demultiplexer -NioReactor ---> Initiation Dispatcher - - From 9e401b0f34776f6357b2691ed28cf884e3d2e538 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 6 Sep 2015 14:14:38 +0530 Subject: [PATCH 11/80] Fixed version number --- reactor/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reactor/pom.xml b/reactor/pom.xml index 516a4b93c..599376e32 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.7.0 + 1.6.0 reactor From 02162994a30c500518bf2c9e199b2ee80dfc6f2c Mon Sep 17 00:00:00 2001 From: Hannes Pernpeintner Date: Mon, 17 Aug 2015 03:54:37 +0200 Subject: [PATCH 12/80] #184 Fluent Interface pattern --- fluentinterface/pom.xml | 20 ++ .../com/iluwatar/fluentinterface/App.java | 90 ++++++++ .../fluentiterable/FluentIterable.java | 192 ++++++++++++++++++ .../com/iluwatar/fluentinterface/AppTest.java | 12 ++ pom.xml | 3 +- 5 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 fluentinterface/pom.xml create mode 100644 fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java create mode 100644 fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java create mode 100644 fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml new file mode 100644 index 000000000..c78c182e3 --- /dev/null +++ b/fluentinterface/pom.xml @@ -0,0 +1,20 @@ + + + + java-design-patterns + com.iluwatar + 1.5.0 + + 4.0.0 + + fluentinterface + + + junit + junit + test + + + \ No newline at end of file diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java new file mode 100644 index 000000000..fded13624 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java @@ -0,0 +1,90 @@ +package com.iluwatar.fluentinterface; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Function; +import java.util.function.Predicate; + +public class App { + + public static void main(String[] args) { + + List integerList = new ArrayList() {{ + add(1); + add(-61); + add(14); + add(-22); + add(18); + add(-87); + add(6); + add(64); + add(-82); + add(26); + add(-98); + add(97); + add(45); + add(23); + add(2); + add(-68); + add(45); + }}; + prettyPrint("The initial list contains: ", integerList); + + List firstFiveNegatives = FluentIterable.from(integerList) + .filter(negatives()) + .first(3) + .asList(); + prettyPrint("The first three negative values are: ", firstFiveNegatives); + + + List lastTwoPositives = FluentIterable.from(integerList) + .filter(positives()) + .last(2) + .asList(); + prettyPrint("The last two positive values are: ", lastTwoPositives); + + FluentIterable.from(integerList) + .filter(number -> number%2 == 0) + .first() + .ifPresent(evenNumber -> System.out.println(String.format("The first even number is: %d", evenNumber))); + + + List transformedList = FluentIterable.from(integerList) + .filter(negatives()) + .map(transformToString()) + .asList(); + prettyPrint("A string-mapped list of negative numbers contains: ", transformedList); + + } + + private static Function transformToString() { + return integer -> "String[" + String.valueOf(integer) + "]"; + } + private static Predicate negatives() { + return integer -> (integer < 0); + } + private static Predicate positives() { + return integer -> (integer > 0); + } + + private static void prettyPrint(String prefix, Iterable iterable) { + prettyPrint(", ", prefix, ".", iterable); + } + private static void prettyPrint(String prefix, String suffix, Iterable iterable) { + prettyPrint(", ", prefix, suffix, iterable); + } + + private static void prettyPrint(String delimiter, String prefix, String suffix, Iterable iterable) { + StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + joiner.add(iterator.next().toString()); + } + + System.out.println(joiner); + } +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java new file mode 100644 index 000000000..edb9275c1 --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -0,0 +1,192 @@ +package com.iluwatar.fluentinterface.fluentiterable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * The FluentIterable is a more convenient implementation of the common iterable interface based + * on the fluent interface design pattern. + * This implementation demonstrates a possible way to implement this functionality, but + * doesn't aim to be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. + * @param is the class of objects the iterable contains + */ +public class FluentIterable implements Iterable { + + private final Iterable iterable; + + /** + * This constructor creates a copy of a given iterable's contents. + * @param iterable the iterable this interface copies to work on. + */ + protected FluentIterable(Iterable iterable) { + ArrayList copy = new ArrayList<>(); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + this.iterable = copy; + } + + /** + * Iterates over all elements of this iterator and filters them. + * @param predicate the condition to test with for the filtering. If the test + * is negative, the tested object is removed by the iterator. + * @return the same FluentIterable with a filtered collection + */ + public final FluentIterable filter(Predicate predicate) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + TYPE nextElement = iterator.next(); + if(!predicate.test(nextElement)) { + iterator.remove(); + } + } + return this; + } + + /** + * Uses the Iterable interface's forEach method to apply a given function + * for each object of the iterator. + * @param action the action for each object + * @return the same FluentIterable with an untouched collection + */ + public final FluentIterable forEachDo(Consumer action) { + iterable.forEach(action); + return this; + } + + /** + * Can be used to collect objects from the iteration. + * @return an option of the first object of the iteration + */ + public final Optional first() { + List list = first(1).asList(); + if(list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * Can be used to collect objects from the iteration. + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. + */ + public final FluentIterable first(int count) { + Iterator iterator = iterator(); + int currentCount = 0; + while (iterator.hasNext()) { + iterator.next(); + if(currentCount >= count) { + iterator.remove(); + } + currentCount++; + } + return this; + } + + /** + * Can be used to collect objects from the iteration. + * @return an option of the last object of the iteration + */ + public final Optional last() { + List list = last(1).asList(); + if(list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * Can be used to collect objects from the iteration. + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects + */ + public final FluentIterable last(int count) { + int remainingElementsCount = getRemainingElementsCount(); + Iterator iterator = iterator(); + int currentIndex = 0; + while (iterator.hasNext()) { + iterator.next(); + if(currentIndex < remainingElementsCount - count) { + iterator.remove(); + } + currentIndex++; + } + + return this; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + public final FluentIterable map(Function function) { + List temporaryList = new ArrayList(); + Iterator iterator = iterator(); + while (iterator.hasNext()) { + temporaryList.add(function.apply(iterator.next())); + } + return from(temporaryList); + } + + /** + * Collects all remaining objects of this iteration into a list. + * @return a list with all remaining objects of this iteration + */ + public List asList() { + return toList(iterable.iterator()); + } + + /** + * @return a FluentIterable from a given iterable. Calls the FluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new FluentIterable<>(iterable); + } + + @Override + public Iterator iterator() { + return iterable.iterator(); + } + + @Override + public void forEach(Consumer action) { + iterable.forEach(action); + } + + + @Override + public Spliterator spliterator() { + return iterable.spliterator(); + } + + /** + * @return the count of remaining objects in the current iteration + */ + public final int getRemainingElementsCount() { + int counter = 0; + Iterator iterator = iterator(); + while(iterator.hasNext()) { + iterator.next(); + counter++; + } + return counter; + } + + /** + * Collects the remaining objects of the given iterators iteration into an List. + * @return a new List with the remaining objects. + */ + public static List toList(Iterator iterator) { + List copy = new ArrayList<>(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + return copy; + } +} diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java new file mode 100644 index 000000000..32bbca430 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java @@ -0,0 +1,12 @@ +package com.iluwatar.fluentinterface; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/pom.xml b/pom.xml index 7b0d80bd8..2c040005b 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,8 @@ step-builder layers message-channel - + fluentinterface + From ded21b70acdbfa746343a0ebd6e5d33c5bf37dfb Mon Sep 17 00:00:00 2001 From: Hannes Pernpeintner Date: Sat, 22 Aug 2015 20:22:00 +0200 Subject: [PATCH 13/80] #184 Fluent interface pattern, lazy fluentiterable added --- .../com/iluwatar/fluentinterface/App.java | 34 ++- .../fluentiterable/FluentIterable.java | 175 +++---------- .../lazy/DecoratingIterator.java | 53 ++++ .../lazy/LazyFluentIterable.java | 236 ++++++++++++++++++ .../simple/SimpleFluentIterable.java | 194 ++++++++++++++ 5 files changed, 539 insertions(+), 153 deletions(-) create mode 100644 fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java create mode 100644 fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java create mode 100644 fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java index fded13624..96a2db323 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java @@ -1,14 +1,14 @@ package com.iluwatar.fluentinterface; -import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.StringJoiner; +import java.util.*; import java.util.function.Function; import java.util.function.Predicate; +import static java.lang.String.valueOf; + public class App { public static void main(String[] args) { @@ -34,35 +34,49 @@ public class App { }}; prettyPrint("The initial list contains: ", integerList); - List firstFiveNegatives = FluentIterable.from(integerList) + List firstFiveNegatives = SimpleFluentIterable.from(integerList) .filter(negatives()) .first(3) .asList(); prettyPrint("The first three negative values are: ", firstFiveNegatives); - List lastTwoPositives = FluentIterable.from(integerList) + List lastTwoPositives = SimpleFluentIterable.from(integerList) .filter(positives()) .last(2) .asList(); prettyPrint("The last two positive values are: ", lastTwoPositives); - FluentIterable.from(integerList) + SimpleFluentIterable.from(integerList) .filter(number -> number%2 == 0) .first() .ifPresent(evenNumber -> System.out.println(String.format("The first even number is: %d", evenNumber))); - List transformedList = FluentIterable.from(integerList) + List transformedList = SimpleFluentIterable.from(integerList) .filter(negatives()) .map(transformToString()) .asList(); prettyPrint("A string-mapped list of negative numbers contains: ", transformedList); + + List lastTwoOfFirstFourStringMapped = LazyFluentIterable.from(integerList) + .filter(positives()) + .first(4) + .last(2) + .map(number -> "String[" + String.valueOf(number) + "]") + .asList(); + prettyPrint("The lazy list contains the last two of the first four positive numbers mapped to Strings: ", lastTwoOfFirstFourStringMapped); + + LazyFluentIterable.from(integerList) + .filter(negatives()) + .first(2) + .last() + .ifPresent(lastOfFirstTwo -> System.out.println(String.format("The last of the first two negatives is: %d", lastOfFirstTwo))); } private static Function transformToString() { - return integer -> "String[" + String.valueOf(integer) + "]"; + return integer -> "String[" + valueOf(integer) + "]"; } private static Predicate negatives() { return integer -> (integer < 0); diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java index edb9275c1..7bdaf274c 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -1,6 +1,9 @@ package com.iluwatar.fluentinterface.fluentiterable; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -8,116 +11,49 @@ import java.util.function.Predicate; /** * The FluentIterable is a more convenient implementation of the common iterable interface based * on the fluent interface design pattern. - * This implementation demonstrates a possible way to implement this functionality, but + * This interface defines common operations, but * doesn't aim to be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. * @param is the class of objects the iterable contains */ -public class FluentIterable implements Iterable { - - private final Iterable iterable; +public interface FluentIterable extends Iterable { /** - * This constructor creates a copy of a given iterable's contents. - * @param iterable the iterable this interface copies to work on. - */ - protected FluentIterable(Iterable iterable) { - ArrayList copy = new ArrayList<>(); - Iterator iterator = iterable.iterator(); - while (iterator.hasNext()) { - copy.add(iterator.next()); - } - this.iterable = copy; - } - - /** - * Iterates over all elements of this iterator and filters them. + * Filters the iteration with the given predicate. * @param predicate the condition to test with for the filtering. If the test * is negative, the tested object is removed by the iterator. - * @return the same FluentIterable with a filtered collection + * @return a filtered FluentIterable */ - public final FluentIterable filter(Predicate predicate) { - Iterator iterator = iterator(); - while (iterator.hasNext()) { - TYPE nextElement = iterator.next(); - if(!predicate.test(nextElement)) { - iterator.remove(); - } - } - return this; - } + FluentIterable filter(Predicate predicate); /** * Uses the Iterable interface's forEach method to apply a given function - * for each object of the iterator. - * @param action the action for each object - * @return the same FluentIterable with an untouched collection + * for each object of the iterator. This is a terminating operation. */ - public final FluentIterable forEachDo(Consumer action) { - iterable.forEach(action); - return this; - } + void forEachDo(Consumer action); /** - * Can be used to collect objects from the iteration. - * @return an option of the first object of the iteration + * Evaluates the iteration and returns the first element. This is a terminating operation. + * @return the first element after the iteration is evaluated */ - public final Optional first() { - List list = first(1).asList(); - if(list.isEmpty()) { - return Optional.empty(); - } - return Optional.of(list.get(0)); - } + Optional first(); /** - * Can be used to collect objects from the iteration. - * @param count defines the number of objects to return - * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. + * Evaluates the iteration and leaves only the count first elements. + * @return the first count elements as an Iterable */ - public final FluentIterable first(int count) { - Iterator iterator = iterator(); - int currentCount = 0; - while (iterator.hasNext()) { - iterator.next(); - if(currentCount >= count) { - iterator.remove(); - } - currentCount++; - } - return this; - } + FluentIterable first(int count); /** - * Can be used to collect objects from the iteration. - * @return an option of the last object of the iteration + * Evaluates the iteration and returns the last element. This is a terminating operation. + * @return the last element after the iteration is evaluated */ - public final Optional last() { - List list = last(1).asList(); - if(list.isEmpty()) { - return Optional.empty(); - } - return Optional.of(list.get(0)); - } + Optional last(); /** - * Can be used to collect objects from the iteration. - * @param count defines the number of objects to return - * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects + * Evaluates the iteration and leaves only the count last elements. + * @return the last counts elements as an Iterable */ - public final FluentIterable last(int count) { - int remainingElementsCount = getRemainingElementsCount(); - Iterator iterator = iterator(); - int currentIndex = 0; - while (iterator.hasNext()) { - iterator.next(); - if(currentIndex < remainingElementsCount - count) { - iterator.remove(); - } - currentIndex++; - } - - return this; - } + FluentIterable last(int count); /** * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. @@ -125,65 +61,18 @@ public class FluentIterable implements Iterable { * @param the target type of the transformation * @return a new FluentIterable of the new type */ - public final FluentIterable map(Function function) { - List temporaryList = new ArrayList(); - Iterator iterator = iterator(); - while (iterator.hasNext()) { - temporaryList.add(function.apply(iterator.next())); - } - return from(temporaryList); - } + FluentIterable map(Function function); + List asList(); /** - * Collects all remaining objects of this iteration into a list. - * @return a list with all remaining objects of this iteration + * Utility method that iterates over iterable and adds the contents to a list. + * @param iterable the iterable to collect + * @param the type of the objects to iterate + * @return a list with all objects of the given iterator */ - public List asList() { - return toList(iterable.iterator()); - } - - /** - * @return a FluentIterable from a given iterable. Calls the FluentIterable constructor. - */ - public static final FluentIterable from(Iterable iterable) { - return new FluentIterable<>(iterable); - } - - @Override - public Iterator iterator() { - return iterable.iterator(); - } - - @Override - public void forEach(Consumer action) { - iterable.forEach(action); - } - - - @Override - public Spliterator spliterator() { - return iterable.spliterator(); - } - - /** - * @return the count of remaining objects in the current iteration - */ - public final int getRemainingElementsCount() { - int counter = 0; - Iterator iterator = iterator(); - while(iterator.hasNext()) { - iterator.next(); - counter++; - } - return counter; - } - - /** - * Collects the remaining objects of the given iterators iteration into an List. - * @return a new List with the remaining objects. - */ - public static List toList(Iterator iterator) { - List copy = new ArrayList<>(); + static List copyToList(Iterable iterable) { + ArrayList copy = new ArrayList<>(); + Iterator iterator = iterable.iterator(); while (iterator.hasNext()) { copy.add(iterator.next()); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java new file mode 100644 index 000000000..0e5b410cc --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java @@ -0,0 +1,53 @@ +package com.iluwatar.fluentinterface.fluentiterable.lazy; + +import java.util.Iterator; + +/** + * This class is used to realize LazyFluentIterables. It decorates + * a given iterator. + * @param + */ +public abstract class DecoratingIterator implements Iterator { + + protected final Iterator fromIterator; + + private TYPE next = null; + + /** + * Creates an iterator that decorates the given iterator. + * @param fromIterator + */ + public DecoratingIterator(Iterator fromIterator) { + this.fromIterator = fromIterator; + } + + /** + * Precomputes and caches the next element of the iteration. + * @return true if a next element is available + */ + @Override + public final boolean hasNext() { + next = computeNext(); + return next != null; + } + + /** + * Returns the next element of the iteration. This implementation caches it. + * If no next element is cached, it is computed. + * @return the next element obf the iteration + */ + @Override + public final TYPE next() { + TYPE result = next; + next = null; + result = (result == null ? fromIterator.next() : result); + return result; + } + + /** + * Computes the next object of the iteration. Can be implemented to + * realize custom behaviour for an iteration process. + * @return + */ + public abstract TYPE computeNext(); +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java new file mode 100644 index 000000000..c6db4d2cd --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -0,0 +1,236 @@ +package com.iluwatar.fluentinterface.fluentiterable.lazy; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * This is a lazy implementation of the FluentIterable interface. It evaluates + * all chained operations when a terminating operation is applied. + * @param the type of the objects the iteration is about + */ +public class LazyFluentIterable implements FluentIterable { + + private final Iterable iterable; + + /** + * This constructor creates a new LazyFluentIterable. It wraps the + * given iterable. + * @param iterable the iterable this FluentIterable works on. + */ + protected LazyFluentIterable(Iterable iterable) { + this.iterable = iterable; + } + + /** + * This constructor can be used to implement anonymous subclasses + * of the LazyFluentIterable. + */ + protected LazyFluentIterable() { + iterable = this; + } + + /** + * Adds a filter operation to the operation chain and returns a new iterable. + * @param predicate the condition to test with for the filtering. If the test + * is negative, the tested object is removed by the iterator. + * @return a new FluentIterable object that decorates the source iterable + */ + @Override + public FluentIterable filter(Predicate predicate) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + @Override + public TYPE computeNext() { + while(true) { + if(fromIterator.hasNext()) { + TYPE candidate = fromIterator.next(); + if(!predicate.test(candidate)) { + continue; + } + return candidate; + } + + return null; + } + } + }; + } + }; + } + + /** + * Uses the Iterable interface's forEach method to apply a given function + * for each object of the iterator. Is a terminating operation. + * @param action the action for each object + */ + @Override + public void forEachDo(Consumer action) { + Iterator newIterator = iterable.iterator(); + while(newIterator.hasNext()) { + action.accept(newIterator.next()); + } + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @return an option of the first object of the iteration + */ + @Override + public Optional first() { + Optional result = Optional.empty(); + List list = first(1).asList(); + if(!list.isEmpty()) { + result = Optional.of(list.get(0)); + } + + return result; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. + */ + @Override + public FluentIterable first(int count) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + int currentIndex = 0; + + @Override + public TYPE computeNext() { + if(currentIndex < count) { + if(fromIterator.hasNext()) { + TYPE candidate = fromIterator.next(); + currentIndex++; + return candidate; + } + } + return null; + } + }; + } + }; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @return an option of the last object of the iteration + */ + @Override + public Optional last() { + Optional result = Optional.empty(); + List list = last(1).asList(); + if(!list.isEmpty()) { + result = Optional.of(list.get(0)); + } + + return result; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects + */ + @Override + public FluentIterable last(int count) {return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + int currentIndex = 0; + + @Override + public TYPE computeNext() { + List list = new ArrayList<>(); + + Iterator newIterator = iterable.iterator(); + while(newIterator.hasNext()) { + list.add(newIterator.next()); + } + + int totalElementsCount = list.size(); + int stopIndex = totalElementsCount - count; + + TYPE candidate = null; + while(currentIndex < stopIndex && fromIterator.hasNext()) { + currentIndex++; + fromIterator.next(); + } + if(currentIndex >= stopIndex && fromIterator.hasNext()) { + candidate = fromIterator.next(); + } + return candidate; + } + }; + } + }; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + @Override + public FluentIterable map(Function function) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(null) { + Iterator oldTypeIterator = iterable.iterator(); + @Override + public NEW_TYPE computeNext() { + while(true) { + if(oldTypeIterator.hasNext()) { + TYPE candidate = oldTypeIterator.next(); + return function.apply(candidate); + } + return null; + } + } + }; + } + }; + } + + /** + * Collects all remaining objects of this iteration into a list. + * @return a list with all remaining objects of this iteration + */ + @Override + public List asList() { + List copy = FluentIterable.copyToList(iterable); + return copy; + } + + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + @Override + public TYPE computeNext() { + return fromIterator.next(); + } + }; + } + + /** + * @return a FluentIterable from a given iterable. Calls the LazyFluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new LazyFluentIterable<>(iterable); + } + +} diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java new file mode 100644 index 000000000..efaa87bbb --- /dev/null +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -0,0 +1,194 @@ +package com.iluwatar.fluentinterface.fluentiterable.simple; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * This is a simple implementation of the FluentIterable interface. It evaluates + * all chained operations eagerly. + * @param the type of the objects the iteration is about + */ +public class SimpleFluentIterable implements FluentIterable { + + private final Iterable iterable; + + /** + * This constructor creates a copy of a given iterable's contents. + * @param iterable the iterable this interface copies to work on. + */ + protected SimpleFluentIterable(Iterable iterable) { + List copy = FluentIterable.copyToList(iterable); + this.iterable = copy; + } + + /** + * Iterates over all elements of this iterator and filters them. + * @param predicate the condition to test with for the filtering. If the test + * is negative, the tested object is removed by the iterator. + * @return the same FluentIterable with a filtered collection + */ + @Override + public final FluentIterable filter(Predicate predicate) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + TYPE nextElement = iterator.next(); + if(!predicate.test(nextElement)) { + iterator.remove(); + } + } + return this; + } + + /** + * Uses the Iterable interface's forEach method to apply a given function + * for each object of the iterator. Is a terminating operation. + * @param action the action for each object + */ + @Override + public void forEachDo(Consumer action) { + iterable.forEach(action); + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @return an option of the first object of the iteration + */ + @Override + public final Optional first() { + List list = first(1).asList(); + if(list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. + */ + @Override + public final FluentIterable first(int count) { + Iterator iterator = iterator(); + int currentCount = 0; + while (iterator.hasNext()) { + iterator.next(); + if(currentCount >= count) { + iterator.remove(); + } + currentCount++; + } + return this; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @return an option of the last object of the iteration + */ + @Override + public final Optional last() { + List list = last(1).asList(); + if(list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects + */ + @Override + public final FluentIterable last(int count) { + int remainingElementsCount = getRemainingElementsCount(); + Iterator iterator = iterator(); + int currentIndex = 0; + while (iterator.hasNext()) { + iterator.next(); + if(currentIndex < remainingElementsCount - count) { + iterator.remove(); + } + currentIndex++; + } + + return this; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + @Override + public final FluentIterable map(Function function) { + List temporaryList = new ArrayList(); + Iterator iterator = iterator(); + while (iterator.hasNext()) { + temporaryList.add(function.apply(iterator.next())); + } + return from(temporaryList); + } + + /** + * Collects all remaining objects of this iteration into a list. + * @return a list with all remaining objects of this iteration + */ + @Override + public List asList() { + return toList(iterable.iterator()); + } + + /** + * @return a FluentIterable from a given iterable. Calls the SimpleFluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new SimpleFluentIterable<>(iterable); + } + + @Override + public Iterator iterator() { + return iterable.iterator(); + } + + @Override + public void forEach(Consumer action) { + iterable.forEach(action); + } + + + @Override + public Spliterator spliterator() { + return iterable.spliterator(); + } + + /** + * @return the count of remaining objects in the current iteration + */ + public final int getRemainingElementsCount() { + int counter = 0; + Iterator iterator = iterator(); + while(iterator.hasNext()) { + iterator.next(); + counter++; + } + return counter; + } + + /** + * Collects the remaining objects of the given iterators iteration into an List. + * @return a new List with the remaining objects. + */ + public static List toList(Iterator iterator) { + List copy = new ArrayList<>(); + while (iterator.hasNext()) { + copy.add(iterator.next()); + } + return copy; + } +} From a90fcc23916c8768087135e68d8499053b41e860 Mon Sep 17 00:00:00 2001 From: Hannes Pernpeintner Date: Wed, 2 Sep 2015 17:21:20 +0200 Subject: [PATCH 14/80] #184 Fluent interface pattern, documentation changed, collecting operations optimized --- .../fluentiterable/FluentIterable.java | 17 +++-- .../lazy/DecoratingIterator.java | 24 ++++--- .../lazy/LazyFluentIterable.java | 69 ++++++------------- .../simple/SimpleFluentIterable.java | 39 ++++------- 4 files changed, 56 insertions(+), 93 deletions(-) diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java index 7bdaf274c..919cf5664 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Optional; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -18,7 +17,7 @@ import java.util.function.Predicate; public interface FluentIterable extends Iterable { /** - * Filters the iteration with the given predicate. + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy the predicate. * @param predicate the condition to test with for the filtering. If the test * is negative, the tested object is removed by the iterator. * @return a filtered FluentIterable @@ -26,13 +25,8 @@ public interface FluentIterable extends Iterable { FluentIterable filter(Predicate predicate); /** - * Uses the Iterable interface's forEach method to apply a given function - * for each object of the iterator. This is a terminating operation. - */ - void forEachDo(Consumer action); - - /** - * Evaluates the iteration and returns the first element. This is a terminating operation. + * Returns an Optional containing the first element of this iterable if present, + * else returns Optional.empty(). * @return the first element after the iteration is evaluated */ Optional first(); @@ -62,6 +56,11 @@ public interface FluentIterable extends Iterable { * @return a new FluentIterable of the new type */ FluentIterable map(Function function); + + /** + * Returns the contents of this Iterable as a List. + * @return a List representation of this Iterable + */ List asList(); /** diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java index 0e5b410cc..35c4cc0ae 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java @@ -22,7 +22,7 @@ public abstract class DecoratingIterator implements Iterator { } /** - * Precomputes and caches the next element of the iteration. + * Precomputes and saves the next element of the Iterable. null is considered as end of data. * @return true if a next element is available */ @Override @@ -32,22 +32,24 @@ public abstract class DecoratingIterator implements Iterator { } /** - * Returns the next element of the iteration. This implementation caches it. - * If no next element is cached, it is computed. - * @return the next element obf the iteration + * Returns the next element of the Iterable. + * @return the next element of the Iterable, or null if not present. */ @Override public final TYPE next() { - TYPE result = next; - next = null; - result = (result == null ? fromIterator.next() : result); - return result; + if (next == null) { + return fromIterator.next(); + } else { + final TYPE result = next; + next = null; + return result; + } } /** - * Computes the next object of the iteration. Can be implemented to - * realize custom behaviour for an iteration process. - * @return + * Computes the next object of the Iterable. Can be implemented to + * realize custom behaviour for an iteration process. null is considered as end of data. + * @return the next element of the Iterable. */ public abstract TYPE computeNext(); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java index c6db4d2cd..27bca1bf6 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -37,7 +37,7 @@ public class LazyFluentIterable implements FluentIterable { } /** - * Adds a filter operation to the operation chain and returns a new iterable. + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy the predicate. * @param predicate the condition to test with for the filtering. If the test * is negative, the tested object is removed by the iterator. * @return a new FluentIterable object that decorates the source iterable @@ -50,53 +50,33 @@ public class LazyFluentIterable implements FluentIterable { return new DecoratingIterator(iterable.iterator()) { @Override public TYPE computeNext() { - while(true) { - if(fromIterator.hasNext()) { - TYPE candidate = fromIterator.next(); - if(!predicate.test(candidate)) { - continue; - } - return candidate; + while(fromIterator.hasNext()) { + TYPE candidate = fromIterator.next(); + if(!predicate.test(candidate)) { + continue; } - - return null; + return candidate; } + + return null; } }; } }; } - /** - * Uses the Iterable interface's forEach method to apply a given function - * for each object of the iterator. Is a terminating operation. - * @param action the action for each object - */ - @Override - public void forEachDo(Consumer action) { - Iterator newIterator = iterable.iterator(); - while(newIterator.hasNext()) { - action.accept(newIterator.next()); - } - } - /** * Can be used to collect objects from the iteration. Is a terminating operation. - * @return an option of the first object of the iteration + * @return an Optional containing the first object of this Iterable */ @Override public Optional first() { - Optional result = Optional.empty(); - List list = first(1).asList(); - if(!list.isEmpty()) { - result = Optional.of(list.get(0)); - } - - return result; + Iterator resultIterator = first(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); } /** - * Can be used to collect objects from the iteration. Is a terminating operation. + * Can be used to collect objects from the iteration. * @param count defines the number of objects to return * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. */ @@ -126,21 +106,18 @@ public class LazyFluentIterable implements FluentIterable { /** * Can be used to collect objects from the iteration. Is a terminating operation. - * @return an option of the last object of the iteration + * @return an Optional containing the last object of this Iterable */ @Override public Optional last() { - Optional result = Optional.empty(); - List list = last(1).asList(); - if(!list.isEmpty()) { - result = Optional.of(list.get(0)); - } - - return result; + Iterator resultIterator = last(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); } /** - * Can be used to collect objects from the iteration. Is a terminating operation. + * Can be used to collect objects from the Iterable. Is a terminating operation. + * This operation is memory intensive, because the contents of this Iterable + * are collected into a List, when the next object is requested. * @param count defines the number of objects to return * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects */ @@ -193,13 +170,11 @@ public class LazyFluentIterable implements FluentIterable { Iterator oldTypeIterator = iterable.iterator(); @Override public NEW_TYPE computeNext() { - while(true) { - if(oldTypeIterator.hasNext()) { - TYPE candidate = oldTypeIterator.next(); - return function.apply(candidate); - } - return null; + while(oldTypeIterator.hasNext()) { + TYPE candidate = oldTypeIterator.next(); + return function.apply(candidate); } + return null; } }; } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java index efaa87bbb..a4bf77218 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -26,7 +26,7 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Iterates over all elements of this iterator and filters them. + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy the predicate. * @param predicate the condition to test with for the filtering. If the test * is negative, the tested object is removed by the iterator. * @return the same FluentIterable with a filtered collection @@ -44,30 +44,17 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Uses the Iterable interface's forEach method to apply a given function - * for each object of the iterator. Is a terminating operation. - * @param action the action for each object - */ - @Override - public void forEachDo(Consumer action) { - iterable.forEach(action); - } - - /** - * Can be used to collect objects from the iteration. Is a terminating operation. - * @return an option of the first object of the iteration + * Can be used to collect objects from the Iterable. Is a terminating operation. + * @return an option of the first object of the Iterable */ @Override public final Optional first() { - List list = first(1).asList(); - if(list.isEmpty()) { - return Optional.empty(); - } - return Optional.of(list.get(0)); + Iterator resultIterator = first(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); } /** - * Can be used to collect objects from the iteration. Is a terminating operation. + * Can be used to collect objects from the Iterable. Is a terminating operation. * @param count defines the number of objects to return * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. */ @@ -86,8 +73,8 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Can be used to collect objects from the iteration. Is a terminating operation. - * @return an option of the last object of the iteration + * Can be used to collect objects from the Iterable. Is a terminating operation. + * @return an option of the last object of the Iterable */ @Override public final Optional last() { @@ -99,7 +86,7 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Can be used to collect objects from the iteration. Is a terminating operation. + * Can be used to collect objects from the Iterable. Is a terminating operation. * @param count defines the number of objects to return * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects */ @@ -136,8 +123,8 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Collects all remaining objects of this iteration into a list. - * @return a list with all remaining objects of this iteration + * Collects all remaining objects of this Iterable into a list. + * @return a list with all remaining objects of this Iterable */ @Override public List asList() { @@ -168,7 +155,7 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * @return the count of remaining objects in the current iteration + * @return the count of remaining objects of the current Iterable */ public final int getRemainingElementsCount() { int counter = 0; @@ -181,7 +168,7 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Collects the remaining objects of the given iterators iteration into an List. + * Collects the remaining objects of the given iterator into a List. * @return a new List with the remaining objects. */ public static List toList(Iterator iterator) { From ee47ae021aed75b1c467e31f75729224c02f943b Mon Sep 17 00:00:00 2001 From: Hannes Pernpeintner Date: Thu, 3 Sep 2015 18:49:52 +0200 Subject: [PATCH 15/80] #184 Fluent interface pattern, added cached initialization to anonymous iterator for lazy fluentiterable, small documentation changes --- .../lazy/DecoratingIterator.java | 2 +- .../lazy/LazyFluentIterable.java | 28 ++++++++++++------- .../simple/SimpleFluentIterable.java | 1 + 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java index 35c4cc0ae..3c1230bce 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java @@ -4,7 +4,7 @@ import java.util.Iterator; /** * This class is used to realize LazyFluentIterables. It decorates - * a given iterator. + * a given iterator. Does not support consecutive hasNext() calls. * @param */ public abstract class DecoratingIterator implements Iterator { diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java index 27bca1bf6..998bbd659 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -126,19 +126,14 @@ public class LazyFluentIterable implements FluentIterable { @Override public Iterator iterator() { return new DecoratingIterator(iterable.iterator()) { - int currentIndex = 0; + public int stopIndex; + public int totalElementsCount; + private List list; + private int currentIndex = 0; @Override public TYPE computeNext() { - List list = new ArrayList<>(); - - Iterator newIterator = iterable.iterator(); - while(newIterator.hasNext()) { - list.add(newIterator.next()); - } - - int totalElementsCount = list.size(); - int stopIndex = totalElementsCount - count; + initialize(); TYPE candidate = null; while(currentIndex < stopIndex && fromIterator.hasNext()) { @@ -150,6 +145,19 @@ public class LazyFluentIterable implements FluentIterable { } return candidate; } + + private void initialize() { + if(list == null) { + list = new ArrayList<>(); + Iterator newIterator = iterable.iterator(); + while(newIterator.hasNext()) { + list.add(newIterator.next()); + } + + totalElementsCount = list.size(); + stopIndex = totalElementsCount - count; + } + } }; } }; diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java index a4bf77218..0736387e5 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -10,6 +10,7 @@ import java.util.function.Predicate; /** * This is a simple implementation of the FluentIterable interface. It evaluates * all chained operations eagerly. + * This implementation would be costly to be utilized in real applications. * @param the type of the objects the iteration is about */ public class SimpleFluentIterable implements FluentIterable { From 7a1c2ee41310b5974589428d975401380e085322 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Tue, 8 Sep 2015 12:49:54 +0530 Subject: [PATCH 16/80] Work on #208, added getting started section in README and linked it to programming priciples site --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a4ec970c9..38936193c 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ Reusing design patterns helps to prevent subtle issues that can cause major problems, and it also improves code readability for coders and architects who are familiar with the patterns. +# Getting started + +Before you dive into the material, you should be familiar with various +[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). + # How to contribute If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). From fb13ddc5d6ca34975c5c0bf474987e6b1c19c34a Mon Sep 17 00:00:00 2001 From: Hannes Pernpeintner Date: Mon, 7 Sep 2015 13:25:26 +0200 Subject: [PATCH 17/80] #184 Fluent interface pattern, added uml, adjusted style, added pattern description --- faq.md | 4 + fluentinterface/etc/fluentinterface.png | Bin 0 -> 59199 bytes fluentinterface/etc/fluentinterface.ucls | 100 +++++ fluentinterface/index.md | 28 ++ fluentinterface/pom.xml | 2 +- .../com/iluwatar/fluentinterface/App.java | 174 ++++---- .../fluentiterable/FluentIterable.java | 123 +++--- .../lazy/DecoratingIterator.java | 87 ++-- .../lazy/LazyFluentIterable.java | 400 +++++++++--------- .../simple/SimpleFluentIterable.java | 334 ++++++++------- .../com/iluwatar/fluentinterface/AppTest.java | 10 +- 11 files changed, 727 insertions(+), 535 deletions(-) create mode 100644 fluentinterface/etc/fluentinterface.png create mode 100644 fluentinterface/etc/fluentinterface.ucls create mode 100644 fluentinterface/index.md diff --git a/faq.md b/faq.md index 5633a693d..b98bc7589 100644 --- a/faq.md +++ b/faq.md @@ -61,3 +61,7 @@ As for performance and scalability, pools can become bottlenecks, if all the pooled objects are in use and more clients need them, threads will become blocked waiting for available object from the pool. This is not the case with Flyweight. + +### Q7: What are the differences between FluentInterface and Builder patterns? {#Q7} + +Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change. \ No newline at end of file diff --git a/fluentinterface/etc/fluentinterface.png b/fluentinterface/etc/fluentinterface.png new file mode 100644 index 0000000000000000000000000000000000000000..4a79ce0b7f69395cb81822042ae9c85f90da5e17 GIT binary patch literal 59199 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 literal 0 HcmV?d00001 diff --git a/fluentinterface/etc/fluentinterface.ucls b/fluentinterface/etc/fluentinterface.ucls new file mode 100644 index 000000000..3277148cd --- /dev/null +++ b/fluentinterface/etc/fluentinterface.ucls @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fluentinterface/index.md b/fluentinterface/index.md new file mode 100644 index 000000000..942867933 --- /dev/null +++ b/fluentinterface/index.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Fluent Interface +folder: fluentinterface +permalink: /patterns/fluentinterface/ +categories: Architectural +tags: Java +--- + +**Intent:** A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. + +![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") + +**Applicability:** Use the Fluent Interface pattern when + +* you provide an API that would benefit from a DSL-like usage +* you have objects that are difficult to configure or use + +**Real world examples:** + +* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html) +* [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained) +* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/) + +**Credits** + +* [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html) +* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/) \ No newline at end of file diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index c78c182e3..be8ab8039 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -5,7 +5,7 @@ java-design-patterns com.iluwatar - 1.5.0 + 1.6.0 4.0.0 diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java index 96a2db323..b9e5909f1 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java @@ -1,5 +1,6 @@ package com.iluwatar.fluentinterface; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable; import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable; @@ -9,96 +10,113 @@ import java.util.function.Predicate; import static java.lang.String.valueOf; +/** + * Fluent interface pattern is useful when you want to provide an easy readable, flowing API. Those + * interfaces tend to mimic domain specific languages, so they can nearly be read as human + * languages. + *

+ * In this example two implementations of a {@link FluentIterable} interface are given. The + * SimpleFluentIterable evaluates eagerly and would be too costly for real world applications. The + * LazyFluentIterable is evaluated on termination. Their usage is demonstrated with a simple number + * list that is filtered, transformed and collected. The result is printed afterwards. + *

+ */ public class App { - public static void main(String[] args) { + public static void main(String[] args) { - List integerList = new ArrayList() {{ - add(1); - add(-61); - add(14); - add(-22); - add(18); - add(-87); - add(6); - add(64); - add(-82); - add(26); - add(-98); - add(97); - add(45); - add(23); - add(2); - add(-68); - add(45); - }}; - prettyPrint("The initial list contains: ", integerList); + List integerList = new ArrayList() { + { + add(1); + add(-61); + add(14); + add(-22); + add(18); + add(-87); + add(6); + add(64); + add(-82); + add(26); + add(-98); + add(97); + add(45); + add(23); + add(2); + add(-68); + add(45); + } + }; + prettyPrint("The initial list contains: ", integerList); - List firstFiveNegatives = SimpleFluentIterable.from(integerList) - .filter(negatives()) - .first(3) - .asList(); - prettyPrint("The first three negative values are: ", firstFiveNegatives); + List firstFiveNegatives = + SimpleFluentIterable.fromCopyOf(integerList).filter(negatives()).first(3).asList(); + prettyPrint("The first three negative values are: ", firstFiveNegatives); - List lastTwoPositives = SimpleFluentIterable.from(integerList) - .filter(positives()) - .last(2) - .asList(); - prettyPrint("The last two positive values are: ", lastTwoPositives); + List lastTwoPositives = + SimpleFluentIterable.fromCopyOf(integerList).filter(positives()).last(2).asList(); + prettyPrint("The last two positive values are: ", lastTwoPositives); - SimpleFluentIterable.from(integerList) - .filter(number -> number%2 == 0) - .first() - .ifPresent(evenNumber -> System.out.println(String.format("The first even number is: %d", evenNumber))); + SimpleFluentIterable + .fromCopyOf(integerList) + .filter(number -> number % 2 == 0) + .first() + .ifPresent( + evenNumber -> System.out.println(String.format("The first even number is: %d", + evenNumber))); - List transformedList = SimpleFluentIterable.from(integerList) - .filter(negatives()) - .map(transformToString()) - .asList(); - prettyPrint("A string-mapped list of negative numbers contains: ", transformedList); + List transformedList = + SimpleFluentIterable.fromCopyOf(integerList).filter(negatives()).map(transformToString()) + .asList(); + prettyPrint("A string-mapped list of negative numbers contains: ", transformedList); - List lastTwoOfFirstFourStringMapped = LazyFluentIterable.from(integerList) - .filter(positives()) - .first(4) - .last(2) - .map(number -> "String[" + String.valueOf(number) + "]") - .asList(); - prettyPrint("The lazy list contains the last two of the first four positive numbers mapped to Strings: ", lastTwoOfFirstFourStringMapped); + List lastTwoOfFirstFourStringMapped = + LazyFluentIterable.from(integerList).filter(positives()).first(4).last(2) + .map(number -> "String[" + String.valueOf(number) + "]").asList(); + prettyPrint( + "The lazy list contains the last two of the first four positive numbers mapped to Strings: ", + lastTwoOfFirstFourStringMapped); - LazyFluentIterable.from(integerList) - .filter(negatives()) - .first(2) - .last() - .ifPresent(lastOfFirstTwo -> System.out.println(String.format("The last of the first two negatives is: %d", lastOfFirstTwo))); + LazyFluentIterable + .from(integerList) + .filter(negatives()) + .first(2) + .last() + .ifPresent( + lastOfFirstTwo -> System.out.println(String.format( + "The last of the first two negatives is: %d", lastOfFirstTwo))); + } + + private static Function transformToString() { + return integer -> "String[" + valueOf(integer) + "]"; + } + + private static Predicate negatives() { + return integer -> (integer < 0); + } + + private static Predicate positives() { + return integer -> (integer > 0); + } + + private static void prettyPrint(String prefix, Iterable iterable) { + prettyPrint(", ", prefix, ".", iterable); + } + + private static void prettyPrint(String prefix, String suffix, Iterable iterable) { + prettyPrint(", ", prefix, suffix, iterable); + } + + private static void prettyPrint(String delimiter, String prefix, String suffix, + Iterable iterable) { + StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + joiner.add(iterator.next().toString()); } - private static Function transformToString() { - return integer -> "String[" + valueOf(integer) + "]"; - } - private static Predicate negatives() { - return integer -> (integer < 0); - } - private static Predicate positives() { - return integer -> (integer > 0); - } - - private static void prettyPrint(String prefix, Iterable iterable) { - prettyPrint(", ", prefix, ".", iterable); - } - private static void prettyPrint(String prefix, String suffix, Iterable iterable) { - prettyPrint(", ", prefix, suffix, iterable); - } - - private static void prettyPrint(String delimiter, String prefix, String suffix, Iterable iterable) { - StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); - Iterator iterator = iterable.iterator(); - while (iterator.hasNext()) { - joiner.add(iterator.next().toString()); - } - - System.out.println(joiner); - } + System.out.println(joiner); + } } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java index 919cf5664..5c4df0391 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -8,73 +8,82 @@ import java.util.function.Function; import java.util.function.Predicate; /** - * The FluentIterable is a more convenient implementation of the common iterable interface based - * on the fluent interface design pattern. - * This interface defines common operations, but - * doesn't aim to be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. + * The FluentIterable is a more convenient implementation of the common iterable interface based on + * the fluent interface design pattern. This interface defines common operations, but doesn't aim to + * be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. + * * @param is the class of objects the iterable contains */ public interface FluentIterable extends Iterable { - /** - * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy the predicate. - * @param predicate the condition to test with for the filtering. If the test - * is negative, the tested object is removed by the iterator. - * @return a filtered FluentIterable - */ - FluentIterable filter(Predicate predicate); + /** + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy + * the predicate. + * + * @param predicate the condition to test with for the filtering. If the test is negative, the + * tested object is removed by the iterator. + * @return a filtered FluentIterable + */ + FluentIterable filter(Predicate predicate); - /** - * Returns an Optional containing the first element of this iterable if present, - * else returns Optional.empty(). - * @return the first element after the iteration is evaluated - */ - Optional first(); + /** + * Returns an Optional containing the first element of this iterable if present, else returns + * Optional.empty(). + * + * @return the first element after the iteration is evaluated + */ + Optional first(); - /** - * Evaluates the iteration and leaves only the count first elements. - * @return the first count elements as an Iterable - */ - FluentIterable first(int count); + /** + * Evaluates the iteration and leaves only the count first elements. + * + * @return the first count elements as an Iterable + */ + FluentIterable first(int count); - /** - * Evaluates the iteration and returns the last element. This is a terminating operation. - * @return the last element after the iteration is evaluated - */ - Optional last(); + /** + * Evaluates the iteration and returns the last element. This is a terminating operation. + * + * @return the last element after the iteration is evaluated + */ + Optional last(); - /** - * Evaluates the iteration and leaves only the count last elements. - * @return the last counts elements as an Iterable - */ - FluentIterable last(int count); + /** + * Evaluates the iteration and leaves only the count last elements. + * + * @return the last counts elements as an Iterable + */ + FluentIterable last(int count); - /** - * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. - * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE - * @param the target type of the transformation - * @return a new FluentIterable of the new type - */ - FluentIterable map(Function function); + /** + * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + FluentIterable map(Function function); - /** - * Returns the contents of this Iterable as a List. - * @return a List representation of this Iterable - */ - List asList(); + /** + * Returns the contents of this Iterable as a List. + * + * @return a List representation of this Iterable + */ + List asList(); - /** - * Utility method that iterates over iterable and adds the contents to a list. - * @param iterable the iterable to collect - * @param the type of the objects to iterate - * @return a list with all objects of the given iterator - */ - static List copyToList(Iterable iterable) { - ArrayList copy = new ArrayList<>(); - Iterator iterator = iterable.iterator(); - while (iterator.hasNext()) { - copy.add(iterator.next()); - } - return copy; + /** + * Utility method that iterates over iterable and adds the contents to a list. + * + * @param iterable the iterable to collect + * @param the type of the objects to iterate + * @return a list with all objects of the given iterator + */ + static List copyToList(Iterable iterable) { + ArrayList copy = new ArrayList<>(); + Iterator iterator = iterable.iterator(); + while (iterator.hasNext()) { + copy.add(iterator.next()); } + return copy; + } } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java index 3c1230bce..e80356d8e 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java @@ -3,53 +3,58 @@ package com.iluwatar.fluentinterface.fluentiterable.lazy; import java.util.Iterator; /** - * This class is used to realize LazyFluentIterables. It decorates - * a given iterator. Does not support consecutive hasNext() calls. + * This class is used to realize LazyFluentIterables. It decorates a given iterator. Does not + * support consecutive hasNext() calls. + * * @param */ public abstract class DecoratingIterator implements Iterator { - protected final Iterator fromIterator; + protected final Iterator fromIterator; - private TYPE next = null; + private TYPE next = null; - /** - * Creates an iterator that decorates the given iterator. - * @param fromIterator - */ - public DecoratingIterator(Iterator fromIterator) { - this.fromIterator = fromIterator; + /** + * Creates an iterator that decorates the given iterator. + * + * @param fromIterator + */ + public DecoratingIterator(Iterator fromIterator) { + this.fromIterator = fromIterator; + } + + /** + * Precomputes and saves the next element of the Iterable. null is considered as end of data. + * + * @return true if a next element is available + */ + @Override + public final boolean hasNext() { + next = computeNext(); + return next != null; + } + + /** + * Returns the next element of the Iterable. + * + * @return the next element of the Iterable, or null if not present. + */ + @Override + public final TYPE next() { + if (next == null) { + return fromIterator.next(); + } else { + final TYPE result = next; + next = null; + return result; } + } - /** - * Precomputes and saves the next element of the Iterable. null is considered as end of data. - * @return true if a next element is available - */ - @Override - public final boolean hasNext() { - next = computeNext(); - return next != null; - } - - /** - * Returns the next element of the Iterable. - * @return the next element of the Iterable, or null if not present. - */ - @Override - public final TYPE next() { - if (next == null) { - return fromIterator.next(); - } else { - final TYPE result = next; - next = null; - return result; - } - } - - /** - * Computes the next object of the Iterable. Can be implemented to - * realize custom behaviour for an iteration process. null is considered as end of data. - * @return the next element of the Iterable. - */ - public abstract TYPE computeNext(); + /** + * Computes the next object of the Iterable. Can be implemented to realize custom behaviour for an + * iteration process. null is considered as end of data. + * + * @return the next element of the Iterable. + */ + public abstract TYPE computeNext(); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java index 998bbd659..5adfa83ce 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -11,209 +11,221 @@ import java.util.function.Function; import java.util.function.Predicate; /** - * This is a lazy implementation of the FluentIterable interface. It evaluates - * all chained operations when a terminating operation is applied. + * This is a lazy implementation of the FluentIterable interface. It evaluates all chained + * operations when a terminating operation is applied. + * * @param the type of the objects the iteration is about */ public class LazyFluentIterable implements FluentIterable { - private final Iterable iterable; + private final Iterable iterable; - /** - * This constructor creates a new LazyFluentIterable. It wraps the - * given iterable. - * @param iterable the iterable this FluentIterable works on. - */ - protected LazyFluentIterable(Iterable iterable) { - this.iterable = iterable; - } + /** + * This constructor creates a new LazyFluentIterable. It wraps the given iterable. + * + * @param iterable the iterable this FluentIterable works on. + */ + protected LazyFluentIterable(Iterable iterable) { + this.iterable = iterable; + } - /** - * This constructor can be used to implement anonymous subclasses - * of the LazyFluentIterable. - */ - protected LazyFluentIterable() { - iterable = this; - } + /** + * This constructor can be used to implement anonymous subclasses of the LazyFluentIterable. + */ + protected LazyFluentIterable() { + iterable = this; + } - /** - * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy the predicate. - * @param predicate the condition to test with for the filtering. If the test - * is negative, the tested object is removed by the iterator. - * @return a new FluentIterable object that decorates the source iterable - */ - @Override - public FluentIterable filter(Predicate predicate) { - return new LazyFluentIterable() { - @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { - @Override - public TYPE computeNext() { - while(fromIterator.hasNext()) { - TYPE candidate = fromIterator.next(); - if(!predicate.test(candidate)) { - continue; - } - return candidate; - } - - return null; - } - }; - } - }; - } - - /** - * Can be used to collect objects from the iteration. Is a terminating operation. - * @return an Optional containing the first object of this Iterable - */ - @Override - public Optional first() { - Iterator resultIterator = first(1).iterator(); - return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); - } - - /** - * Can be used to collect objects from the iteration. - * @param count defines the number of objects to return - * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. - */ - @Override - public FluentIterable first(int count) { - return new LazyFluentIterable() { - @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { - int currentIndex = 0; - - @Override - public TYPE computeNext() { - if(currentIndex < count) { - if(fromIterator.hasNext()) { - TYPE candidate = fromIterator.next(); - currentIndex++; - return candidate; - } - } - return null; - } - }; - } - }; - } - - /** - * Can be used to collect objects from the iteration. Is a terminating operation. - * @return an Optional containing the last object of this Iterable - */ - @Override - public Optional last() { - Iterator resultIterator = last(1).iterator(); - return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); - } - - /** - * Can be used to collect objects from the Iterable. Is a terminating operation. - * This operation is memory intensive, because the contents of this Iterable - * are collected into a List, when the next object is requested. - * @param count defines the number of objects to return - * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects - */ - @Override - public FluentIterable last(int count) {return new LazyFluentIterable() { - @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { - public int stopIndex; - public int totalElementsCount; - private List list; - private int currentIndex = 0; - - @Override - public TYPE computeNext() { - initialize(); - - TYPE candidate = null; - while(currentIndex < stopIndex && fromIterator.hasNext()) { - currentIndex++; - fromIterator.next(); - } - if(currentIndex >= stopIndex && fromIterator.hasNext()) { - candidate = fromIterator.next(); - } - return candidate; - } - - private void initialize() { - if(list == null) { - list = new ArrayList<>(); - Iterator newIterator = iterable.iterator(); - while(newIterator.hasNext()) { - list.add(newIterator.next()); - } - - totalElementsCount = list.size(); - stopIndex = totalElementsCount - count; - } - } - }; - } - }; - } - - /** - * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. - * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE - * @param the target type of the transformation - * @return a new FluentIterable of the new type - */ - @Override - public FluentIterable map(Function function) { - return new LazyFluentIterable() { - @Override - public Iterator iterator() { - return new DecoratingIterator(null) { - Iterator oldTypeIterator = iterable.iterator(); - @Override - public NEW_TYPE computeNext() { - while(oldTypeIterator.hasNext()) { - TYPE candidate = oldTypeIterator.next(); - return function.apply(candidate); - } - return null; - } - }; - } - }; - } - - /** - * Collects all remaining objects of this iteration into a list. - * @return a list with all remaining objects of this iteration - */ - @Override - public List asList() { - List copy = FluentIterable.copyToList(iterable); - return copy; - } - - @Override - public Iterator iterator() { + /** + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy + * the predicate. + * + * @param predicate the condition to test with for the filtering. If the test is negative, the + * tested object is removed by the iterator. + * @return a new FluentIterable object that decorates the source iterable + */ + @Override + public FluentIterable filter(Predicate predicate) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { return new DecoratingIterator(iterable.iterator()) { - @Override - public TYPE computeNext() { - return fromIterator.next(); + @Override + public TYPE computeNext() { + while (fromIterator.hasNext()) { + TYPE candidate = fromIterator.next(); + if (!predicate.test(candidate)) { + continue; + } + return candidate; } - }; - } - /** - * @return a FluentIterable from a given iterable. Calls the LazyFluentIterable constructor. - */ - public static final FluentIterable from(Iterable iterable) { - return new LazyFluentIterable<>(iterable); - } + return null; + } + }; + } + }; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * + * @return an Optional containing the first object of this Iterable + */ + @Override + public Optional first() { + Iterator resultIterator = first(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + } + + /** + * Can be used to collect objects from the iteration. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first + * objects. + */ + @Override + public FluentIterable first(int count) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + int currentIndex = 0; + + @Override + public TYPE computeNext() { + if (currentIndex < count) { + if (fromIterator.hasNext()) { + TYPE candidate = fromIterator.next(); + currentIndex++; + return candidate; + } + } + return null; + } + }; + } + }; + } + + /** + * Can be used to collect objects from the iteration. Is a terminating operation. + * + * @return an Optional containing the last object of this Iterable + */ + @Override + public Optional last() { + Iterator resultIterator = last(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. This operation is + * memory intensive, because the contents of this Iterable are collected into a List, when the + * next object is requested. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last + * objects + */ + @Override + public FluentIterable last(int count) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + private int stopIndex; + private int totalElementsCount; + private List list; + private int currentIndex = 0; + + @Override + public TYPE computeNext() { + initialize(); + + TYPE candidate = null; + while (currentIndex < stopIndex && fromIterator.hasNext()) { + currentIndex++; + fromIterator.next(); + } + if (currentIndex >= stopIndex && fromIterator.hasNext()) { + candidate = fromIterator.next(); + } + return candidate; + } + + private void initialize() { + if (list == null) { + list = new ArrayList<>(); + Iterator newIterator = iterable.iterator(); + while (newIterator.hasNext()) { + list.add(newIterator.next()); + } + + totalElementsCount = list.size(); + stopIndex = totalElementsCount - count; + } + } + }; + } + }; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + @Override + public FluentIterable map(Function function) { + return new LazyFluentIterable() { + @Override + public Iterator iterator() { + return new DecoratingIterator(null) { + Iterator oldTypeIterator = iterable.iterator(); + + @Override + public NEW_TYPE computeNext() { + while (oldTypeIterator.hasNext()) { + TYPE candidate = oldTypeIterator.next(); + return function.apply(candidate); + } + return null; + } + }; + } + }; + } + + /** + * Collects all remaining objects of this iteration into a list. + * + * @return a list with all remaining objects of this iteration + */ + @Override + public List asList() { + List copy = FluentIterable.copyToList(iterable); + return copy; + } + + @Override + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + @Override + public TYPE computeNext() { + return fromIterator.next(); + } + }; + } + + /** + * @return a FluentIterable from a given iterable. Calls the LazyFluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new LazyFluentIterable<>(iterable); + } } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java index 0736387e5..db7a31954 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -8,175 +8,191 @@ import java.util.function.Function; import java.util.function.Predicate; /** - * This is a simple implementation of the FluentIterable interface. It evaluates - * all chained operations eagerly. - * This implementation would be costly to be utilized in real applications. + * This is a simple implementation of the FluentIterable interface. It evaluates all chained + * operations eagerly. This implementation would be costly to be utilized in real applications. + * * @param the type of the objects the iteration is about */ public class SimpleFluentIterable implements FluentIterable { - private final Iterable iterable; + private final Iterable iterable; - /** - * This constructor creates a copy of a given iterable's contents. - * @param iterable the iterable this interface copies to work on. - */ - protected SimpleFluentIterable(Iterable iterable) { - List copy = FluentIterable.copyToList(iterable); - this.iterable = copy; + /** + * This constructor creates a copy of a given iterable's contents. + * + * @param iterable the iterable this interface copies to work on. + */ + protected SimpleFluentIterable(Iterable iterable) { + this.iterable = iterable; + } + + /** + * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy + * the predicate. + * + * @param predicate the condition to test with for the filtering. If the test is negative, the + * tested object is removed by the iterator. + * @return the same FluentIterable with a filtered collection + */ + @Override + public final FluentIterable filter(Predicate predicate) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + TYPE nextElement = iterator.next(); + if (!predicate.test(nextElement)) { + iterator.remove(); + } + } + return this; + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @return an option of the first object of the Iterable + */ + @Override + public final Optional first() { + Iterator resultIterator = first(1).iterator(); + return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' first + * objects. + */ + @Override + public final FluentIterable first(int count) { + Iterator iterator = iterator(); + int currentCount = 0; + while (iterator.hasNext()) { + iterator.next(); + if (currentCount >= count) { + iterator.remove(); + } + currentCount++; + } + return this; + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @return an option of the last object of the Iterable + */ + @Override + public final Optional last() { + List list = last(1).asList(); + if (list.isEmpty()) { + return Optional.empty(); + } + return Optional.of(list.get(0)); + } + + /** + * Can be used to collect objects from the Iterable. Is a terminating operation. + * + * @param count defines the number of objects to return + * @return the same FluentIterable with a collection decimated to a maximum of 'count' last + * objects + */ + @Override + public final FluentIterable last(int count) { + int remainingElementsCount = getRemainingElementsCount(); + Iterator iterator = iterator(); + int currentIndex = 0; + while (iterator.hasNext()) { + iterator.next(); + if (currentIndex < remainingElementsCount - count) { + iterator.remove(); + } + currentIndex++; } - /** - * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy the predicate. - * @param predicate the condition to test with for the filtering. If the test - * is negative, the tested object is removed by the iterator. - * @return the same FluentIterable with a filtered collection - */ - @Override - public final FluentIterable filter(Predicate predicate) { - Iterator iterator = iterator(); - while (iterator.hasNext()) { - TYPE nextElement = iterator.next(); - if(!predicate.test(nextElement)) { - iterator.remove(); - } - } - return this; + return this; + } + + /** + * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * + * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE + * @param the target type of the transformation + * @return a new FluentIterable of the new type + */ + @Override + public final FluentIterable map(Function function) { + List temporaryList = new ArrayList(); + Iterator iterator = iterator(); + while (iterator.hasNext()) { + temporaryList.add(function.apply(iterator.next())); } + return from(temporaryList); + } - /** - * Can be used to collect objects from the Iterable. Is a terminating operation. - * @return an option of the first object of the Iterable - */ - @Override - public final Optional first() { - Iterator resultIterator = first(1).iterator(); - return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); + /** + * Collects all remaining objects of this Iterable into a list. + * + * @return a list with all remaining objects of this Iterable + */ + @Override + public List asList() { + return toList(iterable.iterator()); + } + + /** + * @return a FluentIterable from a given iterable. Calls the SimpleFluentIterable constructor. + */ + public static final FluentIterable from(Iterable iterable) { + return new SimpleFluentIterable<>(iterable); + } + + public static final FluentIterable fromCopyOf(Iterable iterable) { + List copy = FluentIterable.copyToList(iterable); + return new SimpleFluentIterable<>(copy); + } + + @Override + public Iterator iterator() { + return iterable.iterator(); + } + + @Override + public void forEach(Consumer action) { + iterable.forEach(action); + } + + + @Override + public Spliterator spliterator() { + return iterable.spliterator(); + } + + /** + * @return the count of remaining objects of the current Iterable + */ + public final int getRemainingElementsCount() { + int counter = 0; + Iterator iterator = iterator(); + while (iterator.hasNext()) { + iterator.next(); + counter++; } + return counter; + } - /** - * Can be used to collect objects from the Iterable. Is a terminating operation. - * @param count defines the number of objects to return - * @return the same FluentIterable with a collection decimated to a maximum of 'count' first objects. - */ - @Override - public final FluentIterable first(int count) { - Iterator iterator = iterator(); - int currentCount = 0; - while (iterator.hasNext()) { - iterator.next(); - if(currentCount >= count) { - iterator.remove(); - } - currentCount++; - } - return this; - } - - /** - * Can be used to collect objects from the Iterable. Is a terminating operation. - * @return an option of the last object of the Iterable - */ - @Override - public final Optional last() { - List list = last(1).asList(); - if(list.isEmpty()) { - return Optional.empty(); - } - return Optional.of(list.get(0)); - } - - /** - * Can be used to collect objects from the Iterable. Is a terminating operation. - * @param count defines the number of objects to return - * @return the same FluentIterable with a collection decimated to a maximum of 'count' last objects - */ - @Override - public final FluentIterable last(int count) { - int remainingElementsCount = getRemainingElementsCount(); - Iterator iterator = iterator(); - int currentIndex = 0; - while (iterator.hasNext()) { - iterator.next(); - if(currentIndex < remainingElementsCount - count) { - iterator.remove(); - } - currentIndex++; - } - - return this; - } - - /** - * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. - * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE - * @param the target type of the transformation - * @return a new FluentIterable of the new type - */ - @Override - public final FluentIterable map(Function function) { - List temporaryList = new ArrayList(); - Iterator iterator = iterator(); - while (iterator.hasNext()) { - temporaryList.add(function.apply(iterator.next())); - } - return from(temporaryList); - } - - /** - * Collects all remaining objects of this Iterable into a list. - * @return a list with all remaining objects of this Iterable - */ - @Override - public List asList() { - return toList(iterable.iterator()); - } - - /** - * @return a FluentIterable from a given iterable. Calls the SimpleFluentIterable constructor. - */ - public static final FluentIterable from(Iterable iterable) { - return new SimpleFluentIterable<>(iterable); - } - - @Override - public Iterator iterator() { - return iterable.iterator(); - } - - @Override - public void forEach(Consumer action) { - iterable.forEach(action); - } - - - @Override - public Spliterator spliterator() { - return iterable.spliterator(); - } - - /** - * @return the count of remaining objects of the current Iterable - */ - public final int getRemainingElementsCount() { - int counter = 0; - Iterator iterator = iterator(); - while(iterator.hasNext()) { - iterator.next(); - counter++; - } - return counter; - } - - /** - * Collects the remaining objects of the given iterator into a List. - * @return a new List with the remaining objects. - */ - public static List toList(Iterator iterator) { - List copy = new ArrayList<>(); - while (iterator.hasNext()) { - copy.add(iterator.next()); - } - return copy; + /** + * Collects the remaining objects of the given iterator into a List. + * + * @return a new List with the remaining objects. + */ + public static List toList(Iterator iterator) { + List copy = new ArrayList<>(); + while (iterator.hasNext()) { + copy.add(iterator.next()); } + return copy; + } } diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java index 32bbca430..d0abb7bf1 100644 --- a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java @@ -4,9 +4,9 @@ import org.junit.Test; public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } From d1c653f74c7bbfc61b78120a505f43212925d557 Mon Sep 17 00:00:00 2001 From: zafarella Date: Thu, 13 Aug 2015 01:31:49 -0400 Subject: [PATCH 18/80] add draft --- singleton/presentation/index.html | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 singleton/presentation/index.html diff --git a/singleton/presentation/index.html b/singleton/presentation/index.html new file mode 100644 index 000000000..238bdb50f --- /dev/null +++ b/singleton/presentation/index.html @@ -0,0 +1,57 @@ + + + + + Circular Slides - impress.js + + + +

+
My Slides +
+
Slide 1
+
Slide 2
+
Slide 3
+
Slide 4
+ +
+
+ + + From 4ee25105b90366dd5a498256ad177590274796e9 Mon Sep 17 00:00:00 2001 From: zafarella Date: Wed, 9 Sep 2015 17:39:44 -0400 Subject: [PATCH 19/80] add checkstyle to check against google standard --- checkstyle.xml | 202 +++++++++++++++++++++++ pom.xml | 431 ++++++++++++++++++++++++++----------------------- 2 files changed, 430 insertions(+), 203 deletions(-) create mode 100644 checkstyle.xml diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..e79788f46 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 7b0d80bd8..68fd2254e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,211 +1,236 @@ - - 4.0.0 + + 4.0.0 - com.iluwatar - java-design-patterns - 1.6.0 - pom + com.iluwatar + java-design-patterns + 1.6.0 + pom - - UTF-8 - 5.0.0.Final - 1.8.2.RELEASE - 1.4.188 - 4.12 - 3.0 - 3.1.0 - 0.7.2.201409121644 - 1.4 - 2.15.3 - - - abstract-factory - builder - factory-method - prototype - singleton - adapter - bridge - composite - dao - decorator - facade - flyweight - proxy - chain - command - interpreter - iterator - mediator - memento - model-view-presenter - observer - state - strategy - template-method - visitor - double-checked-locking - servant - service-locator - null-object - event-aggregator - callback - execute-around - property - intercepting-filter - poison-pill - lazy-loading - service-layer - specification - tolerant-reader - model-view-controller - flux - double-dispatch - multiton - resource-acquisition-is-initialization - thread-pool - private-class-data - object-pool - dependency-injection - naked-objects - front-controller - repository - async-method-invocation - business-delegate - half-sync-half-async - step-builder - layers - message-channel - + + UTF-8 + 5.0.0.Final + 1.8.2.RELEASE + 1.4.188 + 4.12 + 3.0 + 3.1.0 + 0.7.2.201409121644 + 1.4 + 2.15.3 + + + abstract-factory + builder + factory-method + prototype + singleton + adapter + bridge + composite + dao + decorator + facade + flyweight + proxy + chain + command + interpreter + iterator + mediator + memento + model-view-presenter + observer + state + strategy + template-method + visitor + double-checked-locking + servant + service-locator + null-object + event-aggregator + callback + execute-around + property + intercepting-filter + poison-pill + lazy-loading + service-layer + specification + tolerant-reader + model-view-controller + flux + double-dispatch + multiton + resource-acquisition-is-initialization + thread-pool + private-class-data + object-pool + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + business-delegate + half-sync-half-async + step-builder + layers + message-channel + - - - - org.hibernate - hibernate-core - ${hibernate.version} - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.springframework.data - spring-data-jpa - ${spring-data.version} - - - com.h2database - h2 - ${h2.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.apache.camel - camel-core - ${camel.version} - - - org.apache.camel - camel-stream - ${camel.version} - - - junit - junit - ${junit.version} - test - - - + + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + com.h2database + h2 + ${h2.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + + + junit + junit + ${junit.version} + test + + + - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - - jacoco-maven-plugin - - - [0.6.2,) - - - prepare-agent - - - - - - - - - - - - + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + + jacoco-maven-plugin + + + [0.6.2,) + + + prepare-agent + + + + + + + + + + + + - - - - org.apache.maven.plugins - maven-compiler-plugin - ${compiler.version} - - 1.8 - 1.8 - - - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - - - - - prepare-agent - - prepare-agent - - - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.version} + + 1.8 + 1.8 + + + + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls.version} + + jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + + domainapp/dom/modules/simple/QSimpleObject.class + + + + + prepare-agent + + prepare-agent + + + + - \ No newline at end of file + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.15 + + + validate + + check + + validate + + checkstyle.xml + UTF-8 + true + false + + + + + + + + From e97c5a1f9550dc498b647010672290977dc9f7d7 Mon Sep 17 00:00:00 2001 From: zafarella Date: Wed, 9 Sep 2015 17:41:28 -0400 Subject: [PATCH 20/80] delete presentation --- singleton/presentation/index.html | 57 ------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 singleton/presentation/index.html diff --git a/singleton/presentation/index.html b/singleton/presentation/index.html deleted file mode 100644 index 238bdb50f..000000000 --- a/singleton/presentation/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - Circular Slides - impress.js - - - -
-
My Slides -
-
Slide 1
-
Slide 2
-
Slide 3
-
Slide 4
- -
-
- - - From 5aa3eb4652c8606b92c521452c07a43d47c4806a Mon Sep 17 00:00:00 2001 From: zafarella Date: Wed, 9 Sep 2015 17:43:21 -0400 Subject: [PATCH 21/80] reformat rules --- checkstyle.xml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/checkstyle.xml b/checkstyle.xml index e79788f46..0ff943d95 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -23,7 +23,7 @@ --> - + @@ -38,7 +38,8 @@ - + @@ -55,7 +56,8 @@ - + @@ -64,7 +66,8 @@ - + @@ -159,12 +162,14 @@ - + - + @@ -176,7 +181,8 @@ - + From f12de5cae4cc11b830914d4c333ad20df41edd3b Mon Sep 17 00:00:00 2001 From: zafarella Date: Wed, 9 Sep 2015 17:45:20 -0400 Subject: [PATCH 22/80] preserve tabs instead of space --- pom.xml | 450 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 225 insertions(+), 225 deletions(-) diff --git a/pom.xml b/pom.xml index 68fd2254e..10f37ccfe 100644 --- a/pom.xml +++ b/pom.xml @@ -1,236 +1,236 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - com.iluwatar - java-design-patterns - 1.6.0 - pom + com.iluwatar + java-design-patterns + 1.6.0 + pom - - UTF-8 - 5.0.0.Final - 1.8.2.RELEASE - 1.4.188 - 4.12 - 3.0 - 3.1.0 - 0.7.2.201409121644 - 1.4 - 2.15.3 - - - abstract-factory - builder - factory-method - prototype - singleton - adapter - bridge - composite - dao - decorator - facade - flyweight - proxy - chain - command - interpreter - iterator - mediator - memento - model-view-presenter - observer - state - strategy - template-method - visitor - double-checked-locking - servant - service-locator - null-object - event-aggregator - callback - execute-around - property - intercepting-filter - poison-pill - lazy-loading - service-layer - specification - tolerant-reader - model-view-controller - flux - double-dispatch - multiton - resource-acquisition-is-initialization - thread-pool - private-class-data - object-pool - dependency-injection - naked-objects - front-controller - repository - async-method-invocation - business-delegate - half-sync-half-async - step-builder - layers - message-channel - + + UTF-8 + 5.0.0.Final + 1.8.2.RELEASE + 1.4.188 + 4.12 + 3.0 + 3.1.0 + 0.7.2.201409121644 + 1.4 + 2.15.3 + + + abstract-factory + builder + factory-method + prototype + singleton + adapter + bridge + composite + dao + decorator + facade + flyweight + proxy + chain + command + interpreter + iterator + mediator + memento + model-view-presenter + observer + state + strategy + template-method + visitor + double-checked-locking + servant + service-locator + null-object + event-aggregator + callback + execute-around + property + intercepting-filter + poison-pill + lazy-loading + service-layer + specification + tolerant-reader + model-view-controller + flux + double-dispatch + multiton + resource-acquisition-is-initialization + thread-pool + private-class-data + object-pool + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + business-delegate + half-sync-half-async + step-builder + layers + message-channel + - - - - org.hibernate - hibernate-core - ${hibernate.version} - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.springframework.data - spring-data-jpa - ${spring-data.version} - - - com.h2database - h2 - ${h2.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.apache.camel - camel-core - ${camel.version} - - - org.apache.camel - camel-stream - ${camel.version} - - - junit - junit - ${junit.version} - test - - - + + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + com.h2database + h2 + ${h2.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + + + junit + junit + ${junit.version} + test + + + - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - - jacoco-maven-plugin - - - [0.6.2,) - - - prepare-agent - - - - - - - - - - - - + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + + jacoco-maven-plugin + + + [0.6.2,) + + + prepare-agent + + + + + + + + + + + + - - - - org.apache.maven.plugins - maven-compiler-plugin - ${compiler.version} - - 1.8 - 1.8 - - - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - - - - - prepare-agent - - prepare-agent - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.version} + + 1.8 + 1.8 + + + + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls.version} + + jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + + domainapp/dom/modules/simple/QSimpleObject.class + + + + + prepare-agent + + prepare-agent + + + + - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.15 - - - validate - - check - - validate - - checkstyle.xml - UTF-8 - true - false - - - - - - + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.15 + + + validate + + check + + validate + + checkstyle.xml + UTF-8 + true + false + + + + + + From 31537a238324987c7ca3dcf53df8fb33945e8d9c Mon Sep 17 00:00:00 2001 From: zafarella Date: Wed, 9 Sep 2015 19:05:34 -0400 Subject: [PATCH 23/80] suppress checkstyle warnings due to travis failure because of huge logs --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 10f37ccfe..b65a30712 100644 --- a/pom.xml +++ b/pom.xml @@ -224,7 +224,7 @@ checkstyle.xml UTF-8 - true + false false From efa7e01223e21a3d126906e8020e2f0be8dec6c1 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 10 Sep 2015 13:12:36 +0530 Subject: [PATCH 24/80] #184 polished a few rough edges, class diagram change, and removed warnings --- fluentinterface/etc/fluentinterface.png | Bin 59199 -> 83424 bytes fluentinterface/etc/fluentinterface.ucls | 198 +++++++++--------- .../com/iluwatar/fluentinterface/App.java | 31 +-- .../lazy/LazyFluentIterable.java | 5 +- .../simple/SimpleFluentIterable.java | 2 +- 5 files changed, 108 insertions(+), 128 deletions(-) diff --git a/fluentinterface/etc/fluentinterface.png b/fluentinterface/etc/fluentinterface.png index 4a79ce0b7f69395cb81822042ae9c85f90da5e17..611fec7c6e7daa4aab70f1d61c621822333eb7b5 100644 GIT binary patch literal 83424 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%0AXA48uE6Wo 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

7U;(81 zmhb8hZ5F<(zsxsvJ2UHV_+V->)mQS7=2CBnK0{^k2f9N8WM;7F#(e#ctVf#nuKuU) zm8AAtkyPg>5J3L7xF9%1t2sNZ`T`LVC;FmfZ9o>QT++qj4jIR0` zdl92a+GC}hP@KKNER|ZIS*m4Pfh(!>K58{(J}#d0rbBpc%>?EWwEA4nHlNbOkaU=| zT7CVKND280bnpYhw9}d39whJCM3qyaCdqiAF$*7H2$>Q+wx)gG^3;v213p!|o_#3c zzVY*OTTX}H<9=eJ`Elw!iL8c{#uL;d$YYoAD$43!JL(u`p*^zxn)i_RhO)w^U?FcW zEN1}_x&o+N5N^nL4vM*n3+KxN-ROxk(8hs*@dKnTMPF=G`vAfgq&OKKSj%n|k`3)w zFvmG+IPO}gNHsQAEe5_uCv|AM7-%nLPpwJRMd$#!=Z(hEDxYJgiDfY|9onKMTD z1F1AnHY%X%lDsJrm3RU@YUj7~6Z@5O?WiZp2Efn)S?eNg{6NvnUQjLhq}JKlQ<8a5 z3IX$*6NGrGA5Z&U*VDbSEVE5=QF0ns)aA2+V{i0F7P)&jd@;oXX%Zzb-N|cQ9PW{U zf}3*yHQhmP%7n|*{)nj)xCrJB*4e6|8wiuxtX$kck(BS*DWlzHnv#H@e7^zxun`lr zk5eQ8J&?{V15b>?`siT1Mp;sS7{mo2QYj>WC$ef!bb2WNiw)}HPLG`>duZCij7L&EoIo_XrPre3vpuH9o*-0aX|EdIG^YM+|@w< zeE``8?A{20aiLEN4-F#AWh%beXOdltn3qAi>`c2x2^%-%CC$T2)wqqKRk@B$N@ zVg()>SKE-Z+ZXMkG4*3L0LYb5Xo6!1fup>c{fRbT^0|%Ka=TEQwBhQ>UJ2XH;H{F4 ze7>aB`$F7eT$&^Sy;3f80a9Zg$dK%jO%TMW^|kKaKoN)mGXr`d za08wVP~SjtSq18Kv%38u43%cUz+bd8I)OMgtaKmy+Fuw^yMUL8e$(k=_vTWn3WOkm z?nH*SNWdWDQ;+B~q0&!n2f^k~BPxLo4bZQ-IN>y@S2$cIenwNH9yw9iNu{J6VYNAl zEEE^f2e)jWnmeFDa5eXr`X@y|w#*c1v!>b?JcPtCxFedarVpH;aISdQHX4_`)nSca zofgxbT_e$qqmMsfKrORPl0|t?NAgwaac*%d-0S)Cr^tvWH}6fy6B(k%Yz+oEd;@g{ zhaSlEEbf9kuz0ZREWXz&GroTdXR7Xq?6OT#P;JTK!wtke?O8HlBo7t&zo}O^GGtcj=J$<*S>>L(wer9jAtNzwKq;M}ejtkT*HX~ln zrrv8Sx8v^FEEi*zo5DAjG4A*E1_`VK3(bLta`KlTtPBW)MHeEUXx_`rrblY(ZW7XY zo279eZg!%1o-B))ftGwFGgPlXsK!yNEjS0{>EV1tML(Q2(MDf@7STaSq%}dX@Jl&{ zUBG!XNQP05T7DyWTsp3N|7az*TOoZg#;3fX&35jFZg*8IU@CIW2ydUlwJ2P}K)xK;uXjZ@m)s6(Jr>)^Lpi8-&1Nl%&?lToyu6S^xEJSAEbhF-9phikn zm73`CY*En6!XiTMbthby;fRzo^Pl~ePj};o4v6uUz&ZwpkxbXZc>2qmy(gZP9+d?9 zs2%?s;Wj>Kh%vayN}P&C$GijRxsJ)v%QMSEkT(Q2WTd-aTz^jWQROaLu~}*Mjvbk- zio_irUg!%iRRNV)@HaAr5q&;7?cqSl6(Eo

sKQS4 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 Rc^U%{c)I$ztaD0e0s!dz!;t_0 literal 59199 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 diff --git a/fluentinterface/etc/fluentinterface.ucls b/fluentinterface/etc/fluentinterface.ucls index 3277148cd..aab2c9ad7 100644 --- a/fluentinterface/etc/fluentinterface.ucls +++ b/fluentinterface/etc/fluentinterface.ucls @@ -1,100 +1,100 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java index b9e5909f1..f7352fe39 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java @@ -25,27 +25,12 @@ public class App { public static void main(String[] args) { - List integerList = new ArrayList() { - { - add(1); - add(-61); - add(14); - add(-22); - add(18); - add(-87); - add(6); - add(64); - add(-82); - add(26); - add(-98); - add(97); - add(45); - add(23); - add(2); - add(-68); - add(45); - } - }; + List integerList = new ArrayList<>(); + integerList.addAll(Arrays.asList( + 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, + 45, 23, 2, -68, 45 + )); + prettyPrint("The initial list contains: ", integerList); List firstFiveNegatives = @@ -105,10 +90,6 @@ public class App { prettyPrint(", ", prefix, ".", iterable); } - private static void prettyPrint(String prefix, String suffix, Iterable iterable) { - prettyPrint(", ", prefix, suffix, iterable); - } - private static void prettyPrint(String delimiter, String prefix, String suffix, Iterable iterable) { StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java index 5adfa83ce..560b10189 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -1,15 +1,14 @@ package com.iluwatar.fluentinterface.fluentiterable.lazy; -import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Optional; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + /** * This is a lazy implementation of the FluentIterable interface. It evaluates all chained * operations when a terminating operation is applied. diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java index db7a31954..19283152e 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -124,7 +124,7 @@ public class SimpleFluentIterable implements FluentIterable { */ @Override public final FluentIterable map(Function function) { - List temporaryList = new ArrayList(); + List temporaryList = new ArrayList<>(); Iterator iterator = iterator(); while (iterator.hasNext()) { temporaryList.add(function.apply(iterator.next())); From 338ecd1658c2d49c6395aedacbb4f7eb0aa2fa57 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 10 Sep 2015 15:26:47 +0530 Subject: [PATCH 25/80] Work on #208, categorized factory method in beginner difficulty tag --- factory-method/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/factory-method/index.md b/factory-method/index.md index fa30a4349..8011e42c8 100644 --- a/factory-method/index.md +++ b/factory-method/index.md @@ -4,7 +4,9 @@ title: Factory Method folder: factory-method permalink: /patterns/factory-method/ categories: Creational -tags: Java +tags: + - Java + - Difficulty-Beginner --- **Intent:** Define an interface for creating an object, but let subclasses From b12bbb8e0052e99136165be79c550482d999f67b Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Thu, 10 Sep 2015 17:37:04 +0200 Subject: [PATCH 26/80] Work on #218, automagic website updates --- .travis.yml | 6 ++++++ update-ghpages.sh | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 update-ghpages.sh diff --git a/.travis.yml b/.travis.yml index af275508b..a59370c30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,18 @@ language: java jdk: - oraclejdk8 +env: + global: + - GH_REF: github.com/iluwatar/java-design-patterns.git + - secure: "LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg=" + before_install: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" after_success: - mvn clean test jacoco:report coveralls:report + - bash update-ghpages.sh # Migration to container-based infrastructure sudo: false diff --git a/update-ghpages.sh b/update-ghpages.sh new file mode 100644 index 000000000..7867150d6 --- /dev/null +++ b/update-ghpages.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Setup Git +git config user.name "Travis-CI" +git config user.email "travis@no.reply" + +# Clone gh-pages +git clone -b gh-pages "https://${GH_REF}" ghpagesclone +cd ghpagesclone + +# Init and update submodule to latest +git submodule update --init --recursive +git submodule update --remote + +# If there is a new version of the master branch +if git status | grep patterns > /dev/null 2>&1 +then + # it should be committed + git add . + git commit -m ":sparkles: :up: Automagic Update via Travis-CI" + git push --quiet "https://${GH_TOKEN}@${GH_REF}" master:gh-pages > /dev/null 2>&1 +fi From bd89530fa24dcdaa619e51f446daad000bb2fbdd Mon Sep 17 00:00:00 2001 From: Markus Date: Thu, 10 Sep 2015 17:55:05 +0200 Subject: [PATCH 27/80] Work on #218, fixed git config git config must either be global or inside a repo. I moved the lines for initialization of git to only apply to the ghpagesclone. --- update-ghpages.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/update-ghpages.sh b/update-ghpages.sh index 7867150d6..63ac36427 100644 --- a/update-ghpages.sh +++ b/update-ghpages.sh @@ -1,9 +1,5 @@ #!/bin/bash -# Setup Git -git config user.name "Travis-CI" -git config user.email "travis@no.reply" - # Clone gh-pages git clone -b gh-pages "https://${GH_REF}" ghpagesclone cd ghpagesclone @@ -12,6 +8,10 @@ cd ghpagesclone git submodule update --init --recursive git submodule update --remote +# Setup Git +git config user.name "Travis-CI" +git config user.email "travis@no.reply" + # If there is a new version of the master branch if git status | grep patterns > /dev/null 2>&1 then From bbd78649278cf5fc719cff04d2530e7fe964ba13 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Thu, 10 Sep 2015 18:43:23 +0200 Subject: [PATCH 28/80] Work on #218, fix push command --- update-ghpages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-ghpages.sh b/update-ghpages.sh index 63ac36427..82486d5a4 100644 --- a/update-ghpages.sh +++ b/update-ghpages.sh @@ -18,5 +18,5 @@ then # it should be committed git add . git commit -m ":sparkles: :up: Automagic Update via Travis-CI" - git push --quiet "https://${GH_TOKEN}@${GH_REF}" master:gh-pages > /dev/null 2>&1 + git push --quiet "https://${GH_TOKEN}:x-oauth-basic@${GH_REF}" gh-pages > /dev/null 2>&1 fi From 9ecb1b25462402e39cdf52596669789330f9105a Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 11 Sep 2015 13:08:50 +0530 Subject: [PATCH 29/80] Work on #184, changed category, added difficulty tag, more real applications and credits, added implementation approaches --- fluentinterface/index.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/fluentinterface/index.md b/fluentinterface/index.md index 942867933..27a4d1a26 100644 --- a/fluentinterface/index.md +++ b/fluentinterface/index.md @@ -3,14 +3,25 @@ layout: pattern title: Fluent Interface folder: fluentinterface permalink: /patterns/fluentinterface/ -categories: Architectural -tags: Java +categories: Other +tags: + - Java + - Difficulty-Intermediate --- **Intent:** A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. +**Implementation:** + +A fluent interface can be implemented using any of + + * Method Chaining - calling a method returns some object on which further methods can be called. + * Static Factory Methods and Imports + * Named parameters - can be simulated in Java using static factory methods. + ![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") + **Applicability:** Use the Fluent Interface pattern when * you provide an API that would benefit from a DSL-like usage @@ -20,9 +31,12 @@ tags: Java * [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html) * [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained) -* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/) - -**Credits** - -* [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html) -* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/) \ No newline at end of file +* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/) +* [Mockito](http://mockito.org/) +* [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial) + +**Credits** + +* [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html) +* [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/) +* [Internal DSL](http://www.infoq.com/articles/internal-dsls-java) From 65add323ed1cc435cca3e3c2802da7cebe26a00c Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 11 Sep 2015 13:51:44 +0530 Subject: [PATCH 30/80] Work on #208, explained the ways our patterns can be explored --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 38936193c..77282f569 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,13 @@ are familiar with the patterns. # Getting started Before you dive into the material, you should be familiar with various -[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). +[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). + +Once you are familiar with these concepts you can start drilling down into patterns by any of the following approaches + + - Using difficulty tags, `Difficulty-Beginner`, `Difficulty-Intermediate` & `Difficulty-Expert`. + - Using pattern categories, `Creational`, `Behavioral` and others. + - Search for a specific pattern. Can't find one? Please report a new pattern [here](https://github.com/iluwatar/java-design-patterns/issues). # How to contribute From 3ebc64c5d689cd200d979d36d2895975a778b8b2 Mon Sep 17 00:00:00 2001 From: zafarella Date: Thu, 13 Aug 2015 01:31:49 -0400 Subject: [PATCH 31/80] add draft --- singleton/presentation/index.html | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 singleton/presentation/index.html diff --git a/singleton/presentation/index.html b/singleton/presentation/index.html new file mode 100644 index 000000000..238bdb50f --- /dev/null +++ b/singleton/presentation/index.html @@ -0,0 +1,57 @@ + + + + + Circular Slides - impress.js + + + +
+
My Slides +
+
Slide 1
+
Slide 2
+
Slide 3
+
Slide 4
+ +
+
+ + + From 6176f134c4b1d4e58d85089f80c21e6715dc2025 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Fri, 11 Sep 2015 22:56:11 +0300 Subject: [PATCH 32/80] Update Hibernate, Spring and Apache Isis dependencies. --- naked-objects/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index 8cac48a09..c9387b58d 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -27,7 +27,7 @@ - 1.9.0-SNAPSHOT + 1.9.0 UTF-8 UTF-8 diff --git a/pom.xml b/pom.xml index 2c040005b..a67b32e78 100644 --- a/pom.xml +++ b/pom.xml @@ -9,8 +9,8 @@ UTF-8 - 5.0.0.Final - 1.8.2.RELEASE + 5.0.1.Final + 1.9.0.RELEASE 1.4.188 4.12 3.0 From 266aca20c781c0ca146f52685bd6008edbc1bb01 Mon Sep 17 00:00:00 2001 From: zafarella Date: Fri, 11 Sep 2015 18:30:17 -0400 Subject: [PATCH 33/80] delete presentation from branch --- singleton/presentation/index.html | 57 ------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 singleton/presentation/index.html diff --git a/singleton/presentation/index.html b/singleton/presentation/index.html deleted file mode 100644 index 238bdb50f..000000000 --- a/singleton/presentation/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - Circular Slides - impress.js - - - -
-
My Slides -
-
Slide 1
-
Slide 2
-
Slide 3
-
Slide 4
- -
-
- - - From 8d429525dcb752db7854f0695630c1a98eb17b76 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 12 Sep 2015 17:46:24 +0530 Subject: [PATCH 34/80] Work on #74, updated javadocs, reformatted code to google style guide, added missing final modifiers --- .../java/com/iluwatar/reactor/app/App.java | 145 ++++--- .../com/iluwatar/reactor/app/AppClient.java | 253 +++++------ .../iluwatar/reactor/app/LoggingHandler.java | 81 ++-- .../reactor/framework/AbstractNioChannel.java | 256 +++++------ .../reactor/framework/ChannelHandler.java | 22 +- .../reactor/framework/Dispatcher.java | 56 +-- .../reactor/framework/NioDatagramChannel.java | 266 ++++++------ .../reactor/framework/NioReactor.java | 401 +++++++++--------- .../framework/NioServerSocketChannel.java | 135 +++--- .../framework/SameThreadDispatcher.java | 47 +- .../framework/ThreadPoolDispatcher.java | 71 ++-- .../com/iluwatar/reactor/app/AppTest.java | 50 ++- 12 files changed, 908 insertions(+), 875 deletions(-) diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java index 947173494..fcc327b34 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -10,20 +10,18 @@ import com.iluwatar.reactor.framework.NioServerSocketChannel; import com.iluwatar.reactor.framework.ThreadPoolDispatcher; /** - * This application demonstrates Reactor pattern. The example demonstrated is a Distributed Logging Service - * where it listens on multiple TCP or UDP sockets for incoming log requests. + * This application demonstrates Reactor pattern. The example demonstrated is a Distributed Logging + * Service where it listens on multiple TCP or UDP sockets for incoming log requests. * *

- * INTENT - *
- * The Reactor design pattern handles service requests that are delivered concurrently to an + * INTENT
+ * The Reactor design pattern handles service requests that are delivered concurrently to an * application by one or more clients. The application can register specific handlers for processing * which are called by reactor on specific events. * *

- * PROBLEM - *
- * Server applications in a distributed system must handle multiple clients that send them service + * PROBLEM
+ * Server applications in a distributed system must handle multiple clients that send them service * requests. Following forces need to be resolved: *

    *
  • Availability
  • @@ -33,8 +31,28 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher; *
* *

- * The application utilizes single thread to listen for requests on all ports. It does not create - * a separate thread for each client, which provides better scalability under load (number of clients + * PARTICIPANTS
+ *

    + *
  • Synchronous Event De-multiplexer
  • {@link NioReactor} plays the role of synchronous event + * de-multiplexer. It waits for events on multiple channels registered to it in an event loop. + * + *

    + *

  • Initiation Dispatcher
  • {@link NioReactor} plays this role as the application specific + * {@link ChannelHandler}s are registered to the reactor. + * + *

    + *

  • Handle
  • {@link AbstractNioChannel} acts as a handle that is registered to the reactor. + * When any events occur on a handle, reactor calls the appropriate handler. + * + *

    + *

  • Event Handler
  • {@link ChannelHandler} acts as an event handler, which is bound to a + * channel and is called back when any event occurs on any of its associated handles. Application + * logic resides in event handlers. + *
+ * + *

+ * The application utilizes single thread to listen for requests on all ports. It does not create a + * separate thread for each client, which provides better scalability under load (number of clients * increase). * *

@@ -45,59 +63,60 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher; */ public class App { - private NioReactor reactor; + private NioReactor reactor; - /** - * App entry. - * @throws IOException - */ - public static void main(String[] args) throws IOException { - new App().start(); - } - - /** - * Starts the NIO reactor. - * @throws IOException if any channel fails to bind. - */ - public void start() throws IOException { - /* - * The application can customize its event dispatching mechanism. - */ - reactor = new NioReactor(new ThreadPoolDispatcher(2)); - - /* - * This represents application specific business logic that dispatcher will call - * on appropriate events. These events are read events in our example. - */ - LoggingHandler loggingHandler = new LoggingHandler(); - - /* - * Our application binds to multiple channels and uses same logging handler to handle - * incoming log requests. - */ - reactor - .registerChannel(tcpChannel(6666, loggingHandler)) - .registerChannel(tcpChannel(6667, loggingHandler)) - .registerChannel(udpChannel(6668, loggingHandler)) - .start(); - } - - /** - * Stops the NIO reactor. This is a blocking call. - */ - public void stop() { - reactor.stop(); - } + /** + * App entry. + * + * @throws IOException + */ + public static void main(String[] args) throws IOException { + new App().start(); + } - private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { - NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); - channel.bind(); - return channel; - } - - private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { - NioDatagramChannel channel = new NioDatagramChannel(port, handler); - channel.bind(); - return channel; - } + /** + * Starts the NIO reactor. + * + * @throws IOException if any channel fails to bind. + */ + public void start() throws IOException { + /* + * The application can customize its event dispatching mechanism. + */ + reactor = new NioReactor(new ThreadPoolDispatcher(2)); + + /* + * This represents application specific business logic that dispatcher will call on appropriate + * events. These events are read events in our example. + */ + LoggingHandler loggingHandler = new LoggingHandler(); + + /* + * Our application binds to multiple channels and uses same logging handler to handle incoming + * log requests. + */ + reactor.registerChannel(tcpChannel(6666, loggingHandler)).registerChannel(tcpChannel(6667, loggingHandler)) + .registerChannel(udpChannel(6668, loggingHandler)).start(); + } + + /** + * Stops the NIO reactor. This is a blocking call. + * + * @throws InterruptedException if interrupted while stopping the reactor. + */ + public void stop() throws InterruptedException { + reactor.stop(); + } + + private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { + NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); + channel.bind(); + return channel; + } + + private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { + NioDatagramChannel channel = new NioDatagramChannel(port, handler); + channel.bind(); + return channel; + } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java index 033711569..c50e4d3e7 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -17,148 +17,149 @@ import java.util.concurrent.TimeUnit; /** * Represents the clients of Reactor pattern. Multiple clients are run concurrently and send logging * requests to Reactor. - * + * * @author npathai */ public class AppClient { - private ExecutorService service = Executors.newFixedThreadPool(4); + private final ExecutorService service = Executors.newFixedThreadPool(4); - /** - * App client entry. - * @throws IOException if any I/O error occurs. - */ - public static void main(String[] args) throws IOException { - AppClient appClient = new AppClient(); - appClient.start(); - } + /** + * App client entry. + * + * @throws IOException if any I/O error occurs. + */ + public static void main(String[] args) throws IOException { + AppClient appClient = new AppClient(); + appClient.start(); + } - /** - * Starts the logging clients. - * @throws IOException if any I/O error occurs. - */ - public void start() throws IOException { - service.execute(new TCPLoggingClient("Client 1", 6666)); - service.execute(new TCPLoggingClient("Client 2", 6667)); - service.execute(new UDPLoggingClient("Client 3", 6668)); - service.execute(new UDPLoggingClient("Client 4", 6668)); - } + /** + * Starts the logging clients. + * + * @throws IOException if any I/O error occurs. + */ + public void start() throws IOException { + service.execute(new TCPLoggingClient("Client 1", 6666)); + service.execute(new TCPLoggingClient("Client 2", 6667)); + service.execute(new UDPLoggingClient("Client 3", 6668)); + service.execute(new UDPLoggingClient("Client 4", 6668)); + } - /** - * Stops logging clients. This is a blocking call. - */ - public void stop() { - service.shutdown(); - if (!service.isTerminated()) { - service.shutdownNow(); - try { - service.awaitTermination(1000, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private static void artificialDelayOf(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + /** + * Stops logging clients. This is a blocking call. + */ + public void stop() { + service.shutdown(); + if (!service.isTerminated()) { + service.shutdownNow(); + try { + service.awaitTermination(1000, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } - /** - * A logging client that sends requests to Reactor on TCP socket. - */ - static class TCPLoggingClient implements Runnable { + private static void artificialDelayOf(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - private int serverPort; - private String clientName; + /** + * A logging client that sends requests to Reactor on TCP socket. + */ + static class TCPLoggingClient implements Runnable { - /** - * Creates a new TCP logging client. - * - * @param clientName the name of the client to be sent in logging requests. - * @param port the port on which client will send logging requests. - */ - public TCPLoggingClient(String clientName, int serverPort) { - this.clientName = clientName; - this.serverPort = serverPort; - } + private final int serverPort; + private final String clientName; - public void run() { - try (Socket socket = new Socket(InetAddress.getLocalHost(), serverPort)) { - OutputStream outputStream = socket.getOutputStream(); - PrintWriter writer = new PrintWriter(outputStream); - sendLogRequests(writer, socket.getInputStream()); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } + /** + * Creates a new TCP logging client. + * + * @param clientName the name of the client to be sent in logging requests. + * @param port the port on which client will send logging requests. + */ + public TCPLoggingClient(String clientName, int serverPort) { + this.clientName = clientName; + this.serverPort = serverPort; + } - private void sendLogRequests(PrintWriter writer, InputStream inputStream) throws IOException { - for (int i = 0; i < 4; i++) { - writer.println(clientName + " - Log request: " + i); - writer.flush(); + public void run() { + try (Socket socket = new Socket(InetAddress.getLocalHost(), serverPort)) { + OutputStream outputStream = socket.getOutputStream(); + PrintWriter writer = new PrintWriter(outputStream); + sendLogRequests(writer, socket.getInputStream()); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } - byte[] data = new byte[1024]; - int read = inputStream.read(data, 0, data.length); - if (read == 0) { - System.out.println("Read zero bytes"); - } else { - System.out.println(new String(data, 0, read)); - } + private void sendLogRequests(PrintWriter writer, InputStream inputStream) throws IOException { + for (int i = 0; i < 4; i++) { + writer.println(clientName + " - Log request: " + i); + writer.flush(); - artificialDelayOf(100); - } - } + byte[] data = new byte[1024]; + int read = inputStream.read(data, 0, data.length); + if (read == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(data, 0, read)); + } - } + artificialDelayOf(100); + } + } - /** - * A logging client that sends requests to Reactor on UDP socket. - */ - static class UDPLoggingClient implements Runnable { - private String clientName; - private InetSocketAddress remoteAddress; + } - /** - * Creates a new UDP logging client. - * - * @param clientName the name of the client to be sent in logging requests. - * @param port the port on which client will send logging requests. - * @throws UnknownHostException if localhost is unknown - */ - public UDPLoggingClient(String clientName, int port) throws UnknownHostException { - this.clientName = clientName; - this.remoteAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); - } + /** + * A logging client that sends requests to Reactor on UDP socket. + */ + static class UDPLoggingClient implements Runnable { + private final String clientName; + private final InetSocketAddress remoteAddress; - @Override - public void run() { - try (DatagramSocket socket = new DatagramSocket()) { - for (int i = 0; i < 4; i++) { - - String message = clientName + " - Log request: " + i; - DatagramPacket request = new DatagramPacket(message.getBytes(), - message.getBytes().length, remoteAddress); - - socket.send(request); + /** + * Creates a new UDP logging client. + * + * @param clientName the name of the client to be sent in logging requests. + * @param port the port on which client will send logging requests. + * @throws UnknownHostException if localhost is unknown + */ + public UDPLoggingClient(String clientName, int port) throws UnknownHostException { + this.clientName = clientName; + this.remoteAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); + } - byte[] data = new byte[1024]; - DatagramPacket reply = new DatagramPacket(data, data.length); - socket.receive(reply); - if (reply.getLength() == 0) { - System.out.println("Read zero bytes"); - } else { - System.out.println(new String(reply.getData(), 0, reply.getLength())); - } - - artificialDelayOf(100); - } - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } + @Override + public void run() { + try (DatagramSocket socket = new DatagramSocket()) { + for (int i = 0; i < 4; i++) { + + String message = clientName + " - Log request: " + i; + DatagramPacket request = new DatagramPacket(message.getBytes(), message.getBytes().length, remoteAddress); + + socket.send(request); + + byte[] data = new byte[1024]; + DatagramPacket reply = new DatagramPacket(data, data.length); + socket.receive(reply); + if (reply.getLength() == 0) { + System.out.println("Read zero bytes"); + } else { + System.out.println(new String(reply.getData(), 0, reply.getLength())); + } + + artificialDelayOf(100); + } + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java index eed26b078..1f2694b0b 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -8,53 +8,54 @@ import com.iluwatar.reactor.framework.ChannelHandler; import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; /** - * Logging server application logic. It logs the incoming requests on standard console and returns - * a canned acknowledgement back to the remote peer. + * Logging server application logic. It logs the incoming requests on standard console and returns a + * canned acknowledgement back to the remote peer. * * @author npathai */ public class LoggingHandler implements ChannelHandler { - private static final byte[] ACK = "Data logged successfully".getBytes(); + private static final byte[] ACK = "Data logged successfully".getBytes(); - /** - * Decodes the received data and logs it on standard console. - */ - @Override - public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { - /* - * As this handler is attached with both TCP and UDP channels we need to check whether - * the data received is a ByteBuffer (from TCP channel) or a DatagramPacket (from UDP channel). - */ - if (readObject instanceof ByteBuffer) { - doLogging(((ByteBuffer)readObject)); - sendReply(channel, key); - } else if (readObject instanceof DatagramPacket) { - DatagramPacket datagram = (DatagramPacket)readObject; - doLogging(datagram.getData()); - sendReply(channel, datagram, key); - } else { - throw new IllegalStateException("Unknown data received"); - } - } + /** + * Decodes the received data and logs it on standard console. + */ + @Override + public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) { + /* + * As this handler is attached with both TCP and UDP channels we need to check whether the data + * received is a ByteBuffer (from TCP channel) or a DatagramPacket (from UDP channel). + */ + if (readObject instanceof ByteBuffer) { + doLogging(((ByteBuffer) readObject)); + sendReply(channel, key); + } else if (readObject instanceof DatagramPacket) { + DatagramPacket datagram = (DatagramPacket) readObject; + doLogging(datagram.getData()); + sendReply(channel, datagram, key); + } else { + throw new IllegalStateException("Unknown data received"); + } + } - private void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) { - /* - * Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming message. - */ - DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap(ACK)); - replyPacket.setReceiver(incomingPacket.getSender()); - - channel.write(replyPacket, key); - } + private void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) { + /* + * Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming + * message. + */ + DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap(ACK)); + replyPacket.setReceiver(incomingPacket.getSender()); - private void sendReply(AbstractNioChannel channel, SelectionKey key) { - ByteBuffer buffer = ByteBuffer.wrap(ACK); - channel.write(buffer, key); - } + channel.write(replyPacket, key); + } - private void doLogging(ByteBuffer data) { - // assuming UTF-8 :( - System.out.println(new String(data.array(), 0, data.limit())); - } + private void sendReply(AbstractNioChannel channel, SelectionKey key) { + ByteBuffer buffer = ByteBuffer.wrap(ACK); + channel.write(buffer, key); + } + + private void doLogging(ByteBuffer data) { + // assuming UTF-8 :( + System.out.println(new String(data.array(), 0, data.limit())); + } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java index 24862644d..09f308731 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -10,143 +10,145 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; /** - * This represents the Handle of Reactor pattern. These are resources managed by OS - * which can be submitted to {@link NioReactor}. + * This represents the Handle of Reactor pattern. These are resources managed by OS which can + * be submitted to {@link NioReactor}. * *

- * This class serves has the responsibility of reading the data when a read event occurs and - * writing the data back when the channel is writable. It leaves the reading and writing of - * data on the concrete implementation. It provides a block writing mechanism wherein when - * any {@link ChannelHandler} wants to write data back, it queues the data in pending write queue - * and clears it in block manner. This provides better throughput. - * + * This class serves has the responsibility of reading the data when a read event occurs and writing + * the data back when the channel is writable. It leaves the reading and writing of data on the + * concrete implementation. It provides a block writing mechanism wherein when any + * {@link ChannelHandler} wants to write data back, it queues the data in pending write queue and + * clears it in block manner. This provides better throughput. + * * @author npathai * */ public abstract class AbstractNioChannel { - - private SelectableChannel channel; - private ChannelHandler handler; - private Map> channelToPendingWrites = new ConcurrentHashMap<>(); - private NioReactor reactor; - - /** - * Creates a new channel. - * @param handler which will handle events occurring on this channel. - * @param channel a NIO channel to be wrapped. - */ - public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { - this.handler = handler; - this.channel = channel; - } - - /** - * Injects the reactor in this channel. - */ - void setReactor(NioReactor reactor) { - this.reactor = reactor; - } - /** - * @return the wrapped NIO channel. - */ - public SelectableChannel getChannel() { - return channel; - } + private final SelectableChannel channel; + private final ChannelHandler handler; + private final Map> channelToPendingWrites = new ConcurrentHashMap<>(); + private NioReactor reactor; - /** - * The operation in which the channel is interested, this operation is provided to {@link Selector}. - * - * @return interested operation. - * @see SelectionKey - */ - public abstract int getInterestedOps(); - - /** - * Binds the channel on provided port. - * - * @throws IOException if any I/O error occurs. - */ - public abstract void bind() throws IOException; - - /** - * Reads the data using the key and returns the read data. The underlying channel should be fetched using - * {@link SelectionKey#channel()}. - * - * @param key the key on which read event occurred. - * @return data read. - * @throws IOException if any I/O error occurs. - */ - public abstract Object read(SelectionKey key) throws IOException; + /** + * Creates a new channel. + * + * @param handler which will handle events occurring on this channel. + * @param channel a NIO channel to be wrapped. + */ + public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { + this.handler = handler; + this.channel = channel; + } - /** - * @return the handler associated with this channel. - */ - public ChannelHandler getHandler() { - return handler; - } + /** + * Injects the reactor in this channel. + */ + void setReactor(NioReactor reactor) { + this.reactor = reactor; + } - /* - * Called from the context of reactor thread when the key becomes writable. - * The channel writes the whole pending block of data at once. - */ - void flush(SelectionKey key) throws IOException { - Queue pendingWrites = channelToPendingWrites.get(key.channel()); - while (true) { - Object pendingWrite = pendingWrites.poll(); - if (pendingWrite == null) { - // We don't have anything more to write so channel is interested in reading more data - reactor.changeOps(key, SelectionKey.OP_READ); - break; - } - - // ask the concrete channel to make sense of data and write it to java channel - doWrite(pendingWrite, key); - } - } + /** + * @return the wrapped NIO channel. + */ + public SelectableChannel getChannel() { + return channel; + } - /** - * Writes the data to the channel. - * - * @param pendingWrite the data to be written on channel. - * @param key the key which is writable. - * @throws IOException if any I/O error occurs. - */ - protected abstract void doWrite(Object pendingWrite, SelectionKey key) throws IOException; + /** + * The operation in which the channel is interested, this operation is provided to + * {@link Selector}. + * + * @return interested operation. + * @see SelectionKey + */ + public abstract int getInterestedOps(); - /** - * Queues the data for writing. The data is not guaranteed to be written on underlying channel - * when this method returns. It will be written when the channel is flushed. - * - *

- * This method is used by the {@link ChannelHandler} to send reply back to the client. - *
- * Example: - *

-	 * 
-	 * {@literal @}Override
-	 * public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) {
-	 *   byte[] data = ((ByteBuffer)readObject).array();
-	 *   ByteBuffer buffer = ByteBuffer.wrap("Server reply".getBytes());
-	 *   channel.write(buffer, key);
-	 * }
-	 * 
-	 * 
-	 * @param data the data to be written on underlying channel.
-	 * @param key the key which is writable.
-	 */
-	public void write(Object data, SelectionKey key) {
-		Queue pendingWrites = this.channelToPendingWrites.get(key.channel());
-		if (pendingWrites == null) {
-			synchronized (this.channelToPendingWrites) {
-				pendingWrites = this.channelToPendingWrites.get(key.channel());
-				if (pendingWrites == null) {
-					pendingWrites = new ConcurrentLinkedQueue<>();
-					this.channelToPendingWrites.put(key.channel(), pendingWrites);
-				}
-			}
-		}
-		pendingWrites.add(data);
-		reactor.changeOps(key, SelectionKey.OP_WRITE);
-	}
+  /**
+   * Binds the channel on provided port.
+   * 
+   * @throws IOException if any I/O error occurs.
+   */
+  public abstract void bind() throws IOException;
+
+  /**
+   * Reads the data using the key and returns the read data. The underlying channel should be
+   * fetched using {@link SelectionKey#channel()}.
+   * 
+   * @param key the key on which read event occurred.
+   * @return data read.
+   * @throws IOException if any I/O error occurs.
+   */
+  public abstract Object read(SelectionKey key) throws IOException;
+
+  /**
+   * @return the handler associated with this channel.
+   */
+  public ChannelHandler getHandler() {
+    return handler;
+  }
+
+  /*
+   * Called from the context of reactor thread when the key becomes writable. The channel writes the
+   * whole pending block of data at once.
+   */
+  void flush(SelectionKey key) throws IOException {
+    Queue pendingWrites = channelToPendingWrites.get(key.channel());
+    while (true) {
+      Object pendingWrite = pendingWrites.poll();
+      if (pendingWrite == null) {
+        // We don't have anything more to write so channel is interested in reading more data
+        reactor.changeOps(key, SelectionKey.OP_READ);
+        break;
+      }
+
+      // ask the concrete channel to make sense of data and write it to java channel
+      doWrite(pendingWrite, key);
+    }
+  }
+
+  /**
+   * Writes the data to the channel.
+   * 
+   * @param pendingWrite the data to be written on channel.
+   * @param key the key which is writable.
+   * @throws IOException if any I/O error occurs.
+   */
+  protected abstract void doWrite(Object pendingWrite, SelectionKey key) throws IOException;
+
+  /**
+   * Queues the data for writing. The data is not guaranteed to be written on underlying channel
+   * when this method returns. It will be written when the channel is flushed.
+   * 
+   * 

+ * This method is used by the {@link ChannelHandler} to send reply back to the client.
+ * Example: + * + *

+   * 
+   * {@literal @}Override
+   * public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) {
+   *   byte[] data = ((ByteBuffer)readObject).array();
+   *   ByteBuffer buffer = ByteBuffer.wrap("Server reply".getBytes());
+   *   channel.write(buffer, key);
+   * }
+   * 
+   * 
+   * @param data the data to be written on underlying channel.
+   * @param key the key which is writable.
+   */
+  public void write(Object data, SelectionKey key) {
+    Queue pendingWrites = this.channelToPendingWrites.get(key.channel());
+    if (pendingWrites == null) {
+      synchronized (this.channelToPendingWrites) {
+        pendingWrites = this.channelToPendingWrites.get(key.channel());
+        if (pendingWrites == null) {
+          pendingWrites = new ConcurrentLinkedQueue<>();
+          this.channelToPendingWrites.put(key.channel(), pendingWrites);
+        }
+      }
+    }
+    pendingWrites.add(data);
+    reactor.changeOps(key, SelectionKey.OP_WRITE);
+  }
 }
diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java
index 0aae9db75..a4a392a34 100644
--- a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java
+++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java
@@ -7,19 +7,19 @@ import java.nio.channels.SelectionKey;
  * to it by the {@link Dispatcher}. This is where the application logic resides.
  * 
  * 

- * A {@link ChannelHandler} can be associated with one or many {@link AbstractNioChannel}s, and whenever - * an event occurs on any of the associated channels, the handler is notified of the event. - * + * A {@link ChannelHandler} can be associated with one or many {@link AbstractNioChannel}s, and + * whenever an event occurs on any of the associated channels, the handler is notified of the event. + * * @author npathai */ public interface ChannelHandler { - /** - * Called when the {@code channel} receives some data from remote peer. - * - * @param channel the channel from which the data was received. - * @param readObject the data read. - * @param key the key on which read event occurred. - */ - void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); + /** + * Called when the {@code channel} receives some data from remote peer. + * + * @param channel the channel from which the data was received. + * @param readObject the data read. + * @param key the key on which read event occurred. + */ + void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java index c563ef9d3..0ed53f8fc 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java @@ -3,39 +3,41 @@ package com.iluwatar.reactor.framework; import java.nio.channels.SelectionKey; /** - * Represents the event dispatching strategy. When {@link NioReactor} senses any event on the - * registered {@link AbstractNioChannel}s then it de-multiplexes the event type, read or write - * or connect, and then calls the {@link Dispatcher} to dispatch the read events. This decouples the I/O - * processing from application specific processing. - *
- * Dispatcher should call the {@link ChannelHandler} associated with the channel on which event occurred. + * Represents the event dispatching strategy. When {@link NioReactor} senses any event on the + * registered {@link AbstractNioChannel}s then it de-multiplexes the event type, read or write or + * connect, and then calls the {@link Dispatcher} to dispatch the read events. This decouples the + * I/O processing from application specific processing.
+ * Dispatcher should call the {@link ChannelHandler} associated with the channel on which event + * occurred. * *

- * The application can customize the way in which event is dispatched such as using the reactor thread to - * dispatch event to channels or use a worker pool to do the non I/O processing. - * + * The application can customize the way in which event is dispatched such as using the reactor + * thread to dispatch event to channels or use a worker pool to do the non I/O processing. + * * @see SameThreadDispatcher * @see ThreadPoolDispatcher * * @author npathai */ public interface Dispatcher { - /** - * This hook method is called when read event occurs on particular channel. The data read - * is provided in readObject. The implementation should dispatch this read event - * to the associated {@link ChannelHandler} of channel. - * - *

- * The type of readObject depends on the channel on which data was received. - * - * @param channel on which read event occurred - * @param readObject object read by channel - * @param key on which event occurred - */ - void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); - - /** - * Stops dispatching events and cleans up any acquired resources such as threads. - */ - void stop(); + /** + * This hook method is called when read event occurs on particular channel. The data read is + * provided in readObject. The implementation should dispatch this read event to the + * associated {@link ChannelHandler} of channel. + * + *

+ * The type of readObject depends on the channel on which data was received. + * + * @param channel on which read event occurred + * @param readObject object read by channel + * @param key on which event occurred + */ + void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key); + + /** + * Stops dispatching events and cleans up any acquired resources such as threads. + * + * @throws InterruptedException if interrupted while stopping dispatcher. + */ + void stop() throws InterruptedException; } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java index f338ce4a3..089911d10 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -15,143 +15,147 @@ import java.nio.channels.SelectionKey; */ public class NioDatagramChannel extends AbstractNioChannel { - private int port; + private final int port; - /** - * Creates a {@link DatagramChannel} which will bind at provided port and use handler to handle - * incoming events on this channel. - *

- * Note the constructor does not bind the socket, {@link #bind()} method should be called for binding - * the socket. - * - * @param port the port to be bound to listen for incoming datagram requests. - * @param handler the handler to be used for handling incoming requests on this channel. - * @throws IOException if any I/O error occurs. - */ - public NioDatagramChannel(int port, ChannelHandler handler) throws IOException { - super(handler, DatagramChannel.open()); - this.port = port; - } + /** + * Creates a {@link DatagramChannel} which will bind at provided port and use handler + * to handle incoming events on this channel. + *

+ * Note the constructor does not bind the socket, {@link #bind()} method should be called for + * binding the socket. + * + * @param port the port to be bound to listen for incoming datagram requests. + * @param handler the handler to be used for handling incoming requests on this channel. + * @throws IOException if any I/O error occurs. + */ + public NioDatagramChannel(int port, ChannelHandler handler) throws IOException { + super(handler, DatagramChannel.open()); + this.port = port; + } - @Override - public int getInterestedOps() { - /* there is no need to accept connections in UDP, so the channel shows interest in - * reading data. - */ - return SelectionKey.OP_READ; - } + @Override + public int getInterestedOps() { + /* + * there is no need to accept connections in UDP, so the channel shows interest in reading data. + */ + return SelectionKey.OP_READ; + } - /** - * Reads and returns a {@link DatagramPacket} from the underlying channel. - * @return the datagram packet read having the sender address. - */ - @Override - public DatagramPacket read(SelectionKey key) throws IOException { - ByteBuffer buffer = ByteBuffer.allocate(1024); - SocketAddress sender = ((DatagramChannel)key.channel()).receive(buffer); - - /* - * It is required to create a DatagramPacket because we need to preserve which - * socket address acts as destination for sending reply packets. - */ - buffer.flip(); - DatagramPacket packet = new DatagramPacket(buffer); - packet.setSender(sender); - - return packet; - } - - /** - * @return the underlying datagram channel. - */ - @Override - public DatagramChannel getChannel() { - return (DatagramChannel) super.getChannel(); - } - - /** - * Binds UDP socket on the provided port. - * - * @throws IOException if any I/O error occurs. - */ - @Override - public void bind() throws IOException { - getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); - getChannel().configureBlocking(false); - System.out.println("Bound UDP socket at port: " + port); - } + /** + * Reads and returns a {@link DatagramPacket} from the underlying channel. + * + * @return the datagram packet read having the sender address. + */ + @Override + public DatagramPacket read(SelectionKey key) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(1024); + SocketAddress sender = ((DatagramChannel) key.channel()).receive(buffer); - /** - * Writes the pending {@link DatagramPacket} to the underlying channel sending data to - * the intended receiver of the packet. - */ - @Override - protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { - DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; - getChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); - } + /* + * It is required to create a DatagramPacket because we need to preserve which socket address + * acts as destination for sending reply packets. + */ + buffer.flip(); + DatagramPacket packet = new DatagramPacket(buffer); + packet.setSender(sender); - /** - * Writes the outgoing {@link DatagramPacket} to the channel. The intended receiver of the - * datagram packet must be set in the data using {@link DatagramPacket#setReceiver(SocketAddress)}. - */ - @Override - public void write(Object data, SelectionKey key) { - super.write(data, key); - } - - /** - * Container of data used for {@link NioDatagramChannel} to communicate with remote peer. - */ - public static class DatagramPacket { - private SocketAddress sender; - private ByteBuffer data; - private SocketAddress receiver; + return packet; + } - /** - * Creates a container with underlying data. - * - * @param data the underlying message to be written on channel. - */ - public DatagramPacket(ByteBuffer data) { - this.data = data; - } + /** + * @return the underlying datagram channel. + */ + @Override + public DatagramChannel getChannel() { + return (DatagramChannel) super.getChannel(); + } - /** - * @return the sender address. - */ - public SocketAddress getSender() { - return sender; - } - - /** - * Sets the sender address of this packet. - * @param sender the sender address. - */ - public void setSender(SocketAddress sender) { - this.sender = sender; - } + /** + * Binds UDP socket on the provided port. + * + * @throws IOException if any I/O error occurs. + */ + @Override + public void bind() throws IOException { + getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + getChannel().configureBlocking(false); + System.out.println("Bound UDP socket at port: " + port); + } - /** - * @return the receiver address. - */ - public SocketAddress getReceiver() { - return receiver; - } - - /** - * Sets the intended receiver address. This must be set when writing to the channel. - * @param receiver the receiver address. - */ - public void setReceiver(SocketAddress receiver) { - this.receiver = receiver; - } + /** + * Writes the pending {@link DatagramPacket} to the underlying channel sending data to the + * intended receiver of the packet. + */ + @Override + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; + getChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); + } - /** - * @return the underlying message that will be written on channel. - */ - public ByteBuffer getData() { - return data; - } - } -} \ No newline at end of file + /** + * Writes the outgoing {@link DatagramPacket} to the channel. The intended receiver of the + * datagram packet must be set in the data using + * {@link DatagramPacket#setReceiver(SocketAddress)}. + */ + @Override + public void write(Object data, SelectionKey key) { + super.write(data, key); + } + + /** + * Container of data used for {@link NioDatagramChannel} to communicate with remote peer. + */ + public static class DatagramPacket { + private SocketAddress sender; + private ByteBuffer data; + private SocketAddress receiver; + + /** + * Creates a container with underlying data. + * + * @param data the underlying message to be written on channel. + */ + public DatagramPacket(ByteBuffer data) { + this.data = data; + } + + /** + * @return the sender address. + */ + public SocketAddress getSender() { + return sender; + } + + /** + * Sets the sender address of this packet. + * + * @param sender the sender address. + */ + public void setSender(SocketAddress sender) { + this.sender = sender; + } + + /** + * @return the receiver address. + */ + public SocketAddress getReceiver() { + return receiver; + } + + /** + * Sets the intended receiver address. This must be set when writing to the channel. + * + * @param receiver the receiver address. + */ + public void setReceiver(SocketAddress receiver) { + this.receiver = receiver; + } + + /** + * @return the underlying message that will be written on channel. + */ + public ByteBuffer getData() { + return data; + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java index 273898ae3..89af20630 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -12,228 +12,225 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; + /** * This class acts as Synchronous Event De-multiplexer and Initiation Dispatcher of Reactor pattern. * Multiple handles i.e. {@link AbstractNioChannel}s can be registered to the reactor and it blocks - * for events from all these handles. Whenever an event occurs on any of the registered handles, - * it synchronously de-multiplexes the event which can be any of read, write or accept, and - * dispatches the event to the appropriate {@link ChannelHandler} using the {@link Dispatcher}. + * for events from all these handles. Whenever an event occurs on any of the registered handles, it + * synchronously de-multiplexes the event which can be any of read, write or accept, and dispatches + * the event to the appropriate {@link ChannelHandler} using the {@link Dispatcher}. * *

- * Implementation: - * A NIO reactor runs in its own thread when it is started using {@link #start()} method. - * {@link NioReactor} uses {@link Selector} for realizing Synchronous Event De-multiplexing. + * Implementation: A NIO reactor runs in its own thread when it is started using {@link #start()} + * method. {@link NioReactor} uses {@link Selector} for realizing Synchronous Event De-multiplexing. * *

- * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible edge cases - * which are required in a real application. This implementation is meant to demonstrate the fundamental - * concepts that lie behind Reactor pattern. + * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible + * edge cases which are required in a real application. This implementation is meant to demonstrate + * the fundamental concepts that lie behind Reactor pattern. * * @author npathai * */ public class NioReactor { - private Selector selector; - private Dispatcher dispatcher; - /** - * All the work of altering the SelectionKey operations and Selector operations are performed in - * the context of main event loop of reactor. So when any channel needs to change its readability - * or writability, a new command is added in the command queue and then the event loop picks up - * the command and executes it in next iteration. - */ - private Queue pendingCommands = new ConcurrentLinkedQueue<>(); - private ExecutorService reactorMain = Executors.newSingleThreadExecutor(); - - /** - * Creates a reactor which will use provided {@code dispatcher} to dispatch events. - * The application can provide various implementations of dispatcher which suits its - * needs. - * - * @param dispatcher a non-null dispatcher used to dispatch events on registered channels. - * @throws IOException if any I/O error occurs. - */ - public NioReactor(Dispatcher dispatcher) throws IOException { - this.dispatcher = dispatcher; - this.selector = Selector.open(); - } + private final Selector selector; + private final Dispatcher dispatcher; + /** + * All the work of altering the SelectionKey operations and Selector operations are performed in + * the context of main event loop of reactor. So when any channel needs to change its readability + * or writability, a new command is added in the command queue and then the event loop picks up + * the command and executes it in next iteration. + */ + private final Queue pendingCommands = new ConcurrentLinkedQueue<>(); + private final ExecutorService reactorMain = Executors.newSingleThreadExecutor(); - /** - * Starts the reactor event loop in a new thread. - * - * @throws IOException if any I/O error occurs. - */ - public void start() throws IOException { - reactorMain.execute(() -> { - try { - System.out.println("Reactor started, waiting for events..."); - eventLoop(); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } - - /** - * Stops the reactor and related resources such as dispatcher. - */ - public void stop() { - reactorMain.shutdownNow(); - selector.wakeup(); - try { - reactorMain.awaitTermination(4, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - dispatcher.stop(); - } + /** + * Creates a reactor which will use provided {@code dispatcher} to dispatch events. The + * application can provide various implementations of dispatcher which suits its needs. + * + * @param dispatcher a non-null dispatcher used to dispatch events on registered channels. + * @throws IOException if any I/O error occurs. + */ + public NioReactor(Dispatcher dispatcher) throws IOException { + this.dispatcher = dispatcher; + this.selector = Selector.open(); + } - /** - * Registers a new channel (handle) with this reactor. Reactor will start waiting for events on this channel - * and notify of any events. While registering the channel the reactor uses {@link AbstractNioChannel#getInterestedOps()} - * to know about the interested operation of this channel. - * - * @param channel a new channel on which reactor will wait for events. The channel must be bound - * prior to being registered. - * @return this - * @throws IOException if any I/O error occurs. - */ - public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { - SelectionKey key = channel.getChannel().register(selector, channel.getInterestedOps()); - key.attach(channel); - channel.setReactor(this); - return this; - } - - private void eventLoop() throws IOException { - while (true) { - - // honor interrupt request - if (Thread.interrupted()) { - break; - } - - // honor any pending commands first - processPendingCommands(); - - /* - * Synchronous event de-multiplexing happens here, this is blocking call which - * returns when it is possible to initiate non-blocking operation on any of the - * registered channels. - */ - selector.select(); - - /* - * Represents the events that have occurred on registered handles. - */ - Set keys = selector.selectedKeys(); + /** + * Starts the reactor event loop in a new thread. + * + * @throws IOException if any I/O error occurs. + */ + public void start() throws IOException { + reactorMain.execute(() -> { + try { + System.out.println("Reactor started, waiting for events..."); + eventLoop(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } - Iterator iterator = keys.iterator(); - - while (iterator.hasNext()) { - SelectionKey key = iterator.next(); - if (!key.isValid()) { - iterator.remove(); - continue; - } - processKey(key); - } - keys.clear(); - } - } - - private void processPendingCommands() { - Iterator iterator = pendingCommands.iterator(); - while (iterator.hasNext()) { - Runnable command = iterator.next(); - command.run(); - iterator.remove(); - } - } + /** + * Stops the reactor and related resources such as dispatcher. + * + * @throws InterruptedException if interrupted while stopping the reactor. + */ + public void stop() throws InterruptedException { + reactorMain.shutdownNow(); + selector.wakeup(); + reactorMain.awaitTermination(4, TimeUnit.SECONDS); + dispatcher.stop(); + } - /* - * Initiation dispatcher logic, it checks the type of event and notifier application - * specific event handler to handle the event. - */ - private void processKey(SelectionKey key) throws IOException { - if (key.isAcceptable()) { - onChannelAcceptable(key); - } else if (key.isReadable()) { - onChannelReadable(key); - } else if (key.isWritable()) { - onChannelWritable(key); - } - } + /** + * Registers a new channel (handle) with this reactor. Reactor will start waiting for events on + * this channel and notify of any events. While registering the channel the reactor uses + * {@link AbstractNioChannel#getInterestedOps()} to know about the interested operation of this + * channel. + * + * @param channel a new channel on which reactor will wait for events. The channel must be bound + * prior to being registered. + * @return this + * @throws IOException if any I/O error occurs. + */ + public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { + SelectionKey key = channel.getChannel().register(selector, channel.getInterestedOps()); + key.attach(channel); + channel.setReactor(this); + return this; + } - private void onChannelWritable(SelectionKey key) throws IOException { - AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); - channel.flush(key); - } + private void eventLoop() throws IOException { + while (true) { - private void onChannelReadable(SelectionKey key) { - try { - // reads the incoming data in context of reactor main loop. Can this be improved? - Object readObject = ((AbstractNioChannel)key.attachment()).read(key); + // honor interrupt request + if (Thread.interrupted()) { + break; + } - dispatchReadEvent(key, readObject); - } catch (IOException e) { - try { - key.channel().close(); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - } + // honor any pending commands first + processPendingCommands(); - /* - * Uses the application provided dispatcher to dispatch events to application handler. - */ - private void dispatchReadEvent(SelectionKey key, Object readObject) { - dispatcher.onChannelReadEvent((AbstractNioChannel)key.attachment(), readObject, key); - } + /* + * Synchronous event de-multiplexing happens here, this is blocking call which returns when it + * is possible to initiate non-blocking operation on any of the registered channels. + */ + selector.select(); - private void onChannelAcceptable(SelectionKey key) throws IOException { - ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); - SocketChannel socketChannel = serverSocketChannel.accept(); - socketChannel.configureBlocking(false); - SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); - readKey.attach(key.attachment()); - } + /* + * Represents the events that have occurred on registered handles. + */ + Set keys = selector.selectedKeys(); - /** - * Queues the change of operations request of a channel, which will change the interested - * operations of the channel sometime in future. - *

- * This is a non-blocking method and does not guarantee that the operations have changed when - * this method returns. - * - * @param key the key for which operations have to be changed. - * @param interestedOps the new interest operations. - */ - public void changeOps(SelectionKey key, int interestedOps) { - pendingCommands.add(new ChangeKeyOpsCommand(key, interestedOps)); - selector.wakeup(); - } - - /** - * A command that changes the interested operations of the key provided. - */ - class ChangeKeyOpsCommand implements Runnable { - private SelectionKey key; - private int interestedOps; - - public ChangeKeyOpsCommand(SelectionKey key, int interestedOps) { - this.key = key; - this.interestedOps = interestedOps; - } - - public void run() { - key.interestOps(interestedOps); - } - - @Override - public String toString() { - return "Change of ops to: " + interestedOps; - } - } -} \ No newline at end of file + Iterator iterator = keys.iterator(); + + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + if (!key.isValid()) { + iterator.remove(); + continue; + } + processKey(key); + } + keys.clear(); + } + } + + private void processPendingCommands() { + Iterator iterator = pendingCommands.iterator(); + while (iterator.hasNext()) { + Runnable command = iterator.next(); + command.run(); + iterator.remove(); + } + } + + /* + * Initiation dispatcher logic, it checks the type of event and notifier application specific + * event handler to handle the event. + */ + private void processKey(SelectionKey key) throws IOException { + if (key.isAcceptable()) { + onChannelAcceptable(key); + } else if (key.isReadable()) { + onChannelReadable(key); + } else if (key.isWritable()) { + onChannelWritable(key); + } + } + + private void onChannelWritable(SelectionKey key) throws IOException { + AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); + channel.flush(key); + } + + private void onChannelReadable(SelectionKey key) { + try { + // reads the incoming data in context of reactor main loop. Can this be improved? + Object readObject = ((AbstractNioChannel) key.attachment()).read(key); + + dispatchReadEvent(key, readObject); + } catch (IOException e) { + try { + key.channel().close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + + /* + * Uses the application provided dispatcher to dispatch events to application handler. + */ + private void dispatchReadEvent(SelectionKey key, Object readObject) { + dispatcher.onChannelReadEvent((AbstractNioChannel) key.attachment(), readObject, key); + } + + private void onChannelAcceptable(SelectionKey key) throws IOException { + ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); + SocketChannel socketChannel = serverSocketChannel.accept(); + socketChannel.configureBlocking(false); + SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); + readKey.attach(key.attachment()); + } + + /** + * Queues the change of operations request of a channel, which will change the interested + * operations of the channel sometime in future. + *

+ * This is a non-blocking method and does not guarantee that the operations have changed when this + * method returns. + * + * @param key the key for which operations have to be changed. + * @param interestedOps the new interest operations. + */ + public void changeOps(SelectionKey key, int interestedOps) { + pendingCommands.add(new ChangeKeyOpsCommand(key, interestedOps)); + selector.wakeup(); + } + + /** + * A command that changes the interested operations of the key provided. + */ + class ChangeKeyOpsCommand implements Runnable { + private SelectionKey key; + private int interestedOps; + + public ChangeKeyOpsCommand(SelectionKey key, int interestedOps) { + this.key = key; + this.interestedOps = interestedOps; + } + + public void run() { + key.interestOps(interestedOps); + } + + @Override + public String toString() { + return "Change of ops to: " + interestedOps; + } + } +} diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index ae54af643..17f47a394 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -9,81 +9,82 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; /** - * A wrapper over {@link NioServerSocketChannel} which can read and write data on a {@link SocketChannel}. + * A wrapper over {@link NioServerSocketChannel} which can read and write data on a + * {@link SocketChannel}. * * @author npathai */ public class NioServerSocketChannel extends AbstractNioChannel { - private int port; + private final int port; - /** - * Creates a {@link ServerSocketChannel} which will bind at provided port and use - * handler to handle incoming events on this channel. - *

- * Note the constructor does not bind the socket, {@link #bind()} method should be called for binding - * the socket. - * - * @param port the port on which channel will be bound to accept incoming connection requests. - * @param handler the handler that will handle incoming requests on this channel. - * @throws IOException if any I/O error occurs. - */ - public NioServerSocketChannel(int port, ChannelHandler handler) throws IOException { - super(handler, ServerSocketChannel.open()); - this.port = port; - } + /** + * Creates a {@link ServerSocketChannel} which will bind at provided port and use + * handler to handle incoming events on this channel. + *

+ * Note the constructor does not bind the socket, {@link #bind()} method should be called for + * binding the socket. + * + * @param port the port on which channel will be bound to accept incoming connection requests. + * @param handler the handler that will handle incoming requests on this channel. + * @throws IOException if any I/O error occurs. + */ + public NioServerSocketChannel(int port, ChannelHandler handler) throws IOException { + super(handler, ServerSocketChannel.open()); + this.port = port; + } - - @Override - public int getInterestedOps() { - // being a server socket channel it is interested in accepting connection from remote peers. - return SelectionKey.OP_ACCEPT; - } - /** - * @return the underlying {@link ServerSocketChannel}. - */ - @Override - public ServerSocketChannel getChannel() { - return (ServerSocketChannel) super.getChannel(); - } - - /** - * Reads and returns {@link ByteBuffer} from the underlying {@link SocketChannel} represented by - * the key. Due to the fact that there is a dedicated channel for each client connection - * we don't need to store the sender. - */ - @Override - public ByteBuffer read(SelectionKey key) throws IOException { - SocketChannel socketChannel = (SocketChannel) key.channel(); - ByteBuffer buffer = ByteBuffer.allocate(1024); - int read = socketChannel.read(buffer); - buffer.flip(); - if (read == -1) { - throw new IOException("Socket closed"); - } - return buffer; - } + @Override + public int getInterestedOps() { + // being a server socket channel it is interested in accepting connection from remote peers. + return SelectionKey.OP_ACCEPT; + } - /** - * Binds TCP socket on the provided port. - * - * @throws IOException if any I/O error occurs. - */ - @Override - public void bind() throws IOException { - ((ServerSocketChannel)getChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); - ((ServerSocketChannel)getChannel()).configureBlocking(false); - System.out.println("Bound TCP socket at port: " + port); - } + /** + * @return the underlying {@link ServerSocketChannel}. + */ + @Override + public ServerSocketChannel getChannel() { + return (ServerSocketChannel) super.getChannel(); + } - /** - * Writes the pending {@link ByteBuffer} to the underlying channel sending data to - * the intended receiver of the packet. - */ - @Override - protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { - ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; - ((SocketChannel)key.channel()).write(pendingBuffer); - } + /** + * Reads and returns {@link ByteBuffer} from the underlying {@link SocketChannel} represented by + * the key. Due to the fact that there is a dedicated channel for each client + * connection we don't need to store the sender. + */ + @Override + public ByteBuffer read(SelectionKey key) throws IOException { + SocketChannel socketChannel = (SocketChannel) key.channel(); + ByteBuffer buffer = ByteBuffer.allocate(1024); + int read = socketChannel.read(buffer); + buffer.flip(); + if (read == -1) { + throw new IOException("Socket closed"); + } + return buffer; + } + + /** + * Binds TCP socket on the provided port. + * + * @throws IOException if any I/O error occurs. + */ + @Override + public void bind() throws IOException { + ((ServerSocketChannel) getChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + ((ServerSocketChannel) getChannel()).configureBlocking(false); + System.out.println("Bound TCP socket at port: " + port); + } + + /** + * Writes the pending {@link ByteBuffer} to the underlying channel sending data to the intended + * receiver of the packet. + */ + @Override + protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { + ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; + ((SocketChannel) key.channel()).write(pendingBuffer); + } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java index b5392ac8f..baacda9f3 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java @@ -4,8 +4,8 @@ import java.nio.channels.SelectionKey; /** * Dispatches the events in the context of caller thread. This implementation is a good fit for - * small applications where there are limited clients. Using this implementation limits the scalability - * because the I/O thread performs the application specific processing. + * small applications where there are limited clients. Using this implementation limits the + * scalability because the I/O thread performs the application specific processing. * *

* For better performance use {@link ThreadPoolDispatcher}. @@ -16,28 +16,25 @@ import java.nio.channels.SelectionKey; */ public class SameThreadDispatcher implements Dispatcher { - /** - * Dispatches the read event in the context of caller thread. - *
- * Note this is a blocking call. It returns only after the associated handler has handled the - * read event. - */ - @Override - public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { - if (channel.getHandler() != null) { - /* - * Calls the associated handler to notify the read event where application specific code - * resides. - */ - channel.getHandler().handleChannelRead(channel, readObject, key); - } - } + /** + * Dispatches the read event in the context of caller thread.
+ * Note this is a blocking call. It returns only after the associated handler has handled the read + * event. + */ + @Override + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { + /* + * Calls the associated handler to notify the read event where application specific code + * resides. + */ + channel.getHandler().handleChannelRead(channel, readObject, key); + } - /** - * No resources to free. - */ - @Override - public void stop() { - // no-op - } + /** + * No resources to free. + */ + @Override + public void stop() { + // no-op + } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java index 8624b878e..9fd539adb 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java @@ -6,50 +6,45 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** - * An implementation that uses a pool of worker threads to dispatch the events. This provides - * better scalability as the application specific processing is not performed in the context - * of I/O (reactor) thread. + * An implementation that uses a pool of worker threads to dispatch the events. This provides better + * scalability as the application specific processing is not performed in the context of I/O + * (reactor) thread. * * @author npathai * */ -public class ThreadPoolDispatcher extends SameThreadDispatcher { +public class ThreadPoolDispatcher implements Dispatcher { - private ExecutorService executorService; + private final ExecutorService executorService; - /** - * Creates a pooled dispatcher with tunable pool size. - * - * @param poolSize number of pooled threads - */ - public ThreadPoolDispatcher(int poolSize) { - this.executorService = Executors.newFixedThreadPool(poolSize); - } + /** + * Creates a pooled dispatcher with tunable pool size. + * + * @param poolSize number of pooled threads + */ + public ThreadPoolDispatcher(int poolSize) { + this.executorService = Executors.newFixedThreadPool(poolSize); + } - /** - * Submits the work of dispatching the read event to worker pool, where it gets picked - * up by worker threads. - *
- * Note that this is a non-blocking call and returns immediately. It is not guaranteed - * that the event has been handled by associated handler. - */ - @Override - public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { - executorService.execute(() -> - ThreadPoolDispatcher.super.onChannelReadEvent(channel, readObject, key)); - } - - /** - * Stops the pool of workers. - */ - @Override - public void stop() { - executorService.shutdownNow(); - try { - executorService.awaitTermination(4, TimeUnit.SECONDS); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + /** + * Submits the work of dispatching the read event to worker pool, where it gets picked up by + * worker threads.
+ * Note that this is a non-blocking call and returns immediately. It is not guaranteed that the + * event has been handled by associated handler. + */ + @Override + public void onChannelReadEvent(AbstractNioChannel channel, Object readObject, SelectionKey key) { + executorService.execute(() -> channel.getHandler().handleChannelRead(channel, readObject, key)); + } + /** + * Stops the pool of workers. + * + * @throws InterruptedException if interrupted while stopping pool of workers. + */ + @Override + public void stop() throws InterruptedException { + executorService.shutdown(); + executorService.awaitTermination(4, TimeUnit.SECONDS); + } } diff --git a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java index 9447aac01..bc51e26de 100644 --- a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java +++ b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java @@ -4,24 +4,38 @@ import java.io.IOException; import org.junit.Test; +/** + * + * This class tests the Distributed Logging service by starting a Reactor and then sending it + * concurrent logging requests using multiple clients. + * + * @author npathai + */ public class AppTest { - @Test - public void testApp() throws IOException { - App app = new App(); - app.start(); - - AppClient client = new AppClient(); - client.start(); - - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - - client.stop(); - - app.stop(); - } + /** + * Test the application. + * + * @throws IOException if any I/O error occurs. + * @throws InterruptedException if interrupted while stopping the application. + */ + @Test + public void testApp() throws IOException, InterruptedException { + App app = new App(); + app.start(); + + AppClient client = new AppClient(); + client.start(); + + // allow clients to send requests. Artificial delay. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + client.stop(); + + app.stop(); + } } From 2dc98f6b93dcdedbb911fb668a6d45e26afbf5c6 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Mon, 14 Sep 2015 07:05:01 +0300 Subject: [PATCH 35/80] Upgrade Coveralls plugin to 4.0.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5bfacf287..70a164c06 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 1.4.188 4.12 3.0 - 3.1.0 + 4.0.0 0.7.2.201409121644 1.4 2.15.3 From 2ff78184e54883b54d05e83e50f47334b22f8eaa Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 14 Sep 2015 12:56:17 +0530 Subject: [PATCH 36/80] Work on #74, added class diagram and index.md --- reactor/etc/reactor.png | Bin 0 -> 124359 bytes reactor/etc/reactor.ucls | 207 +++++++++++++++++++++++++++++++++++++++ reactor/index.md | 30 ++++++ 3 files changed, 237 insertions(+) create mode 100644 reactor/etc/reactor.png create mode 100644 reactor/etc/reactor.ucls create mode 100644 reactor/index.md diff --git a/reactor/etc/reactor.png b/reactor/etc/reactor.png new file mode 100644 index 0000000000000000000000000000000000000000..0b00ec98be5ccdbeeb74d71241de065e3d733554 GIT binary patch literal 124359 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 literal 0 HcmV?d00001 diff --git a/reactor/etc/reactor.ucls b/reactor/etc/reactor.ucls new file mode 100644 index 000000000..d072e4029 --- /dev/null +++ b/reactor/etc/reactor.ucls @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reactor/index.md b/reactor/index.md new file mode 100644 index 000000000..7333c74dd --- /dev/null +++ b/reactor/index.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Reactor +folder: reactor +permalink: /patterns/reactor/ +categories: Architectural +tags: + - Java + - Difficulty-Expert +--- + +**Intent:** The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. The application can register specific handlers for processing which are called by reactor on specific events. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer. + +![Reactor](./etc/reactor.png "Reactor") + +**Applicability:** Use Reactor pattern when + +* a server application needs to handle concurrent service requests from multiple clients. +* a server application needs to be available for receiving requests from new clients even when handling older client requests. +* a server must maximize throughput, minimize latency and use CPU efficiently without blocking. + +**Real world examples:** + +* [Spring Reactor](http://projectreactor.io/) + +**Credits** + +* [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf) +* [Doug Lea - Scalable IO in Java](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf) +* [Netty](http://netty.io/) From e6a4200607614e25aa2457589cc0f65e390c24f3 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Tue, 15 Sep 2015 13:48:58 +0530 Subject: [PATCH 37/80] Work on #74, increased coverage --- .../java/com/iluwatar/reactor/app/App.java | 23 ++++++++---- .../com/iluwatar/reactor/app/AppTest.java | 35 +++++++++++++++++-- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java index fcc327b34..975435712 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -1,9 +1,12 @@ package com.iluwatar.reactor.app; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import com.iluwatar.reactor.framework.AbstractNioChannel; import com.iluwatar.reactor.framework.ChannelHandler; +import com.iluwatar.reactor.framework.Dispatcher; import com.iluwatar.reactor.framework.NioDatagramChannel; import com.iluwatar.reactor.framework.NioReactor; import com.iluwatar.reactor.framework.NioServerSocketChannel; @@ -64,6 +67,7 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher; public class App { private NioReactor reactor; + private List channels = new ArrayList<>(); /** * App entry. @@ -71,19 +75,20 @@ public class App { * @throws IOException */ public static void main(String[] args) throws IOException { - new App().start(); + new App().start(new ThreadPoolDispatcher(2)); } /** * Starts the NIO reactor. + * @param threadPoolDispatcher * * @throws IOException if any channel fails to bind. */ - public void start() throws IOException { + public void start(Dispatcher dispatcher) throws IOException { /* * The application can customize its event dispatching mechanism. */ - reactor = new NioReactor(new ThreadPoolDispatcher(2)); + reactor = new NioReactor(dispatcher); /* * This represents application specific business logic that dispatcher will call on appropriate @@ -103,20 +108,26 @@ public class App { * Stops the NIO reactor. This is a blocking call. * * @throws InterruptedException if interrupted while stopping the reactor. + * @throws IOException if any I/O error occurs */ - public void stop() throws InterruptedException { + public void stop() throws InterruptedException, IOException { reactor.stop(); + for (AbstractNioChannel channel : channels) { + channel.getChannel().close(); + } } - private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { + private AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); channel.bind(); + channels.add(channel); return channel; } - private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { + private AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { NioDatagramChannel channel = new NioDatagramChannel(port, handler); channel.bind(); + channels.add(channel); return channel; } } diff --git a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java index bc51e26de..2ac9b448a 100644 --- a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java +++ b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java @@ -4,6 +4,9 @@ import java.io.IOException; import org.junit.Test; +import com.iluwatar.reactor.framework.SameThreadDispatcher; +import com.iluwatar.reactor.framework.ThreadPoolDispatcher; + /** * * This class tests the Distributed Logging service by starting a Reactor and then sending it @@ -14,15 +17,41 @@ import org.junit.Test; public class AppTest { /** - * Test the application. + * Test the application using pooled thread dispatcher. * * @throws IOException if any I/O error occurs. * @throws InterruptedException if interrupted while stopping the application. */ @Test - public void testApp() throws IOException, InterruptedException { + public void testAppUsingThreadPoolDispatcher() throws IOException, InterruptedException { App app = new App(); - app.start(); + app.start(new ThreadPoolDispatcher(2)); + + AppClient client = new AppClient(); + client.start(); + + // allow clients to send requests. Artificial delay. + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + client.stop(); + + app.stop(); + } + + /** + * Test the application using same thread dispatcher. + * + * @throws IOException if any I/O error occurs. + * @throws InterruptedException if interrupted while stopping the application. + */ + @Test + public void testAppUsingSameThreadDispatcher() throws IOException, InterruptedException { + App app = new App(); + app.start(new SameThreadDispatcher()); AppClient client = new AppClient(); client.start(); From dbc2acae5ffa2d3770c98f9161a97010379f573e Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 16 Sep 2015 11:39:57 +0530 Subject: [PATCH 38/80] Work on #74, removed author name from all classes. [ci skip]. Author names were added due to default eclipse configuration. --- reactor/src/main/java/com/iluwatar/reactor/app/App.java | 2 -- reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java | 2 -- .../src/main/java/com/iluwatar/reactor/app/LoggingHandler.java | 2 -- .../com/iluwatar/reactor/framework/AbstractNioChannel.java | 3 --- .../java/com/iluwatar/reactor/framework/ChannelHandler.java | 2 -- .../main/java/com/iluwatar/reactor/framework/Dispatcher.java | 2 -- .../com/iluwatar/reactor/framework/NioDatagramChannel.java | 2 -- .../main/java/com/iluwatar/reactor/framework/NioReactor.java | 3 --- .../com/iluwatar/reactor/framework/NioServerSocketChannel.java | 2 -- .../com/iluwatar/reactor/framework/SameThreadDispatcher.java | 2 -- .../com/iluwatar/reactor/framework/ThreadPoolDispatcher.java | 3 --- reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java | 2 -- 12 files changed, 27 deletions(-) diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java index 975435712..5c6d91ee8 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -61,8 +61,6 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher; *

* The example uses Java NIO framework to implement the Reactor. * - * @author npathai - * */ public class App { diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java index c50e4d3e7..659f5da21 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -17,8 +17,6 @@ import java.util.concurrent.TimeUnit; /** * Represents the clients of Reactor pattern. Multiple clients are run concurrently and send logging * requests to Reactor. - * - * @author npathai */ public class AppClient { private final ExecutorService service = Executors.newFixedThreadPool(4); diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java index 1f2694b0b..0845303df 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -10,8 +10,6 @@ import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; /** * Logging server application logic. It logs the incoming requests on standard console and returns a * canned acknowledgement back to the remote peer. - * - * @author npathai */ public class LoggingHandler implements ChannelHandler { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java index 09f308731..4abacd86f 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -19,9 +19,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; * concrete implementation. It provides a block writing mechanism wherein when any * {@link ChannelHandler} wants to write data back, it queues the data in pending write queue and * clears it in block manner. This provides better throughput. - * - * @author npathai - * */ public abstract class AbstractNioChannel { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java index a4a392a34..381738ecd 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java @@ -9,8 +9,6 @@ import java.nio.channels.SelectionKey; *

* A {@link ChannelHandler} can be associated with one or many {@link AbstractNioChannel}s, and * whenever an event occurs on any of the associated channels, the handler is notified of the event. - * - * @author npathai */ public interface ChannelHandler { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java index 0ed53f8fc..78aeb84df 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java @@ -16,8 +16,6 @@ import java.nio.channels.SelectionKey; * * @see SameThreadDispatcher * @see ThreadPoolDispatcher - * - * @author npathai */ public interface Dispatcher { /** diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java index 089911d10..b55480ffc 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -10,8 +10,6 @@ import java.nio.channels.SelectionKey; /** * A wrapper over {@link DatagramChannel} which can read and write data on a DatagramChannel. - * - * @author npathai */ public class NioDatagramChannel extends AbstractNioChannel { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java index 89af20630..b818612e5 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -28,9 +28,6 @@ import java.util.concurrent.TimeUnit; * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible * edge cases which are required in a real application. This implementation is meant to demonstrate * the fundamental concepts that lie behind Reactor pattern. - * - * @author npathai - * */ public class NioReactor { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index 17f47a394..c635a6076 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -11,8 +11,6 @@ import java.nio.channels.SocketChannel; /** * A wrapper over {@link NioServerSocketChannel} which can read and write data on a * {@link SocketChannel}. - * - * @author npathai */ public class NioServerSocketChannel extends AbstractNioChannel { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java index baacda9f3..ae995428e 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java @@ -11,8 +11,6 @@ import java.nio.channels.SelectionKey; * For better performance use {@link ThreadPoolDispatcher}. * * @see ThreadPoolDispatcher - * - * @author npathai */ public class SameThreadDispatcher implements Dispatcher { diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java index 9fd539adb..4a240659e 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java @@ -9,9 +9,6 @@ import java.util.concurrent.TimeUnit; * An implementation that uses a pool of worker threads to dispatch the events. This provides better * scalability as the application specific processing is not performed in the context of I/O * (reactor) thread. - * - * @author npathai - * */ public class ThreadPoolDispatcher implements Dispatcher { diff --git a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java index 2ac9b448a..752192ef3 100644 --- a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java +++ b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java @@ -11,8 +11,6 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher; * * This class tests the Distributed Logging service by starting a Reactor and then sending it * concurrent logging requests using multiple clients. - * - * @author npathai */ public class AppTest { From 570a30099e9867d2bb10ed4610ab27ee446831e5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 16 Sep 2015 13:24:21 +0530 Subject: [PATCH 39/80] Some changes to further polish #74 --- reactor/etc/reactor.png | Bin 124359 -> 125804 bytes reactor/etc/reactor.ucls | 146 +++++++++++------- .../java/com/iluwatar/reactor/app/App.java | 19 ++- .../reactor/framework/AbstractNioChannel.java | 2 +- .../reactor/framework/NioDatagramChannel.java | 10 +- .../reactor/framework/NioReactor.java | 7 +- .../framework/NioServerSocketChannel.java | 8 +- .../com/iluwatar/reactor/app/AppTest.java | 8 +- 8 files changed, 119 insertions(+), 81 deletions(-) diff --git a/reactor/etc/reactor.png b/reactor/etc/reactor.png index 0b00ec98be5ccdbeeb74d71241de065e3d733554..abe705682f6c912858dcac8433e2495a1fec4fd3 100644 GIT binary patch literal 125804 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 literal 124359 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 diff --git a/reactor/etc/reactor.ucls b/reactor/etc/reactor.ucls index d072e4029..90e28cdd7 100644 --- a/reactor/etc/reactor.ucls +++ b/reactor/etc/reactor.ucls @@ -124,80 +124,108 @@ - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + + + + + - - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java index 5c6d91ee8..7bb01ddc8 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -66,6 +66,15 @@ public class App { private NioReactor reactor; private List channels = new ArrayList<>(); + private Dispatcher dispatcher; + + /** + * Creates an instance of App which will use provided dispatcher for dispatching events on reactor. + * @param dispatcher the dispatcher that will be used to dispatch events. + */ + public App(Dispatcher dispatcher) { + this.dispatcher = dispatcher; + } /** * App entry. @@ -73,16 +82,15 @@ public class App { * @throws IOException */ public static void main(String[] args) throws IOException { - new App().start(new ThreadPoolDispatcher(2)); + new App(new ThreadPoolDispatcher(2)).start(); } /** * Starts the NIO reactor. - * @param threadPoolDispatcher - * + * * @throws IOException if any channel fails to bind. */ - public void start(Dispatcher dispatcher) throws IOException { + public void start() throws IOException { /* * The application can customize its event dispatching mechanism. */ @@ -110,8 +118,9 @@ public class App { */ public void stop() throws InterruptedException, IOException { reactor.stop(); + dispatcher.stop(); for (AbstractNioChannel channel : channels) { - channel.getChannel().close(); + channel.getJavaChannel().close(); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java index 4abacd86f..9f2f8a95c 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -48,7 +48,7 @@ public abstract class AbstractNioChannel { /** * @return the wrapped NIO channel. */ - public SelectableChannel getChannel() { + public SelectableChannel getJavaChannel() { return channel; } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java index b55480ffc..a2ff3d3d8 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -64,8 +64,8 @@ public class NioDatagramChannel extends AbstractNioChannel { * @return the underlying datagram channel. */ @Override - public DatagramChannel getChannel() { - return (DatagramChannel) super.getChannel(); + public DatagramChannel getJavaChannel() { + return (DatagramChannel) super.getJavaChannel(); } /** @@ -75,8 +75,8 @@ public class NioDatagramChannel extends AbstractNioChannel { */ @Override public void bind() throws IOException { - getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); - getChannel().configureBlocking(false); + getJavaChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + getJavaChannel().configureBlocking(false); System.out.println("Bound UDP socket at port: " + port); } @@ -87,7 +87,7 @@ public class NioDatagramChannel extends AbstractNioChannel { @Override protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; - getChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); + getJavaChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); } /** diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java index b818612e5..16c13e5f9 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -74,12 +74,13 @@ public class NioReactor { * Stops the reactor and related resources such as dispatcher. * * @throws InterruptedException if interrupted while stopping the reactor. + * @throws IOException if any I/O error occurs. */ - public void stop() throws InterruptedException { + public void stop() throws InterruptedException, IOException { reactorMain.shutdownNow(); selector.wakeup(); reactorMain.awaitTermination(4, TimeUnit.SECONDS); - dispatcher.stop(); + selector.close(); } /** @@ -94,7 +95,7 @@ public class NioReactor { * @throws IOException if any I/O error occurs. */ public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { - SelectionKey key = channel.getChannel().register(selector, channel.getInterestedOps()); + SelectionKey key = channel.getJavaChannel().register(selector, channel.getInterestedOps()); key.attach(channel); channel.setReactor(this); return this; diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index c635a6076..c5caaa7ff 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -43,8 +43,8 @@ public class NioServerSocketChannel extends AbstractNioChannel { * @return the underlying {@link ServerSocketChannel}. */ @Override - public ServerSocketChannel getChannel() { - return (ServerSocketChannel) super.getChannel(); + public ServerSocketChannel getJavaChannel() { + return (ServerSocketChannel) super.getJavaChannel(); } /** @@ -71,8 +71,8 @@ public class NioServerSocketChannel extends AbstractNioChannel { */ @Override public void bind() throws IOException { - ((ServerSocketChannel) getChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); - ((ServerSocketChannel) getChannel()).configureBlocking(false); + ((ServerSocketChannel) getJavaChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + ((ServerSocketChannel) getJavaChannel()).configureBlocking(false); System.out.println("Bound TCP socket at port: " + port); } diff --git a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java index 752192ef3..9abb4e690 100644 --- a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java +++ b/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java @@ -22,8 +22,8 @@ public class AppTest { */ @Test public void testAppUsingThreadPoolDispatcher() throws IOException, InterruptedException { - App app = new App(); - app.start(new ThreadPoolDispatcher(2)); + App app = new App(new ThreadPoolDispatcher(2)); + app.start(); AppClient client = new AppClient(); client.start(); @@ -48,8 +48,8 @@ public class AppTest { */ @Test public void testAppUsingSameThreadDispatcher() throws IOException, InterruptedException { - App app = new App(); - app.start(new SameThreadDispatcher()); + App app = new App(new SameThreadDispatcher()); + app.start(); AppClient client = new AppClient(); client.start(); From dd0fcea09094b885f4f58fed7ef15ae7a63bb8a7 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Fri, 18 Sep 2015 19:59:52 +0300 Subject: [PATCH 40/80] Package naming corrections and cleanup. --- fluentinterface/etc/fluentinterface.ucls | 36 +++++------ .../fluentinterface/{ => app}/App.java | 2 +- .../fluentinterface/{ => app}/AppTest.java | 4 +- .../view/controller/with/observer/App.java | 29 --------- .../controller/with/observer/Fatigue.java | 22 ------- .../with/observer/GiantController.java | 46 -------------- .../controller/with/observer/GiantModel.java | 63 ------------------- .../with/observer/GiantModelObserver.java | 12 ---- .../controller/with/observer/GiantView.java | 18 ------ .../view/controller/with/observer/Health.java | 22 ------- .../controller/with/observer/Nourishment.java | 22 ------- .../controller/with/observer/AppTest.java | 19 ------ .../com/iluwatar/servicelayer/app/App.java | 6 +- .../servicelayer/hibernate/HibernateUtil.java | 2 +- .../servicelayer/magic/MagicService.java | 2 +- .../servicelayer/magic/MagicServiceImpl.java | 4 +- .../{servicelayer => }/spell/Spell.java | 2 +- .../{servicelayer => }/spell/SpellDao.java | 2 +- .../spell/SpellDaoImpl.java | 2 +- .../servicelayer/spellbook/Spellbook.java | 2 +- 20 files changed, 33 insertions(+), 284 deletions(-) rename fluentinterface/src/main/java/com/iluwatar/fluentinterface/{ => app}/App.java (98%) rename fluentinterface/src/test/java/com/iluwatar/fluentinterface/{ => app}/AppTest.java (59%) delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/App.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Fatigue.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantController.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModel.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModelObserver.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantView.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Health.java delete mode 100644 model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Nourishment.java delete mode 100644 model-view-controller/src/test/java/com/iluwatar/model/view/controller/with/observer/AppTest.java rename service-layer/src/main/java/com/iluwatar/servicelayer/{servicelayer => }/spell/Spell.java (95%) rename service-layer/src/main/java/com/iluwatar/servicelayer/{servicelayer => }/spell/SpellDao.java (75%) rename service-layer/src/main/java/com/iluwatar/servicelayer/{servicelayer => }/spell/SpellDaoImpl.java (94%) diff --git a/fluentinterface/etc/fluentinterface.ucls b/fluentinterface/etc/fluentinterface.ucls index aab2c9ad7..e30c45bb2 100644 --- a/fluentinterface/etc/fluentinterface.ucls +++ b/fluentinterface/etc/fluentinterface.ucls @@ -1,8 +1,8 @@ - + @@ -55,7 +55,7 @@ + file="C:/Program Files/Java/jdk1.8.0_25/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT"> @@ -64,33 +64,33 @@ - - - - - - - - - - - - - + - + - + + + + + + + + + + + + + diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java similarity index 98% rename from fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java rename to fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java index f7352fe39..81a1ccb6a 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java @@ -1,4 +1,4 @@ -package com.iluwatar.fluentinterface; +package com.iluwatar.fluentinterface.app; import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable; diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java similarity index 59% rename from fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java rename to fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java index d0abb7bf1..29ad885c0 100644 --- a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/AppTest.java +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java @@ -1,7 +1,9 @@ -package com.iluwatar.fluentinterface; +package com.iluwatar.fluentinterface.app; import org.junit.Test; +import com.iluwatar.fluentinterface.app.App; + public class AppTest { @Test diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/App.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/App.java deleted file mode 100644 index 0b74f4b5a..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/App.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * In this second example the model-view relationship is different. This time we use the Observer pattern to notify - * the {@link GiantView} each time the {@link GiantModel} is changed. This way the {@link GiantController} responsibilities - * are narrowed and it only needs to modify the {@link GiantModel} according to the user input. - * - */ -public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - // create model, view and controller - GiantModel giant = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); - GiantView view = new GiantView(); - GiantController controller = new GiantController(giant, view); - // initial display - controller.updateView(); - // controller receives some interactions that affect the giant - // model modifications trigger the view rendering automatically - controller.setHealth(Health.WOUNDED); - controller.setNourishment(Nourishment.HUNGRY); - controller.setFatigue(Fatigue.TIRED); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Fatigue.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Fatigue.java deleted file mode 100644 index 301a579fb..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Fatigue.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * Fatigue enumeration - * - */ -public enum Fatigue { - - ALERT("alert"), TIRED("tired"), SLEEPING("sleeping"); - - private String title; - - Fatigue(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantController.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantController.java deleted file mode 100644 index e892b2946..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantController.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * GiantController updates the giant model. - * - */ -public class GiantController { - - private GiantModel giant; - private GiantView view; - - public GiantController(GiantModel giant, GiantView view) { - this.giant = giant; - this.view = view; - this.giant.registerObserver(this.view); - } - - public Health getHealth() { - return giant.getHealth(); - } - - public void setHealth(Health health) { - this.giant.setHealth(health); - } - - public Fatigue getFatigue() { - return giant.getFatigue(); - } - - public void setFatigue(Fatigue fatigue) { - this.giant.setFatigue(fatigue); - } - - public Nourishment getNourishment() { - return giant.getNourishment(); - } - - public void setNourishment(Nourishment nourishment) { - this.giant.setNourishment(nourishment); - } - - public void updateView() { - this.view.displayGiant(giant); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModel.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModel.java deleted file mode 100644 index 84b32a50a..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModel.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * GiantModel contains the giant data. - * - */ -public class GiantModel { - - private Health health; - private Fatigue fatigue; - private Nourishment nourishment; - private List observers = new ArrayList<>(); - - GiantModel(Health health, Fatigue fatigue, Nourishment nourishment) { - this.health = health; - this.fatigue = fatigue; - this.nourishment = nourishment; - } - - public Health getHealth() { - return health; - } - - public void setHealth(Health health) { - this.health = health; - notifyObservers(); - } - - public Fatigue getFatigue() { - return fatigue; - } - - public void setFatigue(Fatigue fatigue) { - this.fatigue = fatigue; - notifyObservers(); - } - - public Nourishment getNourishment() { - return nourishment; - } - - public void setNourishment(Nourishment nourishment) { - this.nourishment = nourishment; - notifyObservers(); - } - - @Override - public String toString() { - return String.format("The giant looks %s, %s and %s.", health, fatigue, nourishment); - } - - public void registerObserver(GiantModelObserver observer) { - observers.add(observer); - } - - private void notifyObservers() { - observers.stream().forEach((GiantModelObserver o) -> o.modelChanged(this)); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModelObserver.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModelObserver.java deleted file mode 100644 index 6363ef4f7..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantModelObserver.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * GiantModelObserver is the interface for delivering update notifications. - * - */ -public interface GiantModelObserver { - - void modelChanged(GiantModel model); - -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantView.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantView.java deleted file mode 100644 index f4cebad80..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/GiantView.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * GiantView displays the giant - * - */ -public class GiantView implements GiantModelObserver { - - public void displayGiant(GiantModel giant) { - System.out.println(giant); - } - - @Override - public void modelChanged(GiantModel model) { - displayGiant(model); - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Health.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Health.java deleted file mode 100644 index e4b8ed9a7..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Health.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * Health enumeration - * - */ -public enum Health { - - HEALTHY("healthy"), WOUNDED("wounded"), DEAD("dead"); - - private String title; - - Health(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Nourishment.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Nourishment.java deleted file mode 100644 index c1a8253c3..000000000 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/with/observer/Nourishment.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -/** - * - * Nourishment enumeration - * - */ -public enum Nourishment { - - SATURATED("saturated"), HUNGRY("hungry"), STARVING("starving"); - - private String title; - - Nourishment(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } -} diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/with/observer/AppTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/with/observer/AppTest.java deleted file mode 100644 index 9a43ce7bc..000000000 --- a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/with/observer/AppTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.iluwatar.model.view.controller.with.observer; - -import org.junit.Test; - -import com.iluwatar.model.view.controller.with.observer.App; - -/** - * - * Application test - * - */ -public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } -} diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java index 2d91b43d9..47eb5231e 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java @@ -4,9 +4,9 @@ import java.util.List; import com.iluwatar.servicelayer.magic.MagicService; import com.iluwatar.servicelayer.magic.MagicServiceImpl; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; -import com.iluwatar.servicelayer.servicelayer.spell.SpellDao; -import com.iluwatar.servicelayer.servicelayer.spell.SpellDaoImpl; +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spell.SpellDaoImpl; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.spellbook.SpellbookDao; import com.iluwatar.servicelayer.spellbook.SpellbookDaoImpl; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java index 70d586c08..61d0f5d5d 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java @@ -3,7 +3,7 @@ package com.iluwatar.servicelayer.hibernate; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.wizard.Wizard; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java index a6046742b..38742c78d 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java @@ -2,7 +2,7 @@ package com.iluwatar.servicelayer.magic; import java.util.List; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.wizard.Wizard; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java index e5599fc4e..37249894a 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java @@ -3,8 +3,8 @@ package com.iluwatar.servicelayer.magic; import java.util.ArrayList; import java.util.List; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; -import com.iluwatar.servicelayer.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.spellbook.SpellbookDao; import com.iluwatar.servicelayer.wizard.Wizard; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/Spell.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java similarity index 95% rename from service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/Spell.java rename to service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java index 5dc7a6acc..ed166eccc 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/Spell.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java @@ -1,4 +1,4 @@ -package com.iluwatar.servicelayer.servicelayer.spell; +package com.iluwatar.servicelayer.spell; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java similarity index 75% rename from service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDao.java rename to service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java index 079ef39a0..a59307e7f 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java @@ -1,4 +1,4 @@ -package com.iluwatar.servicelayer.servicelayer.spell; +package com.iluwatar.servicelayer.spell; import com.iluwatar.servicelayer.common.Dao; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java similarity index 94% rename from service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDaoImpl.java rename to service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java index d8655ac3a..d278defe2 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/servicelayer/spell/SpellDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java @@ -1,4 +1,4 @@ -package com.iluwatar.servicelayer.servicelayer.spell; +package com.iluwatar.servicelayer.spell; import org.hibernate.Criteria; import org.hibernate.Session; diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java index 5bf42c7ed..c596c9926 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java @@ -13,7 +13,7 @@ import javax.persistence.OneToMany; import javax.persistence.Table; import com.iluwatar.servicelayer.common.BaseEntity; -import com.iluwatar.servicelayer.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.wizard.Wizard; /** From b3e4e8a47bf5f88277029534456334d9e2c59432 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Tue, 22 Sep 2015 18:25:56 +0530 Subject: [PATCH 41/80] Work on #226, #213, added references of Gang Of Four to all GoF patterns, added tag for Gang Of Four, added difficulty tags to some of them --- abstract-factory/index.md | 8 +++++++- adapter/index.md | 8 +++++++- bridge/index.md | 8 +++++++- builder/index.md | 8 +++++++- chain/index.md | 8 +++++++- command/index.md | 8 +++++++- composite/index.md | 8 +++++++- decorator/index.md | 8 +++++++- facade/index.md | 8 +++++++- factory-method/index.md | 5 +++++ flyweight/index.md | 8 +++++++- interpreter/index.md | 8 +++++++- iterator/index.md | 9 ++++++++- mediator/index.md | 9 ++++++++- memento/index.md | 8 +++++++- observer/index.md | 9 ++++++++- prototype/index.md | 8 +++++++- proxy/index.md | 9 ++++++++- singleton/index.md | 9 ++++++++- state/index.md | 9 ++++++++- strategy/index.md | 9 ++++++++- template-method/index.md | 9 ++++++++- visitor/index.md | 9 ++++++++- 23 files changed, 168 insertions(+), 22 deletions(-) diff --git a/abstract-factory/index.md b/abstract-factory/index.md index cb229f4ab..7db699a99 100644 --- a/abstract-factory/index.md +++ b/abstract-factory/index.md @@ -4,7 +4,9 @@ title: Abstract Factory folder: abstract-factory permalink: /patterns/abstract-factory/ categories: Creational -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Provide an interface for creating families of related or dependent @@ -22,3 +24,7 @@ objects without specifying their concrete classes. **Real world examples:** * [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/adapter/index.md b/adapter/index.md index 23aaeadcc..be9a87228 100644 --- a/adapter/index.md +++ b/adapter/index.md @@ -4,7 +4,9 @@ title: Adapter folder: adapter permalink: /patterns/adapter/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Convert the interface of a class into another interface the clients @@ -22,3 +24,7 @@ incompatible interfaces. **Real world examples:** * [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/bridge/index.md b/bridge/index.md index 6ef1c6519..1ad969183 100644 --- a/bridge/index.md +++ b/bridge/index.md @@ -4,7 +4,9 @@ title: Bridge folder: bridge permalink: /patterns/bridge/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Decouple an abstraction from its implementation so that the two can @@ -20,3 +22,7 @@ vary independently. * changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. * you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies * you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation. + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/builder/index.md b/builder/index.md index aa5c86201..f350638d2 100644 --- a/builder/index.md +++ b/builder/index.md @@ -4,7 +4,9 @@ title: Builder folder: builder permalink: /patterns/builder/ categories: Creational -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Separate the construction of a complex object from its @@ -22,3 +24,7 @@ representations. * [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) * [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/chain/index.md b/chain/index.md index 25e14bf06..9be376324 100644 --- a/chain/index.md +++ b/chain/index.md @@ -4,7 +4,9 @@ title: Chain of responsibility folder: chain permalink: /patterns/chain/ categories: Behavioral -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Avoid coupling the sender of a request to its receiver by giving @@ -23,3 +25,7 @@ objects and pass the request along the chain until an object handles it. * [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) * [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/command/index.md b/command/index.md index 23582406e..c9790819f 100644 --- a/command/index.md +++ b/command/index.md @@ -4,7 +4,9 @@ title: Command folder: command permalink: /patterns/command/ categories: Behavioral -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Encapsulate a request as an object, thereby letting you @@ -30,3 +32,7 @@ support undoable operations. **Real world examples:** * [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/composite/index.md b/composite/index.md index 374e1d5aa..4a31a1b33 100644 --- a/composite/index.md +++ b/composite/index.md @@ -4,7 +4,9 @@ title: Composite folder: composite permalink: /patterns/composite/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Compose objects into tree structures to represent part-whole @@ -22,3 +24,7 @@ of objects uniformly. * [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) * [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/decorator/index.md b/decorator/index.md index 02f129258..55849fce6 100644 --- a/decorator/index.md +++ b/decorator/index.md @@ -4,7 +4,9 @@ title: Decorator folder: decorator permalink: /patterns/decorator/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Attach additional responsibilities to an object dynamically. @@ -18,3 +20,7 @@ functionality. * to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/facade/index.md b/facade/index.md index 0f6e2fbb1..59ff888b2 100644 --- a/facade/index.md +++ b/facade/index.md @@ -4,7 +4,9 @@ title: Facade folder: facade permalink: /patterns/facade/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Provide a unified interface to a set of interfaces in a subsystem. @@ -17,3 +19,7 @@ Facade defines a higher-level interface that makes the subsystem easier to use. * you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade. * there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability. * you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, the you can simplify the dependencies between them by making them communicate with each other solely through their facades + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/factory-method/index.md b/factory-method/index.md index 8011e42c8..fee6b6e83 100644 --- a/factory-method/index.md +++ b/factory-method/index.md @@ -7,6 +7,7 @@ categories: Creational tags: - Java - Difficulty-Beginner + - Gang Of Four --- **Intent:** Define an interface for creating an object, but let subclasses @@ -20,3 +21,7 @@ instantiation to subclasses. * a class can't anticipate the class of objects it must create * a class wants its subclasses to specify the objects it creates * classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/flyweight/index.md b/flyweight/index.md index 85fa54ad1..e2273c197 100644 --- a/flyweight/index.md +++ b/flyweight/index.md @@ -4,7 +4,9 @@ title: Flyweight folder: flyweight permalink: /patterns/flyweight/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Use sharing to support large numbers of fine-grained objects @@ -25,3 +27,7 @@ true **Real world examples:** * [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/interpreter/index.md b/interpreter/index.md index c7c4539d5..57b117e06 100644 --- a/interpreter/index.md +++ b/interpreter/index.md @@ -4,7 +4,9 @@ title: Interpreter folder: interpreter permalink: /patterns/interpreter/ categories: Behavioral -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Given a language, define a representation for its grammar along @@ -19,3 +21,7 @@ trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/iterator/index.md b/iterator/index.md index 63bdaf516..ecfdb1b2a 100644 --- a/iterator/index.md +++ b/iterator/index.md @@ -4,7 +4,10 @@ title: Iterator folder: iterator permalink: /patterns/iterator/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Beginner + - Gang Of Four --- **Intent:** Provide a way to access the elements of an aggregate object @@ -21,3 +24,7 @@ sequentially without exposing its underlying representation. **Real world examples:** * [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/mediator/index.md b/mediator/index.md index e67218a53..cb4ce7fb1 100644 --- a/mediator/index.md +++ b/mediator/index.md @@ -4,7 +4,10 @@ title: Mediator folder: mediator permalink: /patterns/mediator/ categories: Behavioral -tags: Java +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate --- **Intent:** Define an object that encapsulates how a set of objects interact. @@ -18,3 +21,7 @@ other explicitly, and it lets you vary their interaction independently. * a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/memento/index.md b/memento/index.md index b91e34739..056af8b26 100644 --- a/memento/index.md +++ b/memento/index.md @@ -4,7 +4,9 @@ title: Memento folder: memento permalink: /patterns/memento/ categories: Behavioral -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Without violating encapsulation, capture and externalize an @@ -20,3 +22,7 @@ object's internal state so that the object can be restored to this state later. **Real world examples:** * [java.util.Date](http://docs.oracle.com/javase/8/docs/api/java/util/Date.html) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/observer/index.md b/observer/index.md index d5b380a30..fedb4cb8c 100644 --- a/observer/index.md +++ b/observer/index.md @@ -4,7 +4,10 @@ title: Observer folder: observer permalink: /patterns/observer/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Beginner + - Gang Of Four --- **Intent:** Define a one-to-many dependency between objects so that when one @@ -26,3 +29,7 @@ automatically. **Real world examples:** * [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/prototype/index.md b/prototype/index.md index cab97f7dc..9d108ff06 100644 --- a/prototype/index.md +++ b/prototype/index.md @@ -4,7 +4,9 @@ title: Prototype folder: prototype permalink: /patterns/prototype/ categories: Creational -tags: Java +tags: + - Java + - Gang Of Four --- **Intent:** Specify the kinds of objects to create using a prototypical @@ -21,3 +23,7 @@ instance, and create new objects by copying this prototype. **Real world examples:** * [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/proxy/index.md b/proxy/index.md index 96ea40714..f863a96a5 100644 --- a/proxy/index.md +++ b/proxy/index.md @@ -4,7 +4,10 @@ title: Proxy folder: proxy permalink: /patterns/proxy/ categories: Structural -tags: Java +tags: + - Java + - Gang Of Four + - Difficulty-Intermediate --- **Intent:** Provide a surrogate or placeholder for another object to control @@ -32,3 +35,7 @@ are several common situations in which the Proxy pattern is applicable * [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) * [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/singleton/index.md b/singleton/index.md index 7a280bdfa..18c137448 100644 --- a/singleton/index.md +++ b/singleton/index.md @@ -4,7 +4,10 @@ title: Singleton folder: singleton permalink: /patterns/singleton/ categories: Creational -tags: Java +tags: + - Java + - Gang Of Four + - Difficulty-Beginner --- **Intent:** Ensure a class only has one instance, and provide a global point of @@ -26,3 +29,7 @@ access to it. **Real world examples:** * [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/state/index.md b/state/index.md index 1b1e4717f..b743cb8be 100644 --- a/state/index.md +++ b/state/index.md @@ -4,7 +4,10 @@ title: State folder: state permalink: /patterns/state/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Intermediate + - Gang Of Four --- **Intent:** Allow an object to alter its behavior when its internal state @@ -16,3 +19,7 @@ changes. The object will appear to change its class. * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/strategy/index.md b/strategy/index.md index 1d3250517..120dd64d4 100644 --- a/strategy/index.md +++ b/strategy/index.md @@ -4,7 +4,10 @@ title: Strategy folder: strategy permalink: /patterns/strategy/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Beginner + - Gang Of Four --- **Intent:** Define a family of algorithms, encapsulate each one, and make them @@ -19,3 +22,7 @@ that use it. * you need different variants of an algorithm. for example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms * an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures * a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/template-method/index.md b/template-method/index.md index f647d2cd5..8b8b7878e 100644 --- a/template-method/index.md +++ b/template-method/index.md @@ -4,7 +4,10 @@ title: Template method folder: template-method permalink: /patterns/template-method/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Beginner + - Gang Of Four --- **Intent:** Define the skeleton of an algorithm in an operation, deferring some @@ -18,3 +21,7 @@ an algorithm without changing the algorithm's structure. * to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary * when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is good example of "refactoring to generalize" as described by Opdyke and Johnson. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations * to control subclasses extensions. You can define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/visitor/index.md b/visitor/index.md index eeb11a5e5..5f32edbbd 100644 --- a/visitor/index.md +++ b/visitor/index.md @@ -4,7 +4,10 @@ title: Visitor folder: visitor permalink: /patterns/visitor/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Expert + - Gang Of Four --- **Intent:** Represent an operation to be performed on the elements of an object @@ -22,3 +25,7 @@ of the elements on which it operates. **Real world examples:** * [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) + +**Credits** + +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) From ec9af416ba82ba95e07686529b2468900be8f453 Mon Sep 17 00:00:00 2001 From: Maxim Galushka Date: Wed, 23 Sep 2015 17:58:58 +0100 Subject: [PATCH 42/80] #247 adding getAttackPower method to pattern decorator --- decorator/src/main/java/com/iluwatar/decorator/App.java | 2 ++ decorator/src/main/java/com/iluwatar/decorator/Hostile.java | 2 ++ .../src/main/java/com/iluwatar/decorator/SmartTroll.java | 6 ++++++ decorator/src/main/java/com/iluwatar/decorator/Troll.java | 5 +++++ 4 files changed, 15 insertions(+) diff --git a/decorator/src/main/java/com/iluwatar/decorator/App.java b/decorator/src/main/java/com/iluwatar/decorator/App.java index 88e1c7103..a9eabf606 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/App.java +++ b/decorator/src/main/java/com/iluwatar/decorator/App.java @@ -23,11 +23,13 @@ public class App { Hostile troll = new Troll(); troll.attack(); troll.fleeBattle(); + System.out.printf("Simple troll power %d.\n", troll.getAttackPower()); // change the behavior of the simple troll by adding a decorator System.out.println("\nA smart looking troll surprises you."); Hostile smart = new SmartTroll(troll); smart.attack(); smart.fleeBattle(); + System.out.printf("Smart troll power %d.\n", smart.getAttackPower()); } } diff --git a/decorator/src/main/java/com/iluwatar/decorator/Hostile.java b/decorator/src/main/java/com/iluwatar/decorator/Hostile.java index 4df751cca..709072501 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Hostile.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Hostile.java @@ -9,6 +9,8 @@ public interface Hostile { void attack(); + int getAttackPower(); + void fleeBattle(); } diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java b/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java index 22ba88dc8..909f94c95 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java +++ b/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java @@ -21,6 +21,12 @@ public class SmartTroll implements Hostile { decorated.attack(); } + @Override + public int getAttackPower() { + // decorated troll power + 20 because it is smart + return decorated.getAttackPower() + 20; + } + @Override public void fleeBattle() { System.out.println("The troll calls for help!"); diff --git a/decorator/src/main/java/com/iluwatar/decorator/Troll.java b/decorator/src/main/java/com/iluwatar/decorator/Troll.java index 11e9b9d1a..85d873dbe 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Troll.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Troll.java @@ -11,6 +11,11 @@ public class Troll implements Hostile { System.out.println("The troll swings at you with a club!"); } + @Override + public int getAttackPower() { + return 10; + } + public void fleeBattle() { System.out.println("The troll shrieks in horror and runs away!"); } From 36809537d997bc39d8c7e7d51670add5213fa603 Mon Sep 17 00:00:00 2001 From: zafarella Date: Thu, 24 Sep 2015 01:01:51 -0400 Subject: [PATCH 43/80] checkstyle fixes - docs, indent etc --- .../main/java/com/iluwatar/singleton/App.java | 69 ++++++++++--------- .../iluwatar/singleton/EnumIvoryTower.java | 11 +-- .../InitializingOnDemandHolderIdiom.java | 36 +++++----- .../com/iluwatar/singleton/IvoryTower.java | 31 ++++++--- .../ThreadSafeDoubleCheckLocking.java | 62 +++++++++-------- .../ThreadSafeLazyLoadedIvoryTower.java | 33 ++++----- 6 files changed, 132 insertions(+), 110 deletions(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/App.java b/singleton/src/main/java/com/iluwatar/singleton/App.java index 67cb6fd00..e8a3a1571 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/App.java +++ b/singleton/src/main/java/com/iluwatar/singleton/App.java @@ -1,3 +1,6 @@ +/** + * Singleton pattern. + */ package com.iluwatar.singleton; /** @@ -16,40 +19,40 @@ package com.iluwatar.singleton; */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point + * @param args command line args + */ + public static void main(String[] args) { - // eagerly initialized singleton - IvoryTower ivoryTower1 = IvoryTower.getInstance(); - IvoryTower ivoryTower2 = IvoryTower.getInstance(); - System.out.println("ivoryTower1=" + ivoryTower1); - System.out.println("ivoryTower2=" + ivoryTower2); + // eagerly initialized singleton + IvoryTower ivoryTower1 = IvoryTower.getInstance(); + IvoryTower ivoryTower2 = IvoryTower.getInstance(); + System.out.println("ivoryTower1=" + ivoryTower1); + System.out.println("ivoryTower2=" + ivoryTower2); - // lazily initialized singleton - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); - System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); + // lazily initialized singleton + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower + .getInstance(); + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower + .getInstance(); + System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); + System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); - // enum singleton - EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; - EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; - System.out.println("enumIvoryTower1=" + enumIvoryTower1); - System.out.println("enumIvoryTower2=" + enumIvoryTower2); - - InitializingOnDemandHolderIdiom demandHolderIdiom = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom); - InitializingOnDemandHolderIdiom demandHolderIdiom2 = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom2); - - ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl1); - ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl2); - } + // enum singleton + EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; + EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; + System.out.println("enumIvoryTower1=" + enumIvoryTower1); + System.out.println("enumIvoryTower2=" + enumIvoryTower2); + + InitializingOnDemandHolderIdiom demandHolderIdiom = InitializingOnDemandHolderIdiom.getInstance(); + System.out.println(demandHolderIdiom); + InitializingOnDemandHolderIdiom demandHolderIdiom2 = InitializingOnDemandHolderIdiom.getInstance(); + System.out.println(demandHolderIdiom2); + + ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl1); + ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl2); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java index 8f9895924..b2523a45f 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java @@ -1,13 +1,14 @@ +/** + * Enum Singleton class. + * Effective Java 2nd Edition (Joshua Bloch) p. 18 + */ package com.iluwatar.singleton; /** - * - * Enum Singleton class. - * Effective Java 2nd Edition (Joshua Bloch) p. 18 - * + * Enum based singleton implementation. */ public enum EnumIvoryTower { - + INSTANCE; @Override diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 4fd4c8163..423bc3242 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -1,36 +1,38 @@ +/** + * Singleton pattern. + */ package com.iluwatar.singleton; import java.io.Serializable; /** - * The Initialize-on-demand-holder idiom is a secure way of + * The Initialize-on-demand-holder idiom is a secure way of * creating lazy initialized singleton object in Java. * refer to "The CERT Oracle Secure Coding Standard for Java" * By Dhruv Mohindra, Robert C. Seacord p.378 - *

+ *

* Singleton objects usually are heavy to create and sometimes need to serialize them. * This class also shows how to preserve singleton in serialized version of singleton. - * - * @author mortezaadi@gmail.com * + * @author mortezaadi@gmail.com */ -public class InitializingOnDemandHolderIdiom implements Serializable{ +public class InitializingOnDemandHolderIdiom implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private static class HelperHolder { - public static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); - } + private InitializingOnDemandHolderIdiom() { + } - public static InitializingOnDemandHolderIdiom getInstance() { - return HelperHolder.INSTANCE; - } + public static InitializingOnDemandHolderIdiom getInstance() { + return HelperHolder.INSTANCE; + } - private InitializingOnDemandHolderIdiom() { - } + protected Object readResolve() { + return getInstance(); + } - protected Object readResolve() { - return getInstance(); - } + private static class HelperHolder { + public static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java index 5d418aa54..29f9e1b24 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java @@ -1,20 +1,33 @@ +/** + * Singleton pattern. + */ package com.iluwatar.singleton; /** - * * Singleton class. * Eagerly initialized static instance guarantees thread * safety. - * */ -public class IvoryTower { +public final class IvoryTower { - private static IvoryTower instance = new IvoryTower(); + /** + * Static to class instance of the class. + */ + private static IvoryTower instance = new IvoryTower(); - private IvoryTower() { - } + /** + * Private constructor so nobody can instantiate the class. + */ + private IvoryTower() { + } - public static IvoryTower getInstance() { - return instance; - } + /** + * To be called by user to + * obtain instance of the class. + * + * @return instance of the singleton. + */ + public static IvoryTower getInstance() { + return instance; + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java index d15509236..c0ac0b4c4 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java @@ -1,41 +1,43 @@ +/** + * Singleton pattern. + * + */ package com.iluwatar.singleton; /** * Double check locking - *

+ *

* http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html - *

+ *

* Broken under Java 1.4. - * - * @author mortezaadi@gmail.com * + * @author mortezaadi@gmail.com */ public class ThreadSafeDoubleCheckLocking { - - private static volatile ThreadSafeDoubleCheckLocking INSTANCE; - /** - * private constructor to prevent client from instantiating. - * - */ - private ThreadSafeDoubleCheckLocking() { - //to prevent instantiating by Reflection call - if(INSTANCE != null) - throw new IllegalStateException("Already initialized."); - } - - public static ThreadSafeDoubleCheckLocking getInstance() { - //local variable increases performance by 25 percent - //Joshua Bloch "Effective Java, Second Edition", p. 283-284 - ThreadSafeDoubleCheckLocking result = INSTANCE; - if (result == null) { - synchronized (ThreadSafeDoubleCheckLocking.class) { - result = INSTANCE; - if (result == null) { - INSTANCE = result = new ThreadSafeDoubleCheckLocking(); - } - } - } - return result; - } + private static volatile ThreadSafeDoubleCheckLocking INSTANCE; + + /** + * private constructor to prevent client from instantiating. + */ + private ThreadSafeDoubleCheckLocking() { + //to prevent instantiating by Reflection call + if (INSTANCE != null) + throw new IllegalStateException("Already initialized."); + } + + public static ThreadSafeDoubleCheckLocking getInstance() { + //local variable increases performance by 25 percent + //Joshua Bloch "Effective Java, Second Edition", p. 283-284 + ThreadSafeDoubleCheckLocking result = INSTANCE; + if (result == null) { + synchronized (ThreadSafeDoubleCheckLocking.class) { + result = INSTANCE; + if (result == null) { + INSTANCE = result = new ThreadSafeDoubleCheckLocking(); + } + } + } + return result; + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java index f7184cfb0..b1f9b81a3 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java @@ -1,28 +1,29 @@ +/** + * Singleton pattern. + */ package com.iluwatar.singleton; /** - * * Thread-safe Singleton class. * The instance is lazily initialized and thus needs synchronization * mechanism. - * */ public class ThreadSafeLazyLoadedIvoryTower { - private static ThreadSafeLazyLoadedIvoryTower instance = null; - - private ThreadSafeLazyLoadedIvoryTower() { - } + private static ThreadSafeLazyLoadedIvoryTower instance = null; - public synchronized static ThreadSafeLazyLoadedIvoryTower getInstance() { - /* - * The instance gets created only when it is called for first time. - * Lazy-loading - */ - if (instance == null) { - instance = new ThreadSafeLazyLoadedIvoryTower(); - } + private ThreadSafeLazyLoadedIvoryTower() { + } - return instance; - } + public synchronized static ThreadSafeLazyLoadedIvoryTower getInstance() { + /* + * The instance gets created only when it is called for first time. + * Lazy-loading + */ + if (instance == null) { + instance = new ThreadSafeLazyLoadedIvoryTower(); + } + + return instance; + } } From ba6511fe5dd92179ffa323593bfe4c198653c30c Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 24 Sep 2015 12:23:02 +0530 Subject: [PATCH 44/80] Work on #226, moved POSA reference and some J2EE design pattern references --- README.md | 2 -- dao/index.md | 8 +++++++- front-controller/index.md | 6 ++++++ half-sync-half-async/index.md | 1 + intercepting-filter/index.md | 5 +++++ layers/index.md | 5 +++++ model-view-controller/index.md | 2 ++ model-view-presenter/index.md | 4 ++++ reactor/index.md | 1 + service-layer/index.md | 1 + service-locator/index.md | 6 +++++- 11 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 77282f569..255fca0f2 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ If you are willing to contribute to the project you will find the relevant infor # Credits -* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) * [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) * [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/) * [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) @@ -48,7 +47,6 @@ If you are willing to contribute to the project you will find the relevant infor * [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) * [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1) * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) -* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) # License diff --git a/dao/index.md b/dao/index.md index 7fd936c12..cf9f43a68 100644 --- a/dao/index.md +++ b/dao/index.md @@ -4,7 +4,9 @@ title: Data Access Object folder: dao permalink: /patterns/dao/ categories: Architectural -tags: Java +tags: + - Java + - Difficulty-Beginner --- **Intent:** Object provides an abstract interface to some type of database or @@ -16,3 +18,7 @@ other persistence mechanism. * when you want to consolidate how the data layer is accessed * when you want to avoid writing multiple data retrieval/persistence layers + +**Credits:** + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/front-controller/index.md b/front-controller/index.md index dd83bc396..ba593a157 100644 --- a/front-controller/index.md +++ b/front-controller/index.md @@ -22,3 +22,9 @@ internationalization, routing and logging in a single place. **Real world examples:** * [Apache Struts](https://struts.apache.org/) + +**Credits:** + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) +* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) +* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/half-sync-half-async/index.md b/half-sync-half-async/index.md index 0ea0254b8..dc1930e3b 100644 --- a/half-sync-half-async/index.md +++ b/half-sync-half-async/index.md @@ -30,3 +30,4 @@ degrading execution efficiency. **Credits:** * [Douglas C. Schmidt and Charles D. Cranor - Half Sync/Half Async](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) +* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/intercepting-filter/index.md b/intercepting-filter/index.md index 9625e445d..41825745b 100644 --- a/intercepting-filter/index.md +++ b/intercepting-filter/index.md @@ -18,6 +18,11 @@ post-processing to requests from a client to a target * a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers * you want a modular approach to configuring pre-processing and post-processing schemes +**Real world examples:** + +* [Struts 2 - Interceptors](https://struts.apache.org/docs/interceptors.html) + **Credits:** * [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm) +* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) diff --git a/layers/index.md b/layers/index.md index 1d12d3b4e..37089a19c 100644 --- a/layers/index.md +++ b/layers/index.md @@ -17,3 +17,8 @@ tags: Java * you want clearly divide software responsibilities into differents parts of the program * you want to prevent a change from propagating throughout the application * you want to make your application more maintainable and testable + +**Credits:** + +* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) + diff --git a/model-view-controller/index.md b/model-view-controller/index.md index f96daf243..1ba1089c0 100644 --- a/model-view-controller/index.md +++ b/model-view-controller/index.md @@ -21,3 +21,5 @@ display. **Credits:** * [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) +* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/model-view-presenter/index.md b/model-view-presenter/index.md index 609c59c13..b51268013 100644 --- a/model-view-presenter/index.md +++ b/model-view-presenter/index.md @@ -17,3 +17,7 @@ situations * when you want to improve the "Separation of Concerns" principle in presentation logic * when a user interface development and testing is necessary. + +**Real world examples:** + +* [MVP4J](https://github.com/amineoualialami/mvp4j) diff --git a/reactor/index.md b/reactor/index.md index 7333c74dd..6e20598d2 100644 --- a/reactor/index.md +++ b/reactor/index.md @@ -26,5 +26,6 @@ tags: **Credits** * [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf) +* [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) * [Doug Lea - Scalable IO in Java](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf) * [Netty](http://netty.io/) diff --git a/service-layer/index.md b/service-layer/index.md index f9e7ac1cb..ea3f3d0ba 100644 --- a/service-layer/index.md +++ b/service-layer/index.md @@ -24,3 +24,4 @@ its business logic. The Service Layer fulfills this role. **Credits:** * [Martin Fowler - Service Layer](http://martinfowler.com/eaaCatalog/serviceLayer.html) +* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/service-locator/index.md b/service-locator/index.md index 8c2aa6417..03c432749 100644 --- a/service-locator/index.md +++ b/service-locator/index.md @@ -10,7 +10,7 @@ tags: Java **Intent:** Encapsulate the processes involved in obtaining a service with a strong abstraction layer. -![alt text](./etc/service-locator.png "Proxy") +![alt text](./etc/service-locator.png "Service Locator") **Applicability:** The service locator pattern is applicable whenever we want to locate/fetch various services using JNDI which, typically, is a redundant @@ -26,3 +26,7 @@ improves the performance of application to great extent. * when network hits are expensive and time consuming * lookups of services are done quite frequently * large number of services are being used + +**Credits:** + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) From 60f9b7127819e383edd374fe3b80b79f7ee994a9 Mon Sep 17 00:00:00 2001 From: zafarella Date: Thu, 24 Sep 2015 13:29:39 -0400 Subject: [PATCH 45/80] eliminate all warnings of checkstyle. --- .../main/java/com/iluwatar/singleton/App.java | 85 ++++++++++--------- .../iluwatar/singleton/EnumIvoryTower.java | 15 ++-- .../InitializingOnDemandHolderIdiom.java | 28 +++--- .../com/iluwatar/singleton/IvoryTower.java | 39 ++++----- .../ThreadSafeDoubleCheckLocking.java | 48 ++++++----- .../ThreadSafeLazyLoadedIvoryTower.java | 30 +++---- 6 files changed, 121 insertions(+), 124 deletions(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/App.java b/singleton/src/main/java/com/iluwatar/singleton/App.java index e8a3a1571..11cb4daa6 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/App.java +++ b/singleton/src/main/java/com/iluwatar/singleton/App.java @@ -1,58 +1,63 @@ /** * Singleton pattern. */ + package com.iluwatar.singleton; /** - * * Singleton pattern ensures that the class ({@link IvoryTower}) can have only one - * existing instance per Java classloader instance and provides global access to it. - *

+ * existing instance per Java classloader instance and provides global access to it. + *

* http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java - *

- * The risk of this pattern is that bugs resulting from setting a singleton up in a distributed environment can - * be tricky to debug, since it will work fine if you debug with a single classloader. Additionally, these - * problems can crop up a while after the implementation of a singleton, since they may start out synchronous and - * only become async with time, so you it may not be clear why you are seeing certain changes in behaviour. - *

+ *

+ * The risk of this pattern is that bugs resulting from setting a singleton up in + * a distributed environment can be tricky to debug, since it will work fine if you + * debug with a single classloader. Additionally, these problems can crop up a while + * after the implementation of a singleton, since they may start out synchronous and + * only become async with time, so you it may not be clear why you are seeing certain + * changes in behaviour. + *

* http://stackoverflow.com/questions/17721263/singleton-across-jvm-or-application-instance-or-tomcat-instance */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { - // eagerly initialized singleton - IvoryTower ivoryTower1 = IvoryTower.getInstance(); - IvoryTower ivoryTower2 = IvoryTower.getInstance(); - System.out.println("ivoryTower1=" + ivoryTower1); - System.out.println("ivoryTower2=" + ivoryTower2); + // eagerly initialized singleton + IvoryTower ivoryTower1 = IvoryTower.getInstance(); + IvoryTower ivoryTower2 = IvoryTower.getInstance(); + System.out.println("ivoryTower1=" + ivoryTower1); + System.out.println("ivoryTower2=" + ivoryTower2); - // lazily initialized singleton - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); - System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); + // lazily initialized singleton + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower + .getInstance(); + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower + .getInstance(); + System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); + System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); - // enum singleton - EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; - EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; - System.out.println("enumIvoryTower1=" + enumIvoryTower1); - System.out.println("enumIvoryTower2=" + enumIvoryTower2); + // enum singleton + EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; + EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; + System.out.println("enumIvoryTower1=" + enumIvoryTower1); + System.out.println("enumIvoryTower2=" + enumIvoryTower2); - InitializingOnDemandHolderIdiom demandHolderIdiom = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom); - InitializingOnDemandHolderIdiom demandHolderIdiom2 = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom2); + InitializingOnDemandHolderIdiom demandHolderIdiom = + InitializingOnDemandHolderIdiom.getInstance(); + System.out.println(demandHolderIdiom); + InitializingOnDemandHolderIdiom demandHolderIdiom2 = + InitializingOnDemandHolderIdiom.getInstance(); + System.out.println(demandHolderIdiom2); - ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl1); - ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl2); - } + ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl1); + ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl2); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java index b2523a45f..f39babe45 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java @@ -1,18 +1,15 @@ -/** - * Enum Singleton class. - * Effective Java 2nd Edition (Joshua Bloch) p. 18 - */ package com.iluwatar.singleton; /** * Enum based singleton implementation. + * Effective Java 2nd Edition (Joshua Bloch) p. 18 */ public enum EnumIvoryTower { - INSTANCE; + INSTANCE; - @Override - public String toString() { - return getDeclaringClass().getCanonicalName() + "@" + hashCode(); - } + @Override + public String toString() { + return getDeclaringClass().getCanonicalName() + "@" + hashCode(); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 423bc3242..88738b8ca 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -1,6 +1,3 @@ -/** - * Singleton pattern. - */ package com.iluwatar.singleton; import java.io.Serializable; @@ -18,21 +15,22 @@ import java.io.Serializable; */ public class InitializingOnDemandHolderIdiom implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private InitializingOnDemandHolderIdiom() { - } + private InitializingOnDemandHolderIdiom() { + } - public static InitializingOnDemandHolderIdiom getInstance() { - return HelperHolder.INSTANCE; - } + public static InitializingOnDemandHolderIdiom getInstance() { + return HelperHolder.INSTANCE; + } - protected Object readResolve() { - return getInstance(); - } + protected Object readResolve() { + return getInstance(); + } - private static class HelperHolder { - public static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); - } + private static class HelperHolder { + public static final InitializingOnDemandHolderIdiom INSTANCE = + new InitializingOnDemandHolderIdiom(); + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java index 29f9e1b24..96809c477 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java @@ -1,6 +1,3 @@ -/** - * Singleton pattern. - */ package com.iluwatar.singleton; /** @@ -10,24 +7,24 @@ package com.iluwatar.singleton; */ public final class IvoryTower { - /** - * Static to class instance of the class. - */ - private static IvoryTower instance = new IvoryTower(); + /** + * Static to class instance of the class. + */ + private static IvoryTower instance = new IvoryTower(); - /** - * Private constructor so nobody can instantiate the class. - */ - private IvoryTower() { - } + /** + * Private constructor so nobody can instantiate the class. + */ + private IvoryTower() { + } - /** - * To be called by user to - * obtain instance of the class. - * - * @return instance of the singleton. - */ - public static IvoryTower getInstance() { - return instance; - } + /** + * To be called by user to + * obtain instance of the class. + * + * @return instance of the singleton. + */ + public static IvoryTower getInstance() { + return instance; + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java index c0ac0b4c4..26b57d4cf 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java @@ -1,7 +1,3 @@ -/** - * Singleton pattern. - * - */ package com.iluwatar.singleton; /** @@ -15,29 +11,35 @@ package com.iluwatar.singleton; */ public class ThreadSafeDoubleCheckLocking { - private static volatile ThreadSafeDoubleCheckLocking INSTANCE; + private static volatile ThreadSafeDoubleCheckLocking INSTANCE; - /** - * private constructor to prevent client from instantiating. - */ - private ThreadSafeDoubleCheckLocking() { - //to prevent instantiating by Reflection call - if (INSTANCE != null) - throw new IllegalStateException("Already initialized."); + /** + * private constructor to prevent client from instantiating. + */ + private ThreadSafeDoubleCheckLocking() { + //to prevent instantiating by Reflection call + if (INSTANCE != null) { + throw new IllegalStateException("Already initialized."); } + } - public static ThreadSafeDoubleCheckLocking getInstance() { - //local variable increases performance by 25 percent - //Joshua Bloch "Effective Java, Second Edition", p. 283-284 - ThreadSafeDoubleCheckLocking result = INSTANCE; + /** + * Public accessor. + * + * @return an instance of the class. + */ + public static ThreadSafeDoubleCheckLocking getInstance() { + //local variable increases performance by 25 percent + //Joshua Bloch "Effective Java, Second Edition", p. 283-284 + ThreadSafeDoubleCheckLocking result = INSTANCE; + if (result == null) { + synchronized (ThreadSafeDoubleCheckLocking.class) { + result = INSTANCE; if (result == null) { - synchronized (ThreadSafeDoubleCheckLocking.class) { - result = INSTANCE; - if (result == null) { - INSTANCE = result = new ThreadSafeDoubleCheckLocking(); - } - } + INSTANCE = result = new ThreadSafeDoubleCheckLocking(); } - return result; + } } + return result; + } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java index b1f9b81a3..f9b62e798 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java @@ -1,6 +1,3 @@ -/** - * Singleton pattern. - */ package com.iluwatar.singleton; /** @@ -10,20 +7,21 @@ package com.iluwatar.singleton; */ public class ThreadSafeLazyLoadedIvoryTower { - private static ThreadSafeLazyLoadedIvoryTower instance = null; + private static ThreadSafeLazyLoadedIvoryTower instance = null; - private ThreadSafeLazyLoadedIvoryTower() { + private ThreadSafeLazyLoadedIvoryTower() { + } + + /** + * The instance gets created only when it is called for first time. + * Lazy-loading + */ + public synchronized static ThreadSafeLazyLoadedIvoryTower getInstance() { + + if (instance == null) { + instance = new ThreadSafeLazyLoadedIvoryTower(); } - public synchronized static ThreadSafeLazyLoadedIvoryTower getInstance() { - /* - * The instance gets created only when it is called for first time. - * Lazy-loading - */ - if (instance == null) { - instance = new ThreadSafeLazyLoadedIvoryTower(); - } - - return instance; - } + return instance; + } } From e077aa34d74bbb8040f8b8a8100a5beae4a328a3 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 26 Sep 2015 20:19:39 +0300 Subject: [PATCH 46/80] Travis deploy web site to S3 --- .travis.yml | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index a59370c30..59711b779 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,30 @@ language: java - jdk: - - oraclejdk8 +- oraclejdk8 env: global: - GH_REF: github.com/iluwatar/java-design-patterns.git - - secure: "LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg=" - -before_install: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg= -after_success: - - mvn clean test jacoco:report coveralls:report - - bash update-ghpages.sh +before_install: +- export DISPLAY=:99.0 +- sh -e /etc/init.d/xvfb start + +after_success: +- mvn clean test jacoco:report coveralls:report +- bash update-ghpages.sh -# Migration to container-based infrastructure sudo: false + +deploy: + provider: s3 + access_key_id: AKIAJVPJZO7743HVSDHA + secret_access_key: + secure: aYaEhSd6/8SV/gvsn/22iOoyWrQtjKX7VHUoNTX16Mw6flYy29BE+/ZOL1CTL5EXEZsC1zyqCi7RoM6QUXk9DYVg4Lc5yTIwaM0evd3obzPeGQVCR9JRAqM9KGKwyW19oEkNxwEE/pKNDLCfkctMZ4bcwDhkIdsMSAVzKDzq+Yw= + bucket: java-design-patterns + local-dir: _site + acl: public_read_write + on: + repo: iluwatar/java-design-patterns + branch: gh-pages \ No newline at end of file From 2ad361f2c36ca4ab9dcc99260a3e46864d2f0acb Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 26 Sep 2015 21:23:00 +0300 Subject: [PATCH 47/80] Travis configuration changes #255 --- .travis.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59711b779..27076f08c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,10 +21,9 @@ deploy: provider: s3 access_key_id: AKIAJVPJZO7743HVSDHA secret_access_key: - secure: aYaEhSd6/8SV/gvsn/22iOoyWrQtjKX7VHUoNTX16Mw6flYy29BE+/ZOL1CTL5EXEZsC1zyqCi7RoM6QUXk9DYVg4Lc5yTIwaM0evd3obzPeGQVCR9JRAqM9KGKwyW19oEkNxwEE/pKNDLCfkctMZ4bcwDhkIdsMSAVzKDzq+Yw= - bucket: java-design-patterns + secure: iHjLIy+dHku1o8PrjGhz1mMfM971nqmpjo2mzVNYqYrxlB8zoAqwlz9LoFJcUAKbxhHLJT/LgtYYj+uBspUMOITejaiyCDcM8edGwSXKJu+g+Fp0PVGZQXGziGInmUVT9b2lpAV8Nn2sRpAHgHrM9fKaAU3epG5LfVkQxSkGZp0= + bucket: java-design-patterns.com local-dir: _site - acl: public_read_write + acl: public_read on: - repo: iluwatar/java-design-patterns - branch: gh-pages \ No newline at end of file + branch: gh-pages From 793b1cc17212497a589eb7f65e453814ec9fcad2 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 26 Sep 2015 22:08:23 +0300 Subject: [PATCH 48/80] Travis configuration changes #255 --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 27076f08c..2f18b64d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,5 +25,6 @@ deploy: bucket: java-design-patterns.com local-dir: _site acl: public_read + skip_cleanup: true on: - branch: gh-pages + branch: ** From 83fed6dd34759bc0870e7c11086ccf2f214be1e4 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 26 Sep 2015 22:14:36 +0300 Subject: [PATCH 49/80] Fix Travis config syntax #255 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2f18b64d2..686eeae8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,4 +27,4 @@ deploy: acl: public_read skip_cleanup: true on: - branch: ** + all_branches: true From 621c3498f685f490e149cffe1f1a9a93cd3ac488 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 26 Sep 2015 22:52:59 +0300 Subject: [PATCH 50/80] Remove broken deploy config #255 --- .travis.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 686eeae8d..60693c5cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,15 +16,3 @@ after_success: - bash update-ghpages.sh sudo: false - -deploy: - provider: s3 - access_key_id: AKIAJVPJZO7743HVSDHA - secret_access_key: - secure: iHjLIy+dHku1o8PrjGhz1mMfM971nqmpjo2mzVNYqYrxlB8zoAqwlz9LoFJcUAKbxhHLJT/LgtYYj+uBspUMOITejaiyCDcM8edGwSXKJu+g+Fp0PVGZQXGziGInmUVT9b2lpAV8Nn2sRpAHgHrM9fKaAU3epG5LfVkQxSkGZp0= - bucket: java-design-patterns.com - local-dir: _site - acl: public_read - skip_cleanup: true - on: - all_branches: true From 11cdd20f6fd5fd48360336a7709009b667202359 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Wed, 30 Sep 2015 22:36:01 +0300 Subject: [PATCH 51/80] Update version to 1.7.0 --- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- decorator/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-method/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- multiton/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- reactor/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- visitor/pom.xml | 2 +- 64 files changed, 67 insertions(+), 67 deletions(-) diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index ddb7e7b9d..bb9df6bc9 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index 74a58de92..d07d26b94 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 adapter diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index 2a5010b74..d5e0e2b3e 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index 947fa2c60..533074d72 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 bridge diff --git a/builder/pom.xml b/builder/pom.xml index 65997c9e6..effc18f0c 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 482d59fab..84ae64a17 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -6,7 +6,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 business-delegate diff --git a/callback/pom.xml b/callback/pom.xml index f8cf6babf..7e66f6e2b 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 callback diff --git a/chain/pom.xml b/chain/pom.xml index ebae620d6..80591a477 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 chain diff --git a/command/pom.xml b/command/pom.xml index 401acba32..22f1c256b 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 command diff --git a/composite/pom.xml b/composite/pom.xml index ca4daaaad..2b8298647 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 composite diff --git a/dao/pom.xml b/dao/pom.xml index a7a5c74ca..0361f3c9b 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 dao diff --git a/decorator/pom.xml b/decorator/pom.xml index 57946b711..92142f184 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 decorator diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index a6780cb20..9a28933f8 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 46d4a70f0..6f1fe93eb 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -3,7 +3,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 5f16635f6..057d75e8e 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 1c9a3b626..4e7f1b55b 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -4,7 +4,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 event-aggregator diff --git a/execute-around/pom.xml b/execute-around/pom.xml index 7d806337b..c6a7785aa 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 execute-around diff --git a/facade/pom.xml b/facade/pom.xml index 17d3d707c..cea5bf0c3 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 facade diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 81072a4f9..39e0b7b4e 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 factory-method diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index be8ab8039..16e237bd5 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -5,7 +5,7 @@ java-design-patterns com.iluwatar - 1.6.0 + 1.7.0 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index 3efccbfa4..f706b49ed 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index 54ca5bef2..7c0f3e2b0 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index a554186f3..c80121df2 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -6,7 +6,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index 94c12d797..e7436d29f 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 half-sync-half-async diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index 35ebd9f59..230f76ca3 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 8583464d3..8a536748c 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index da5040dce..22e574ba5 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 iterator diff --git a/layers/pom.xml b/layers/pom.xml index de672a3df..5d3fd778c 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -6,7 +6,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index b795abf87..61da2f3b5 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index 5a9c00042..1e325bb8f 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 mediator diff --git a/memento/pom.xml b/memento/pom.xml index 182a46204..98d1d4a9f 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index ef66f2401..ee7d54c95 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -6,7 +6,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 7162c71d9..8c9c07809 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 41a2d2013..4f2d320ba 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 model-view-presenter model-view-presenter diff --git a/multiton/pom.xml b/multiton/pom.xml index 6ea1ad2fa..9735bc8ea 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 multiton diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index cadf6d82e..b0efdb435 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.6.0 + 1.7.0 naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index 33cd798f7..bbed2fe1b 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.6.0 + 1.7.0 naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index 1fd518bb1..221b072e5 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.6.0 + 1.7.0 naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index c9387b58d..067d4a5a8 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.6.0 + 1.7.0 naked-objects @@ -350,17 +350,17 @@ ${project.groupId} naked-objects-dom - 1.6.0 + 1.7.0 ${project.groupId} naked-objects-fixture - 1.6.0 + 1.7.0 ${project.groupId} naked-objects-webapp - 1.6.0 + 1.7.0 diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index 3e018995a..bc7d7c299 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.6.0 + 1.7.0 naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index eec32fd4d..a372851ed 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index 4a3b237c6..528e3db16 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 object-pool diff --git a/observer/pom.xml b/observer/pom.xml index 19e93421c..023d93bea 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 observer diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 2a0653981..06d9f34de 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 poison-pill diff --git a/pom.xml b/pom.xml index 2da35eb46..3064f3c0d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 pom diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 70a716062..274739257 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 private-class-data diff --git a/property/pom.xml b/property/pom.xml index 5435f06f5..f32a4008e 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 property diff --git a/prototype/pom.xml b/prototype/pom.xml index 0ebba706e..d5772da60 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index 1f625d736..750c12b84 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 proxy diff --git a/reactor/pom.xml b/reactor/pom.xml index 599376e32..516a4b93c 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 reactor diff --git a/repository/pom.xml b/repository/pom.xml index db0a8af5f..70f2ff6b2 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -6,7 +6,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index c8382ceb8..a16eae745 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 resource-acquisition-is-initialization diff --git a/servant/pom.xml b/servant/pom.xml index b6497215f..1928f83c9 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index 8e0ea8322..a42d07c5e 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index a596e4b0c..f210c74b6 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index cdd9b613a..ab118a1c3 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 singleton diff --git a/specification/pom.xml b/specification/pom.xml index b4d6efe90..3c58aa29f 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 specification diff --git a/state/pom.xml b/state/pom.xml index 0119146b3..2d87a796a 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index f9c98474b..7c7e8ac69 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -6,7 +6,7 @@ java-design-patterns com.iluwatar - 1.6.0 + 1.7.0 step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index 9532a8079..0b71652ba 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index 53a7dc56e..4aa776591 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index 42e54de7e..e335b06e5 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index 7e852d4ee..086251772 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 tolerant-reader diff --git a/visitor/pom.xml b/visitor/pom.xml index 8adda4855..ca5f72be8 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -5,7 +5,7 @@ com.iluwatar java-design-patterns - 1.6.0 + 1.7.0 visitor From 6413c4d2be13a0376904ea2690caa8ff899b89ed Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Fri, 2 Oct 2015 22:53:41 +0300 Subject: [PATCH 52/80] Added more Singleton documentation #188 --- .../main/java/com/iluwatar/singleton/App.java | 44 ++++++++++++------- .../com/iluwatar/singleton/IvoryTower.java | 2 +- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/App.java b/singleton/src/main/java/com/iluwatar/singleton/App.java index 11cb4daa6..7566c9c4d 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/App.java +++ b/singleton/src/main/java/com/iluwatar/singleton/App.java @@ -1,23 +1,35 @@ -/** - * Singleton pattern. - */ - package com.iluwatar.singleton; /** - * Singleton pattern ensures that the class ({@link IvoryTower}) can have only one - * existing instance per Java classloader instance and provides global access to it. + * Singleton pattern ensures that the class can have only one existing instance per Java classloader instance + * and provides global access to it. *

- * http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java - *

- * The risk of this pattern is that bugs resulting from setting a singleton up in + * One of the risks of this pattern is that bugs resulting from setting a singleton up in * a distributed environment can be tricky to debug, since it will work fine if you * debug with a single classloader. Additionally, these problems can crop up a while * after the implementation of a singleton, since they may start out synchronous and * only become async with time, so you it may not be clear why you are seeing certain * changes in behaviour. *

- * http://stackoverflow.com/questions/17721263/singleton-across-jvm-or-application-instance-or-tomcat-instance + * There are many ways to implement the Singleton. The first one is the eagerly initialized instance in + * {@link IvoryTower}. Eager initialization implies that the implementation is thread safe. If you can + * afford giving up control of the instantiation moment, then this implementation will suit you fine. + *

+ * The other option to implement eagerly initialized Singleton is enum based Singleton. The example is + * found in {@link EnumIvoryTower}. At first glance the code looks short and simple. However, you should + * be aware of the downsides including committing to implementation strategy, extending the enum class, + * serializability and restrictions to coding. These are extensively discussed in Stack Overflow: + * http://programmers.stackexchange.com/questions/179386/what-are-the-downsides-of-implementing-a-singleton-with-javas-enum + *

+ * {@link ThreadSafeLazyLoadedIvoryTower} is a Singleton implementation that is initialized on demand. + * The downside is that it is very slow to access since the whole access method is synchronized. + *

+ * Another Singleton implementation that is initialized on demand is found in {@link ThreadSafeDoubleCheckLocking}. It + * is somewhat faster than {@link ThreadSafeLazyLoadedIvoryTower} since it doesn't synchronize the whole access method + * but only the method internals on specific conditions. + *

+ * Yet another way to implement thread safe lazily initialized Singleton can be found in {@link InitializingOnDemandHolderIdiom}. + * However, this implementation requires at least Java 8 API level to work. */ public class App { @@ -48,16 +60,18 @@ public class App { System.out.println("enumIvoryTower1=" + enumIvoryTower1); System.out.println("enumIvoryTower2=" + enumIvoryTower2); + // double checked locking + ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl1); + ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); + System.out.println(dcl2); + + // initialize on demand holder idiom InitializingOnDemandHolderIdiom demandHolderIdiom = InitializingOnDemandHolderIdiom.getInstance(); System.out.println(demandHolderIdiom); InitializingOnDemandHolderIdiom demandHolderIdiom2 = InitializingOnDemandHolderIdiom.getInstance(); System.out.println(demandHolderIdiom2); - - ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl1); - ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl2); } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java index 96809c477..7470c3f29 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java @@ -10,7 +10,7 @@ public final class IvoryTower { /** * Static to class instance of the class. */ - private static IvoryTower instance = new IvoryTower(); + private static final IvoryTower instance = new IvoryTower(); /** * Private constructor so nobody can instantiate the class. From 0a61d7b06729bec9906ec99fca9d9e9c8cd9f9eb Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 11:53:44 +0300 Subject: [PATCH 53/80] Improve Abstract Factory Javadocs --- .../java/com/iluwatar/abstractfactory/App.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java index 29e9272f6..618b98c52 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java @@ -3,12 +3,21 @@ package com.iluwatar.abstractfactory; /** * + * The Abstract Factory pattern provides a way to encapsulate a group of individual + * factories that have a common theme without specifying their concrete classes. In + * normal usage, the client software creates a concrete implementation of the abstract + * factory and then uses the generic interface of the factory to create the concrete + * objects that are part of the theme. The client does not know (or care) which + * concrete objects it gets from each of these internal factories, since it uses only + * the generic interfaces of their products. This pattern separates the details of + * implementation of a set of objects from their general usage and relies on object + * composition, as object creation is implemented in methods exposed in the factory + * interface. + *

* The essence of the Abstract Factory pattern is a factory interface * ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory}, - * {@link OrcKingdomFactory}). - *

- * The example uses both concrete implementations to create a king, a castle and - * an army. + * {@link OrcKingdomFactory}). The example uses both concrete implementations to + * create a king, a castle and an army. * */ public class App { From 8c6caa29b728a53e49cb973cf902dd528cc16c07 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 11:56:59 +0300 Subject: [PATCH 54/80] Improve Adapter Javadoc --- adapter/src/main/java/com/iluwatar/adapter/App.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java index 3d10a4d41..ed036b391 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/App.java +++ b/adapter/src/main/java/com/iluwatar/adapter/App.java @@ -1,7 +1,13 @@ package com.iluwatar.adapter; /** - * + * + * An adapter helps two incompatible interfaces to work together. This is the real + * world definition for an adapter. Interfaces may be incompatible but the inner + * functionality should suit the need. The Adapter design pattern allows otherwise + * incompatible classes to work together by converting the interface of one class + * into an interface expected by the clients. + *

* There are two variations of the Adapter pattern: The class adapter implements * the adaptee's interface whereas the object adapter uses composition to * contain the adaptee in the adapter object. This example uses the object From cdd586ec7cc3e7d83bcec61235ce7e010d3115ff Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 12:40:24 +0300 Subject: [PATCH 55/80] Improve Bridge Javadoc --- bridge/src/main/java/com/iluwatar/bridge/App.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java index e8774caea..972f49b2f 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/App.java +++ b/bridge/src/main/java/com/iluwatar/bridge/App.java @@ -2,6 +2,9 @@ package com.iluwatar.bridge; /** * + * The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, + * you can decouple an abstraction from its implementation so that the two can vary independently. + *

* In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation * ({@link MagicWeaponImpl}) have their own class hierarchies. The interface of the * implementations can be changed without affecting the clients. From 9a08e35101df96536c63476b7b18abf54065121e Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 17:13:38 +0300 Subject: [PATCH 56/80] Improve Builder Javadoc --- .../src/main/java/com/iluwatar/builder/App.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java index bcdbb8821..b6131c3ab 100644 --- a/builder/src/main/java/com/iluwatar/builder/App.java +++ b/builder/src/main/java/com/iluwatar/builder/App.java @@ -4,7 +4,20 @@ import com.iluwatar. builder.Hero.HeroBuilder; /** * - * This is the Builder pattern variation as described by Joshua Bloch in + * The intention of the Builder pattern is to find a solution to the telescoping + * constructor anti-pattern. The telescoping constructor anti-pattern occurs when the + * increase of object constructor parameter combination leads to an exponential list + * of constructors. Instead of using numerous constructors, the builder pattern uses + * another object, a builder, that receives each initialization parameter step by step + * and then returns the resulting constructed object at once. + *

+ * The Builder pattern has another benefit. It can be used for objects that contain + * flat data (html code, SQL query, X.509 certificate...), that is to say, data that + * can't be easily edited. This type of data cannot be edited step by step and must + * be edited at once. The best way to construct such an object is to use a builder + * class. + *

+ * In this example we have the Builder pattern variation as described by Joshua Bloch in * Effective Java 2nd Edition. *

* We want to build {@link Hero} objects, but its construction is complex because of the From a2f3d587091fcdb7b954c22ec1d7f5e30f224a60 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 20:19:01 +0300 Subject: [PATCH 57/80] Improve Chain Javadoc --- chain/src/main/java/com/iluwatar/chain/App.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/chain/src/main/java/com/iluwatar/chain/App.java b/chain/src/main/java/com/iluwatar/chain/App.java index 5f98b2478..4d3ca69db 100644 --- a/chain/src/main/java/com/iluwatar/chain/App.java +++ b/chain/src/main/java/com/iluwatar/chain/App.java @@ -2,10 +2,16 @@ package com.iluwatar.chain; /** * - * Chain of Responsibility organizes request handlers ({@link RequestHandler}) into a - * chain where each handler has a chance to act on the request on its turn. In - * this example the king ({@link OrcKing}) makes requests and the military orcs - * ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain. + * The Chain of Responsibility pattern is a design pattern consisting of command + * objects and a series of processing objects. Each processing object contains + * logic that defines the types of command objects that it can handle; the rest are + * passed to the next processing object in the chain. A mechanism also exists for + * adding new processing objects to the end of this chain. + *

+ * In this example we organize the request handlers ({@link RequestHandler}) into a + * chain where each handler has a chance to act on the request on its turn. Here + * the king ({@link OrcKing}) makes requests and the military orcs ({@link OrcCommander}, + * {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain. * */ public class App { From d5f52edecfd8cf56ae79263035af094b5a149176 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 20:21:56 +0300 Subject: [PATCH 58/80] Improve Command Javadoc --- command/src/main/java/com/iluwatar/command/App.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/src/main/java/com/iluwatar/command/App.java b/command/src/main/java/com/iluwatar/command/App.java index fc05afa66..b421b683b 100644 --- a/command/src/main/java/com/iluwatar/command/App.java +++ b/command/src/main/java/com/iluwatar/command/App.java @@ -2,7 +2,9 @@ package com.iluwatar.command; /** * - * In Command pattern actions are objects that can be executed and undone. + * The Command pattern is a behavioral design pattern in which an object is used to encapsulate all information + * needed to perform an action or trigger an event at a later time. This information includes the method name, + * the object that owns the method and values for the method parameters. *

* Four terms always associated with the command pattern are command, receiver, invoker and client. A command * object (spell) knows about the receiver (target) and invokes a method of the receiver. Values for parameters of From 807478ab3da057f98a5b51f5f7dab5d2c8b2b7cf Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 20:27:28 +0300 Subject: [PATCH 59/80] Improve Composite Javadoc --- .../src/main/java/com/iluwatar/composite/App.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/composite/src/main/java/com/iluwatar/composite/App.java b/composite/src/main/java/com/iluwatar/composite/App.java index 7dfd15443..7bd0e4d01 100644 --- a/composite/src/main/java/com/iluwatar/composite/App.java +++ b/composite/src/main/java/com/iluwatar/composite/App.java @@ -1,10 +1,14 @@ package com.iluwatar.composite; /** - * - * With Composite we can treat tree hierarchies of objects with uniform - * interface ({@link LetterComposite}). In this example we have sentences composed of - * words composed of letters. + * The Composite pattern is a partitioning design pattern. The Composite pattern + * describes that a group of objects is to be treated in the same way as a single + * instance of an object. The intent of a composite is to "compose" objects into + * tree structures to represent part-whole hierarchies. Implementing the Composite + * pattern lets clients treat individual objects and compositions uniformly. + *

+ * In this example we have sentences composed of words composed of letters. All of + * the objects can be treated through the same interface ({@link LetterComposite}). * */ public class App { From 621793ed2d966f6dcc1cb6e390ffd92bee5fe861 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 20:34:15 +0300 Subject: [PATCH 60/80] Improved DAO Javadoc --- dao/src/main/java/com/iluwatar/dao/App.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/com/iluwatar/dao/App.java b/dao/src/main/java/com/iluwatar/dao/App.java index ac6794973..851153f22 100644 --- a/dao/src/main/java/com/iluwatar/dao/App.java +++ b/dao/src/main/java/com/iluwatar/dao/App.java @@ -5,8 +5,14 @@ import java.util.List; /** * + * Data Access Object (DAO) is an object that provides an abstract interface to some type of database or other + * persistence mechanism. By mapping application calls to the persistence layer, DAO provide some specific data + * operations without exposing details of the database. This isolation supports the Single responsibility principle. + * It separates what data accesses the application needs, in terms of domain-specific objects and data types + * (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS. + *

* With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without directly - * interacting with the data. The below example demonstrates basic operations(CRUD): select, add, update, and delete. + * interacting with the data. The below example demonstrates basic CRUD operations: select, add, update, and delete. * */ public class App { From 8cf35fc315c595b6d8f3a2cfd3b744756c94df4d Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 20:43:38 +0300 Subject: [PATCH 61/80] Improve Decorator Javadoc --- .../src/main/java/com/iluwatar/decorator/App.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/decorator/src/main/java/com/iluwatar/decorator/App.java b/decorator/src/main/java/com/iluwatar/decorator/App.java index a9eabf606..bd697d4a4 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/App.java +++ b/decorator/src/main/java/com/iluwatar/decorator/App.java @@ -2,12 +2,15 @@ package com.iluwatar.decorator; /** * - * Decorator pattern is a more flexible alternative to subclassing. The decorator + * The Decorator pattern is a more flexible alternative to subclassing. The Decorator * class implements the same interface as the target and uses composition to - * "decorate" calls to the target. + * "decorate" calls to the target. Using the Decorator pattern it is possible to + * change the behavior of the class during runtime. *

- * Using decorator pattern it is possible to change class behavior during - * runtime, as the example shows. + * In this example we show how the simple {@link Troll} first attacks and then + * flees the battle. Then we decorate the {@link Troll} with a {@link SmartTroll} + * and perform the attack again. You can see how the behavior changes after the + * decoration. * */ public class App { From e32b440a38e74520de5813e277c105e8e14074d2 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:00:21 +0300 Subject: [PATCH 62/80] Improve Double Checked Locking Javadoc --- .../main/java/com/iluwatar/doublechecked/locking/App.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java index 2a5c65813..80806e9eb 100644 --- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java @@ -5,6 +5,11 @@ import java.util.concurrent.Executors; /** * + * Double Checked Locking is a concurrency design pattern used to reduce the overhead + * of acquiring a lock by first testing the locking criterion (the "lock hint") without + * actually acquiring the lock. Only if the locking criterion check indicates that + * locking is required does the actual locking logic proceed. + *

* In {@link Inventory} we store the items with a given size. However, we do not store * more items than the inventory size. To address concurrent access problems we * use double checked locking to add item to inventory. In this method, the From 584a22238d380600c3f7a4c781fb6db0a15ed9a5 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:06:52 +0300 Subject: [PATCH 63/80] Improve Event Aggregator Javadoc --- .../src/main/java/com/iluwatar/event/aggregator/App.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java index 029489077..b69d8ceaa 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java @@ -5,8 +5,12 @@ import java.util.List; /** * - * The Event Aggregator pattern channels events from multiple objects - * into a single object to simplify registration for clients. + * A system with lots of objects can lead to complexities when a client wants to subscribe + * to events. The client has to find and register for each object individually, if each + * object has multiple events then each event requires a separate subscription. + *

+ * An Event Aggregator acts as a single source of events for many objects. It registers + * for all the events of the many objects allowing clients to register with just the aggregator. *

* In the example {@link LordBaelish}, {@link LordVarys} and {@link Scout} deliver events to * {@link KingsHand}. {@link KingsHand}, the event aggregator, then delivers the events From 516b127d217cfe5eff4cf0aea0d6edc843b6cd83 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:11:19 +0300 Subject: [PATCH 64/80] Improved Facade Javadoc --- facade/src/main/java/com/iluwatar/facade/App.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/facade/src/main/java/com/iluwatar/facade/App.java b/facade/src/main/java/com/iluwatar/facade/App.java index 0d8eccd4b..37cda0281 100644 --- a/facade/src/main/java/com/iluwatar/facade/App.java +++ b/facade/src/main/java/com/iluwatar/facade/App.java @@ -2,9 +2,15 @@ package com.iluwatar.facade; /** * - * Facade ({@link DwarvenGoldmineFacade}) provides simpler interface to subsystem. + * The Facade design pattern is often used when a system is very complex or difficult + * to understand because the system has a large number of interdependent classes or + * its source code is unavailable. This pattern hides the complexities of the larger + * system and provides a simpler interface to the client. It typically involves a single + * wrapper class which contains a set of members required by client. These members access + * the system on behalf of the facade client and hide the implementation details. *

- * http://en.wikipedia.org/wiki/Facade_pattern + * In this example the Facade is ({@link DwarvenGoldmineFacade}) and it provides a simpler + * interface to the goldmine subsystem. * */ public class App { From c989f6cb219ba38ee0c2a76852065095f762f9a8 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:14:40 +0300 Subject: [PATCH 65/80] Improve Factory Method Javadoc --- .../src/main/java/com/iluwatar/factory/method/App.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/App.java b/factory-method/src/main/java/com/iluwatar/factory/method/App.java index 69bda3489..118413564 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/App.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/App.java @@ -2,7 +2,14 @@ package com.iluwatar.factory.method; /** * - * In Factory Method we have an interface ({@link Blacksmith}) with a method for + * The Factory Method is a creational design pattern which uses factory methods to deal + * with the problem of creating objects without specifying the exact class of object + * that will be created. This is done by creating objects via calling a factory + * method either specified in an interface and implemented by child classes, or implemented + * in a base class and optionally overridden by derived classes—rather than by calling a + * constructor. + *

+ * In this Factory Method example we have an interface ({@link Blacksmith}) with a method for * creating objects ({@link Blacksmith#manufactureWeapon}). The concrete subclasses * ({@link OrcBlacksmith}, {@link ElfBlacksmith}) then override the method to produce * objects of their liking. From c27291fd27c4f8d9e6aef22fc4c91fcc0e5c19f8 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:17:56 +0300 Subject: [PATCH 66/80] Improve Fluent Interface Javadoc --- .../java/com/iluwatar/fluentinterface/app/App.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java index 81a1ccb6a..7733df37d 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java @@ -11,15 +11,14 @@ import java.util.function.Predicate; import static java.lang.String.valueOf; /** - * Fluent interface pattern is useful when you want to provide an easy readable, flowing API. Those - * interfaces tend to mimic domain specific languages, so they can nearly be read as human - * languages. + * The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those + * interfaces tend to mimic domain specific languages, so they can nearly be read as human languages. *

* In this example two implementations of a {@link FluentIterable} interface are given. The - * SimpleFluentIterable evaluates eagerly and would be too costly for real world applications. The - * LazyFluentIterable is evaluated on termination. Their usage is demonstrated with a simple number - * list that is filtered, transformed and collected. The result is printed afterwards. - *

+ * {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world applications. + * The {@link LazyFluentIterable} is evaluated on termination. Their usage is demonstrated with a + * simple number list that is filtered, transformed and collected. The result is printed afterwards. + * */ public class App { From b4118bb8668b2574094483d8357fddb80ce699d3 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:28:17 +0300 Subject: [PATCH 67/80] Improve Intercepting Filter Javadoc --- .../com/iluwatar/intercepting/filter/App.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java index 7cdb17fb8..c913da66c 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java @@ -1,9 +1,33 @@ package com.iluwatar.intercepting.filter; /** - * - * This is an app that checks whether the order request is valid through pre-processing done via {@link Filter}. - * Each field has its own corresponding {@link Filter} + * + * When a request enters a Web application, it often must pass several entrance + * tests prior to the main processing stage. For example, + * - Has the client been authenticated? + * - Does the client have a valid session? + * - Is the client's IP address from a trusted network? + * - Does the request path violate any constraints? + * - What encoding does the client use to send the data? + * - Do we support the browser type of the client? + * Some of these checks are tests, resulting in a yes or no answer that determines + * whether processing will continue. Other checks manipulate the incoming data + * stream into a form suitable for processing. + *

+ * The classic solution consists of a series of conditional checks, with any failed + * check aborting the request. Nested if/else statements are a standard strategy, + * but this solution leads to code fragility and a copy-and-paste style of programming, + * because the flow of the filtering and the action of the filters is compiled into + * the application. + *

+ * The key to solving this problem in a flexible and unobtrusive manner is to have a + * simple mechanism for adding and removing processing components, in which each + * component completes a specific filtering action. This is the Intercepting Filter + * pattern in action. + *

+ * In this example we check whether the order request is valid through pre-processing + * done via {@link Filter}. Each field has its own corresponding {@link Filter} + *

* @author joshzambales * */ From 44d7be9c94d23fbab3c09a6b0849909868869c2a Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:32:56 +0300 Subject: [PATCH 68/80] Improve Interpreter Javadoc --- .../src/main/java/com/iluwatar/interpreter/App.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/App.java b/interpreter/src/main/java/com/iluwatar/interpreter/App.java index 2273b386b..955563915 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/App.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/App.java @@ -4,8 +4,14 @@ import java.util.Stack; /** * - * Interpreter pattern breaks sentences into expressions ({@link Expression}) that can - * be evaluated and as a whole form the result. + * The Interpreter pattern is a design pattern that specifies how to evaluate sentences + * in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) + * in a specialized computer language. The syntax tree of a sentence in the language is an + * instance of the composite pattern and is used to evaluate (interpret) the sentence for a + * client. + *

+ * In this example we use the Interpreter pattern to break sentences into expressions + * ({@link Expression}) that can be evaluated and as a whole form the result. * */ public class App { From e4ff39e08098da9fab6ca41a544e720d7fac6d03 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 21:37:29 +0300 Subject: [PATCH 69/80] Improve Iterator Javadoc --- iterator/src/main/java/com/iluwatar/iterator/App.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/iterator/src/main/java/com/iluwatar/iterator/App.java b/iterator/src/main/java/com/iluwatar/iterator/App.java index 5b59cbcbc..b8ecfa42c 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/App.java +++ b/iterator/src/main/java/com/iluwatar/iterator/App.java @@ -2,9 +2,13 @@ package com.iluwatar.iterator; /** * - * Iterator ({@link ItemIterator}) adds abstraction layer on top of a collection - * ({@link TreasureChest}). This way the collection can change its internal - * implementation without affecting its clients. + * The Iterator pattern is a design pattern in which an iterator is used to + * traverse a container and access the container's elements. The Iterator pattern + * decouples algorithms from containers. + *

+ * In this example the Iterator ({@link ItemIterator}) adds abstraction layer on + * top of a collection ({@link TreasureChest}). This way the collection can change + * its internal implementation without affecting its clients. * */ public class App { From fc66ae80849fd8938267f50618bf82544efc7208 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 23:34:20 +0300 Subject: [PATCH 70/80] Improve Mediator Javadoc --- .../main/java/com/iluwatar/mediator/App.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mediator/src/main/java/com/iluwatar/mediator/App.java b/mediator/src/main/java/com/iluwatar/mediator/App.java index a7df7d39f..9648ac608 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/App.java +++ b/mediator/src/main/java/com/iluwatar/mediator/App.java @@ -2,8 +2,26 @@ package com.iluwatar.mediator; /** * - * Mediator encapsulates how a set of objects ({@link PartyMember}) interact. Instead of - * referring to each other directly they use a mediator ({@link Party}) interface. + * The Mediator pattern defines an object that encapsulates how a set of objects + * interact. This pattern is considered to be a behavioral pattern due to the way + * it can alter the program's running behavior. + *

+ * Usually a program is made up of a large number of classes. So the logic and + * computation is distributed among these classes. However, as more classes are + * developed in a program, especially during maintenance and/or refactoring, + * the problem of communication between these classes may become more complex. + * This makes the program harder to read and maintain. Furthermore, it can become + * difficult to change the program, since any change may affect code in several + * other classes. + *

+ * With the Mediator pattern, communication between objects is encapsulated with + * a mediator object. Objects no longer communicate directly with each other, but + * instead communicate through the mediator. This reduces the dependencies between + * communicating objects, thereby lowering the coupling. + *

+ * In this example the mediator encapsulates how a set of objects ({@link PartyMember}) + * interact. Instead of referring to each other directly they use the mediator + * ({@link Party}) interface. * */ public class App { From ca6bb7a3a878bd6c24fd607af73f4b310c21358a Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 23:38:37 +0300 Subject: [PATCH 71/80] Improve Memento Javadoc --- .../src/main/java/com/iluwatar/memento/App.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/memento/src/main/java/com/iluwatar/memento/App.java b/memento/src/main/java/com/iluwatar/memento/App.java index 623ffb00e..71d0ed466 100644 --- a/memento/src/main/java/com/iluwatar/memento/App.java +++ b/memento/src/main/java/com/iluwatar/memento/App.java @@ -4,7 +4,20 @@ import java.util.Stack; /** * - * Memento pattern is for storing and restoring object state. The object ({@link Star}) + * The Memento pattern is a software design pattern that provides the ability to restore + * an object to its previous state (undo via rollback). + *

+ * The Memento pattern is implemented with three objects: the originator, a caretaker and + * a memento. The originator is some object that has an internal state. The caretaker is + * going to do something to the originator, but wants to be able to undo the change. The + * caretaker first asks the originator for a memento object. Then it does whatever operation + * (or sequence of operations) it was going to do. To roll back to the state before the + * operations, it returns the memento object to the originator. The memento object itself + * is an opaque object (one which the caretaker cannot, or should not, change). When using + * this pattern, care should be taken if the originator may change other objects or + * resources - the memento pattern operates on a single object. + *

+ * In this example the object ({@link Star}) * gives out a "memento" ({@link StarMemento}) that contains the state of the object. * Later on the memento can be set back to the object restoring the state. * From 4d1aae21f74e7762d115ef3e369d18b8b45cd1dc Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 23:48:51 +0300 Subject: [PATCH 72/80] Improve Observer Javadoc --- .../src/main/java/com/iluwatar/observer/App.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/observer/src/main/java/com/iluwatar/observer/App.java b/observer/src/main/java/com/iluwatar/observer/App.java index bd99da841..bc4742a14 100644 --- a/observer/src/main/java/com/iluwatar/observer/App.java +++ b/observer/src/main/java/com/iluwatar/observer/App.java @@ -6,8 +6,17 @@ import com.iluwatar.observer.generic.GWeather; /** * - * Observer pattern defines one-to-many relationship between objects. The target - * object sends change notifications to its registered observers. + * The Observer pattern is a software design pattern in which an object, called + * the subject, maintains a list of its dependents, called observers, and notifies + * them automatically of any state changes, usually by calling one of their methods. + * It is mainly used to implement distributed event handling systems. The Observer + * pattern is also a key part in the familiar model–view–controller (MVC) architectural + * pattern. The Observer pattern is implemented in numerous programming libraries and + * systems, including almost all GUI toolkits. + *

+ * In this example {@link Weather} has a state that can be observed. The {@link Orcs} + * and {@link Hobbits} register as observers and receive notifications when the + * {@link Weather} changes. * */ public class App { From 98aa28d94e76157e2c7124db49ac930f1412b220 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 23:53:09 +0300 Subject: [PATCH 73/80] Improve Poison Pill Javadoc --- .../src/main/java/com/iluwatar/poison/pill/App.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java index 526ca5b86..91d30fddd 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java @@ -2,12 +2,12 @@ package com.iluwatar.poison.pill; /** * One of the possible approaches to terminate Producer-Consumer pattern is using the Poison Pill idiom. - * If you use Poison Pill as the termination signal then Producer is responsible to notify Consumer that exchange is over - * and reject any further messages. Consumer receiving Poison Pill will stop reading messages from the queue. - * You must also ensure that the Poison Pill will be the last message that will be read from the queue (if you have - * prioritized queue then this can be tricky). + * If you use Poison Pill as the termination signal then Producer is responsible to notify Consumer that + * the exchange is over and reject any further messages. The Consumer receiving Poison Pill will stop + * reading messages from the queue. You must also ensure that the Poison Pill will be the last message + * that will be read from the queue (if you have prioritized queue then this can be tricky). *

- * In simple cases as Poison Pill can be used just null-reference, but holding unique separate shared + * In simple cases the Poison Pill can be just a null-reference, but holding a unique separate shared * object-marker (with name "Poison" or "Poison Pill") is more clear and self describing. * */ From 40e378c1f3ed1407a4918c1d7bd5ff1e09730adc Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sat, 3 Oct 2015 23:57:17 +0300 Subject: [PATCH 74/80] Improve Property Javadoc --- property/src/main/java/com/iluwatar/property/App.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/property/src/main/java/com/iluwatar/property/App.java b/property/src/main/java/com/iluwatar/property/App.java index 54c140574..ac46f5448 100644 --- a/property/src/main/java/com/iluwatar/property/App.java +++ b/property/src/main/java/com/iluwatar/property/App.java @@ -4,12 +4,14 @@ import com.iluwatar.property.Character.Type; /** * - * Example of {@link Character} instantiation using the Property pattern (also known as Prototype inheritance). + * The Property pattern is also known as Prototype inheritance. *

* In prototype inheritance instead of classes, as opposite to Java class inheritance, * objects are used to create another objects and object hierarchies. Hierarchies are created using prototype chain * through delegation: every object has link to parent object. Any base (parent) object can be amended at runtime * (by adding or removal of some property), and all child objects will be affected as result. + *

+ * In this example we demonstrate {@link Character} instantiation using the Property pattern. * */ public class App { From 9a4f83e7b804af6620ab03106d7ec613cacfc4a9 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sun, 4 Oct 2015 00:04:12 +0300 Subject: [PATCH 75/80] Improve Prototype Javadoc --- .../src/main/java/com/iluwatar/prototype/App.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/prototype/src/main/java/com/iluwatar/prototype/App.java b/prototype/src/main/java/com/iluwatar/prototype/App.java index 4003862cb..74bb6989d 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/App.java +++ b/prototype/src/main/java/com/iluwatar/prototype/App.java @@ -2,9 +2,14 @@ package com.iluwatar.prototype; /** * - * In Prototype we have a factory class ({@link HeroFactoryImpl}) producing objects by - * cloning the existing ones. In this example the factory's prototype objects are - * given as constructor parameters. + * The Prototype pattern is a creational design pattern in software development. It is + * used when the type of objects to create is determined by a prototypical instance, + * which is cloned to produce new objects. This pattern is used to: + * - avoid subclasses of an object creator in the client application, like the abstract factory pattern does. + * - avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) + *

+ * In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by + * cloning the existing ones. The factory's prototype objects are given as constructor parameters. * */ public class App { From c3a827b475c895493fd2d4256c46b817856eaee1 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sun, 4 Oct 2015 00:08:34 +0300 Subject: [PATCH 76/80] Improved Proxy Javadoc --- proxy/src/main/java/com/iluwatar/proxy/App.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/iluwatar/proxy/App.java b/proxy/src/main/java/com/iluwatar/proxy/App.java index fb401ac9f..420ad5c0a 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/App.java +++ b/proxy/src/main/java/com/iluwatar/proxy/App.java @@ -2,7 +2,18 @@ package com.iluwatar.proxy; /** * - * Proxy ({@link WizardTowerProxy}) controls access to the actual object ({@link WizardTower}). + * A proxy, in its most general form, is a class functioning as an interface to something else. + * The proxy could interface to anything: a network connection, a large object in memory, a file, + * or some other resource that is expensive or impossible to duplicate. In short, a proxy is a + * wrapper or agent object that is being called by the client to access the real serving object + * behind the scenes. + *

+ * The Proxy design pattern allows you to provide an interface to other objects by creating a + * wrapper class as the proxy. The wrapper class, which is the proxy, can add additional + * functionality to the object of interest without changing the object's code. + *

+ * In this example the proxy ({@link WizardTowerProxy}) controls access to the actual object + * ({@link WizardTower}). * */ public class App { From 8db2bb2ac83eacc72a7cbf8f7fdaba58a544fc0d Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sun, 4 Oct 2015 00:13:08 +0300 Subject: [PATCH 77/80] Improve Servant Javadoc --- servant/src/main/java/com/iluwatar/servant/App.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/servant/src/main/java/com/iluwatar/servant/App.java b/servant/src/main/java/com/iluwatar/servant/App.java index 6a0ba6ca4..832d3d52c 100644 --- a/servant/src/main/java/com/iluwatar/servant/App.java +++ b/servant/src/main/java/com/iluwatar/servant/App.java @@ -7,6 +7,8 @@ import java.util.ArrayList; * Servant offers some functionality to a group of classes without defining that functionality in each of them. * A Servant is a class whose instance provides methods that take care of a desired service, * while objects for which the servant does something, are taken as parameters. + *

+ * In this example {@link Servant} is serving {@link King} and {@link Queen}. * */ public class App { From 74e32259bef4aae0b856552d8b6944f7e91a333e Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sun, 4 Oct 2015 00:16:05 +0300 Subject: [PATCH 78/80] Improve Service Locator Javadoc --- .../src/main/java/com/iluwatar/servicelocator/App.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/App.java b/service-locator/src/main/java/com/iluwatar/servicelocator/App.java index f86e1cc32..5b6615495 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/App.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/App.java @@ -1,9 +1,15 @@ package com.iluwatar.servicelocator; /** - * Service locator pattern, used to lookup JNDI-services + * + * The Service Locator pattern is a design pattern used in software development + * to encapsulate the processes involved in obtaining a service with a strong + * abstraction layer. This pattern uses a central registry known as the "service + * locator", which on request returns the information necessary to perform a certain task. + *

+ * In this example we use the Service locator pattern to lookup JNDI-services * and cache them for subsequent requests. - * + *

* @author saifasif * */ From 2234a25c76d0e926c5e078883c9553a670f78cc8 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sun, 4 Oct 2015 00:19:34 +0300 Subject: [PATCH 79/80] Improve State Javadoc --- state/src/main/java/com/iluwatar/state/App.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/state/src/main/java/com/iluwatar/state/App.java b/state/src/main/java/com/iluwatar/state/App.java index 5b39d02f7..95d411076 100644 --- a/state/src/main/java/com/iluwatar/state/App.java +++ b/state/src/main/java/com/iluwatar/state/App.java @@ -2,10 +2,14 @@ package com.iluwatar.state; /** * - * In State pattern the container object ({@link Mammoth}) has an internal state object ({@link State}) that + * In State pattern the container object has an internal state object that * defines the current behavior. The state object can be changed to alter the * behavior. *

+ * This can be a cleaner way for an object to change its behavior at runtime + * without resorting to large monolithic conditional statements and thus improves + * maintainability. + *

* In this example the {@link Mammoth} changes its behavior as time passes by. * */ From 0a9879a277eb7c3b928c687a2e2ca945970a6517 Mon Sep 17 00:00:00 2001 From: Ilkka Seppala Date: Sun, 4 Oct 2015 00:22:27 +0300 Subject: [PATCH 80/80] Improve Strategy Javadoc --- strategy/src/main/java/com/iluwatar/strategy/App.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/strategy/src/main/java/com/iluwatar/strategy/App.java b/strategy/src/main/java/com/iluwatar/strategy/App.java index 992514801..b88eae242 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/App.java +++ b/strategy/src/main/java/com/iluwatar/strategy/App.java @@ -2,7 +2,10 @@ package com.iluwatar.strategy; /** * - * Strategy ({@link DragonSlayingStrategy}) encapsulates an algorithm. The containing + * The Strategy pattern (also known as the policy pattern) is a software design pattern that + * enables an algorithm's behavior to be selected at runtime. + *

+ * In this example ({@link DragonSlayingStrategy}) encapsulates an algorithm. The containing * object ({@link DragonSlayer}) can alter its behavior by changing its strategy. * */