Resolves checkstyle errors for patterns starting with letter r (#1072)

* Reduces checkstyle errors in reactor

* Reduces checkstyle errors in reader-writer-lock

* Reduces checkstyle errors in repository

* Reduces checkstyle errors in resource-acquisition-is-initialization

* Reduces checkstyle errors in retry
This commit is contained in:
Anurag Agarwal 2019-11-10 23:12:26 +05:30 committed by Ilkka Seppälä
parent 4dae1fae57
commit 9c8ad4485b
31 changed files with 345 additions and 377 deletions

View File

@ -23,10 +23,6 @@
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;
@ -34,19 +30,20 @@ import com.iluwatar.reactor.framework.NioDatagramChannel;
import com.iluwatar.reactor.framework.NioReactor;
import com.iluwatar.reactor.framework.NioServerSocketChannel;
import com.iluwatar.reactor.framework.ThreadPoolDispatcher;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 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.
*
* <p>
* <i>INTENT</i> <br>
*
* <p><i>INTENT</i> <br>
* 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.
*
* <p>
* <i>PROBLEM</i> <br>
*
* <p><i>PROBLEM</i> <br>
* Server applications in a distributed system must handle multiple clients that send them service
* requests. Following forces need to be resolved:
* <ul>
@ -55,9 +52,8 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher;
* <li>Programming Simplicity</li>
* <li>Adaptability</li>
* </ul>
*
* <p>
* <i>PARTICIPANTS</i> <br>
*
* <p><i>PARTICIPANTS</i> <br>
* <ul>
* <li>Synchronous Event De-multiplexer
* <p>
@ -89,7 +85,6 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher;
* 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.
*
*/
public class App {
@ -100,7 +95,7 @@ public class App {
/**
* 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) {
@ -142,9 +137,9 @@ 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
* @throws IOException if any I/O error occurs
*/
public void stop() throws InterruptedException, IOException {
reactor.stop();

View File

@ -23,9 +23,6 @@
package com.iluwatar.reactor.app;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -39,6 +36,8 @@ import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents the clients of Reactor pattern. Multiple clients are run concurrently and send logging
@ -158,7 +157,7 @@ public class AppClient {
* 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.
* @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 {

View File

@ -23,12 +23,11 @@
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;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -63,7 +62,11 @@ public class LoggingHandler implements ChannelHandler {
}
}
private static void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) {
private static void sendReply(
AbstractNioChannel channel,
DatagramPacket incomingPacket,
SelectionKey key
) {
/*
* Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming
* message.

View File

@ -35,13 +35,12 @@ import java.util.concurrent.ConcurrentLinkedQueue;
/**
* This represents the <i>Handle</i> of Reactor pattern. These are resources managed by OS which can
* be submitted to {@link NioReactor}.
*
* <p>
* 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.
*
* <p>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.
*/
public abstract class AbstractNioChannel {
@ -53,7 +52,7 @@ public abstract class AbstractNioChannel {
/**
* Creates a new channel.
*
*
* @param handler which will handle events occurring on this channel.
* @param channel a NIO channel to be wrapped.
*/
@ -70,6 +69,8 @@ public abstract class AbstractNioChannel {
}
/**
* Get channel.
*
* @return the wrapped NIO channel.
*/
public SelectableChannel getJavaChannel() {
@ -77,9 +78,9 @@ public abstract class AbstractNioChannel {
}
/**
* The operation in which the channel is interested, this operation is provided to
* {@link Selector}.
*
* The operation in which the channel is interested, this operation is provided to {@link
* Selector}.
*
* @return interested operation.
* @see SelectionKey
*/
@ -87,7 +88,7 @@ public abstract class AbstractNioChannel {
/**
* Binds the channel on provided port.
*
*
* @throws IOException if any I/O error occurs.
*/
public abstract void bind() throws IOException;
@ -95,7 +96,7 @@ public abstract class AbstractNioChannel {
/**
* 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.
@ -103,6 +104,8 @@ public abstract class AbstractNioChannel {
public abstract Object read(SelectionKey key) throws IOException;
/**
* Get handler.
*
* @return the handler associated with this channel.
*/
public ChannelHandler getHandler() {
@ -130,9 +133,9 @@ public abstract class AbstractNioChannel {
/**
* Writes the data to the channel.
*
*
* @param pendingWrite the data to be written on channel.
* @param key the key which is writable.
* @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;
@ -140,24 +143,23 @@ public abstract class AbstractNioChannel {
/**
* 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.
*
* <p>
* This method is used by the {@link ChannelHandler} to send reply back to the client. <br>
*
* <p>This method is used by the {@link ChannelHandler} to send reply back to the client. <br>
* Example:
*
*
* <pre>
* <code>
* {@literal @}Override
* public void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key) {
* byte[] data = ((ByteBuffer)readObject).array();
* public void handleChannelRead(AbstractNioChannel channel, Object readObj, SelectionKey key) {
* byte[] data = ((ByteBuffer)readObj).array();
* ByteBuffer buffer = ByteBuffer.wrap("Server reply".getBytes());
* channel.write(buffer, key);
* }
* </code>
* </pre>
*
*
* @param data the data to be written on underlying channel.
* @param key the key which is writable.
* @param key the key which is writable.
*/
public void write(Object data, SelectionKey key) {
Queue<Object> pendingWrites = this.channelToPendingWrites.get(key.channel());

View File

@ -28,19 +28,19 @@ import java.nio.channels.SelectionKey;
/**
* Represents the <i>EventHandler</i> of Reactor pattern. It handles the incoming events dispatched
* to it by the {@link Dispatcher}. This is where the application logic resides.
*
* <p>
* 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.
*
* <p>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.
*/
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 channel the channel from which the data was received.
* @param readObject the data read.
* @param key the key on which read event occurred.
* @param key the key on which read event occurred.
*/
void handleChannelRead(AbstractNioChannel channel, Object readObject, SelectionKey key);
}

View File

@ -29,14 +29,12 @@ 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. <br>
* Dispatcher should call the {@link ChannelHandler} associated with the channel on which event
* occurred.
*
* <p>
* The application can customize the way in which event is dispatched such as using the reactor
* I/O processing from application specific processing. <br> Dispatcher should call the {@link
* ChannelHandler} associated with the channel on which event occurred.
*
* <p>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
*/
@ -45,19 +43,18 @@ public interface Dispatcher {
* This hook method is called when read event occurs on particular channel. The data read is
* provided in <code>readObject</code>. The implementation should dispatch this read event to the
* associated {@link ChannelHandler} of <code>channel</code>.
*
* <p>
* The type of <code>readObject</code> depends on the channel on which data was received.
*
* @param channel on which read event occurred
*
* <p>The type of <code>readObject</code> 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
* @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;

View File

@ -23,9 +23,6 @@
package com.iluwatar.reactor.framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -33,6 +30,8 @@ import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A wrapper over {@link DatagramChannel} which can read and write data on a DatagramChannel.
@ -46,11 +45,11 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Creates a {@link DatagramChannel} which will bind at provided port and use <code>handler</code>
* to handle incoming events on this channel.
* <p>
* Note the constructor does not bind the socket, {@link #bind()} method should be called for
*
* <p>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 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.
*/
@ -69,7 +68,7 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Reads and returns a {@link DatagramPacket} from the underlying channel.
*
*
* @return the datagram packet read having the sender address.
*/
@Override
@ -89,6 +88,8 @@ public class NioDatagramChannel extends AbstractNioChannel {
}
/**
* Get datagram channel.
*
* @return the underlying datagram channel.
*/
@Override
@ -98,7 +99,7 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Binds UDP socket on the provided <code>port</code>.
*
*
* @throws IOException if any I/O error occurs.
*/
@Override
@ -120,8 +121,8 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Writes the outgoing {@link DatagramPacket} to the channel. The intended receiver of the
* datagram packet must be set in the <code>data</code> using
* {@link DatagramPacket#setReceiver(SocketAddress)}.
* datagram packet must be set in the <code>data</code> using {@link
* DatagramPacket#setReceiver(SocketAddress)}.
*/
@Override
public void write(Object data, SelectionKey key) {
@ -138,7 +139,7 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Creates a container with underlying data.
*
*
* @param data the underlying message to be written on channel.
*/
public DatagramPacket(ByteBuffer data) {
@ -146,6 +147,8 @@ public class NioDatagramChannel extends AbstractNioChannel {
}
/**
* Get sender address.
*
* @return the sender address.
*/
public SocketAddress getSender() {
@ -154,7 +157,7 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Sets the sender address of this packet.
*
*
* @param sender the sender address.
*/
public void setSender(SocketAddress sender) {
@ -162,6 +165,8 @@ public class NioDatagramChannel extends AbstractNioChannel {
}
/**
* Get receiver address.
*
* @return the receiver address.
*/
public SocketAddress getReceiver() {
@ -170,7 +175,7 @@ public class NioDatagramChannel extends AbstractNioChannel {
/**
* Sets the intended receiver address. This must be set when writing to the channel.
*
*
* @param receiver the receiver address.
*/
public void setReceiver(SocketAddress receiver) {
@ -178,6 +183,8 @@ public class NioDatagramChannel extends AbstractNioChannel {
}
/**
* Get data.
*
* @return the underlying message that will be written on channel.
*/
public ByteBuffer getData() {

View File

@ -23,9 +23,6 @@
package com.iluwatar.reactor.framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
@ -38,22 +35,23 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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}.
*
* <p>
* 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.
*
* <p>
* 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.
* 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}.
*
* <p>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.
*
* <p>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.
*/
public class NioReactor {
@ -62,21 +60,20 @@ public class NioReactor {
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.
* 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<Runnable> pendingCommands = new ConcurrentLinkedQueue<>();
private final 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.
* 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;
@ -99,11 +96,9 @@ 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.
*
* @throws InterruptedException if interrupted while stopping the reactor.
* @throws IOException if any I/O error occurs.
*/
public void stop() throws InterruptedException, IOException {
reactorMain.shutdownNow();
@ -114,15 +109,14 @@ public class NioReactor {
}
/**
* 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.
* 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.
* @throws IOException if any I/O error occurs.
*/
public NioReactor registerChannel(AbstractNioChannel channel) throws IOException {
SelectionKey key = channel.getJavaChannel().register(selector, channel.getInterestedOps());
@ -143,8 +137,8 @@ public class NioReactor {
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.
* 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();
@ -177,8 +171,8 @@ public class NioReactor {
}
/*
* Initiation dispatcher logic, it checks the type of event and notifier application specific event handler to handle
* the event.
* 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()) {
@ -226,15 +220,14 @@ public class NioReactor {
}
/**
* Queues the change of operations request of a channel, which will change the interested operations of the channel
* sometime in future.
* <p>
* 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.
* Queues the change of operations request of a channel, which will change the interested
* operations of the channel sometime in future.
*
* <p>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));

View File

@ -23,9 +23,6 @@
package com.iluwatar.reactor.framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@ -33,10 +30,12 @@ import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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}.
*/
public class NioServerSocketChannel extends AbstractNioChannel {
@ -47,11 +46,11 @@ public class NioServerSocketChannel extends AbstractNioChannel {
/**
* Creates a {@link ServerSocketChannel} which will bind at provided port and use
* <code>handler</code> to handle incoming events on this channel.
* <p>
* Note the constructor does not bind the socket, {@link #bind()} method should be called for
*
* <p>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 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.
*/
@ -68,6 +67,8 @@ public class NioServerSocketChannel extends AbstractNioChannel {
}
/**
* Get server socket channel.
*
* @return the underlying {@link ServerSocketChannel}.
*/
@Override
@ -94,7 +95,7 @@ public class NioServerSocketChannel extends AbstractNioChannel {
/**
* Binds TCP socket on the provided <code>port</code>.
*
*
* @throws IOException if any I/O error occurs.
*/
@Override

View File

@ -29,18 +29,16 @@ 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.
*
* <p>
* For better performance use {@link ThreadPoolDispatcher}.
*
*
* <p>For better performance use {@link ThreadPoolDispatcher}.
*
* @see ThreadPoolDispatcher
*/
public class SameThreadDispatcher implements Dispatcher {
/**
* Dispatches the read event in the context of caller thread. <br>
* Note this is a blocking call. It returns only after the associated handler has handled the read
* event.
* Dispatches the read event in the context of caller thread. <br> 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) {

View File

@ -39,7 +39,7 @@ public class ThreadPoolDispatcher implements Dispatcher {
/**
* Creates a pooled dispatcher with tunable pool size.
*
*
* @param poolSize number of pooled threads
*/
public ThreadPoolDispatcher(int poolSize) {
@ -48,9 +48,8 @@ public class ThreadPoolDispatcher implements Dispatcher {
/**
* Submits the work of dispatching the read event to worker pool, where it gets picked up by
* worker threads. <br>
* Note that this is a non-blocking call and returns immediately. It is not guaranteed that the
* event has been handled by associated handler.
* worker threads. <br> 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) {
@ -59,7 +58,7 @@ public class ThreadPoolDispatcher implements Dispatcher {
/**
* Stops the pool of workers.
*
*
* @throws InterruptedException if interrupted while stopping pool of workers.
*/
@Override

View File

@ -28,26 +28,23 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* In a multiple thread applications, the threads may try to synchronize the shared resources
* regardless of read or write operation. It leads to a low performance especially in a "read more
* write less" system as indeed the read operations are thread-safe to another read operation.
* <p>
* Reader writer lock is a synchronization primitive that try to resolve this problem. This pattern
* allows concurrent access for read-only operations, while write operations require exclusive
* access. This means that multiple threads can read the data in parallel but an exclusive lock is
* needed for writing or modifying data. When a writer is writing the data, all other writers or
* readers will be blocked until the writer is finished writing.
*
* <p>
* This example use two mutex to demonstrate the concurrent access of multiple readers and writers.
*
*
*
* <p>Reader writer lock is a synchronization primitive that try to resolve this problem. This
* pattern allows concurrent access for read-only operations, while write operations require
* exclusive access. This means that multiple threads can read the data in parallel but an exclusive
* lock is needed for writing or modifying data. When a writer is writing the data, all other
* writers or readers will be blocked until the writer is finished writing.
*
* <p>This example use two mutex to demonstrate the concurrent access of multiple readers and
* writers.
*
* @author hongshuwei@gmail.com
*/
public class App {
@ -55,27 +52,27 @@ public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point
*
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
ExecutorService executeService = Executors.newFixedThreadPool(10);
ReaderWriterLock lock = new ReaderWriterLock();
// Start writers
IntStream.range(0, 5)
.forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock(),
.forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock(),
ThreadLocalRandom.current().nextLong(5000))));
LOGGER.info("Writers added...");
// Start readers
IntStream.range(0, 5)
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock(),
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock(),
ThreadLocalRandom.current().nextLong(10))));
LOGGER.info("Readers added...");
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
@ -85,11 +82,10 @@ public class App {
// Start readers
IntStream.range(6, 10)
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock(),
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock(),
ThreadLocalRandom.current().nextLong(10))));
LOGGER.info("More readers added...");
// In the system console, it can see that the read operations are executed concurrently while
// write operations are exclusive.

View File

@ -24,12 +24,11 @@
package com.iluwatar.reader.writer.lock;
import java.util.concurrent.locks.Lock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reader class, read when it acquired the read lock
* Reader class, read when it acquired the read lock.
*/
public class Reader implements Runnable {
@ -38,14 +37,14 @@ public class Reader implements Runnable {
private Lock readLock;
private String name;
private long readingTime;
/**
* Create new Reader
*
* @param name - Name of the thread owning the reader
* @param readLock - Lock for this reader
* Create new Reader.
*
* @param name - Name of the thread owning the reader
* @param readLock - Lock for this reader
* @param readingTime - amount of time (in milliseconds) for this reader to engage reading
*/
public Reader(String name, Lock readLock, long readingTime) {
@ -53,11 +52,11 @@ public class Reader implements Runnable {
this.readLock = readLock;
this.readingTime = readingTime;
}
/**
* Create new Reader who reads for 250ms
*
* @param name - Name of the thread owning the reader
* Create new Reader who reads for 250ms.
*
* @param name - Name of the thread owning the reader
* @param readLock - Lock for this reader
*/
public Reader(String name, Lock readLock) {
@ -78,8 +77,7 @@ public class Reader implements Runnable {
}
/**
* Simulate the read operation
*
* Simulate the read operation.
*/
public void read() throws InterruptedException {
LOGGER.info("{} begin", name);

View File

@ -29,18 +29,17 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class responsible for control the access for reader or writer
*
* Allows multiple readers to hold the lock at same time, but if any writer holds the lock then readers wait. If reader
* holds the lock then writer waits. This lock is not fair.
*
* <p>Allows multiple readers to hold the lock at same time, but if any writer holds the lock then
* readers wait. If reader holds the lock then writer waits. This lock is not fair.
*/
public class ReaderWriterLock implements ReadWriteLock {
private static final Logger LOGGER = LoggerFactory.getLogger(ReaderWriterLock.class);
@ -50,13 +49,13 @@ public class ReaderWriterLock implements ReadWriteLock {
/**
* Global mutex is used to indicate that whether reader or writer gets the lock in the moment.
* <p>
* 1. When it contains the reference of {@link #readerLock}, it means that the lock is acquired by the reader, another
* reader can also do the read operation concurrently. <br>
* 2. When it contains the reference of reference of {@link #writerLock}, it means that the lock is acquired by the
* writer exclusively, no more reader or writer can get the lock.
* <p>
* This is the most important field in this class to control the access for reader/writer.
*
* <p>1. When it contains the reference of {@link #readerLock}, it means that the lock is
* acquired by the reader, another reader can also do the read operation concurrently. <br> 2.
* When it contains the reference of reference of {@link #writerLock}, it means that the lock is
* acquired by the writer exclusively, no more reader or writer can get the lock.
*
* <p>This is the most important field in this class to control the access for reader/writer.
*/
private Set<Object> globalMutex = new HashSet<>();
@ -74,22 +73,21 @@ public class ReaderWriterLock implements ReadWriteLock {
}
/**
* return true when globalMutex hold the reference of writerLock
* return true when globalMutex hold the reference of writerLock.
*/
private boolean doesWriterOwnThisLock() {
return globalMutex.contains(writerLock);
}
/**
* Nobody get the lock when globalMutex contains nothing
*
* Nobody get the lock when globalMutex contains nothing.
*/
private boolean isLockFree() {
return globalMutex.isEmpty();
}
/**
* Reader Lock, can be access for more than one reader concurrently if no writer get the lock
* Reader Lock, can be access for more than one reader concurrently if no writer get the lock.
*/
private class ReadLock implements Lock {
@ -104,8 +102,8 @@ public class ReaderWriterLock implements ReadWriteLock {
}
/**
* Acquire the globalMutex lock on behalf of current and future concurrent readers. Make sure no writers currently
* owns the lock.
* Acquire the globalMutex lock on behalf of current and future concurrent readers. Make sure no
* writers currently owns the lock.
*/
private void acquireForReaders() {
// Try to get the globalMutex lock for the first reader
@ -116,7 +114,8 @@ public class ReaderWriterLock implements ReadWriteLock {
try {
globalMutex.wait();
} catch (InterruptedException e) {
LOGGER.info("InterruptedException while waiting for globalMutex in acquireForReaders", e);
LOGGER
.info("InterruptedException while waiting for globalMutex in acquireForReaders", e);
Thread.currentThread().interrupt();
}
}
@ -165,7 +164,7 @@ public class ReaderWriterLock implements ReadWriteLock {
}
/**
* Writer Lock, can only be accessed by one writer concurrently
* Writer Lock, can only be accessed by one writer concurrently.
*/
private class WriteLock implements Lock {

View File

@ -24,12 +24,11 @@
package com.iluwatar.reader.writer.lock;
import java.util.concurrent.locks.Lock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Writer class, write when it acquired the write lock
* Writer class, write when it acquired the write lock.
*/
public class Writer implements Runnable {
@ -38,24 +37,24 @@ public class Writer implements Runnable {
private Lock writeLock;
private String name;
private long writingTime;
/**
* Create new Writer who writes for 250ms
*
* @param name - Name of the thread owning the writer
* Create new Writer who writes for 250ms.
*
* @param name - Name of the thread owning the writer
* @param writeLock - Lock for this writer
*/
public Writer(String name, Lock writeLock) {
this(name, writeLock, 250L);
}
/**
* Create new Writer
*
* @param name - Name of the thread owning the writer
* @param writeLock - Lock for this writer
* Create new Writer.
*
* @param name - Name of the thread owning the writer
* @param writeLock - Lock for this writer
* @param writingTime - amount of time (in milliseconds) for this reader to engage writing
*/
public Writer(String name, Lock writeLock, long writingTime) {
@ -77,9 +76,9 @@ public class Writer implements Runnable {
writeLock.unlock();
}
}
/**
* Simulate the write operation
* Simulate the write operation.
*/
public void write() throws InterruptedException {
LOGGER.info("{} begin", name);

View File

@ -25,7 +25,6 @@ package com.iluwatar.repository;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@ -38,8 +37,8 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
* query construction code is concentrated. This becomes more important when there are a large
* number of domain classes or heavy querying. In these cases particularly, adding this layer helps
* minimize duplicate query logic.
* <p>
* In this example we utilize Spring Data to automatically generate a repository for us from the
*
* <p>In this example we utilize Spring Data to automatically generate a repository for us from the
* {@link Person} domain object. Using the {@link PersonRepository} we perform CRUD operations on
* the entity, moreover, the query by {@link org.springframework.data.jpa.domain.Specification} are
* also performed. Underneath we have configured in-memory H2 database for which schema is created
@ -50,10 +49,9 @@ public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point
*
* @param args
* command line args
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
@ -107,7 +105,7 @@ public class App {
}
repository.deleteAll();
context.close();
}

View File

@ -27,9 +27,7 @@ import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.slf4j.Logger;
@ -42,9 +40,7 @@ import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
/**
* This is the same example as in {@link App} but with annotations based
* configuration for Spring.
*
* This is the same example as in {@link App} but with annotations based configuration for Spring.
*/
@EnableJpaRepositories
@SpringBootConfiguration
@ -53,8 +49,8 @@ public class AppConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AppConfig.class);
/**
* Creation of H2 db
*
* Creation of H2 db.
*
* @return A new Instance of DataSource
*/
@Bean(destroyMethod = "close")
@ -68,11 +64,12 @@ public class AppConfig {
}
/**
* Factory to create a especific instance of Entity Manager
* Factory to create a especific instance of Entity Manager.
*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
LocalContainerEntityManagerFactoryBean entityManager =
new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(dataSource());
entityManager.setPackagesToScan("com.iluwatar");
entityManager.setPersistenceProvider(new HibernatePersistenceProvider());
@ -82,7 +79,7 @@ public class AppConfig {
}
/**
* Properties for Jpa
* Properties for Jpa.
*/
private static Properties jpaProperties() {
Properties properties = new Properties();
@ -92,7 +89,7 @@ public class AppConfig {
}
/**
* Get transaction manager
* Get transaction manager.
*/
@Bean
public JpaTransactionManager transactionManager() throws SQLException {
@ -102,10 +99,9 @@ public class AppConfig {
}
/**
* Program entry point
*
* @param args
* command line args
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {

View File

@ -28,9 +28,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
*
* Person entity
*
* Person entity.
*/
@Entity
public class Person {
@ -47,7 +45,7 @@ public class Person {
}
/**
* Constructor
* Constructor.
*/
public Person(String name, String surname, int age) {
this.name = name;

View File

@ -28,9 +28,7 @@ import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
/**
*
* Person repository
*
* Person repository.
*/
@Repository
public interface PersonRepository

View File

@ -27,16 +27,15 @@ import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
/**
* Helper class, includes vary Specification as the abstraction of sql query criteria
* Helper class, includes vary Specification as the abstraction of sql query criteria.
*/
public class PersonSpecifications {
/**
* Specifications stating the Between (From - To) Age Specification
* Specifications stating the Between (From - To) Age Specification.
*/
public static class AgeBetweenSpec implements Specification<Person> {
@ -59,8 +58,7 @@ public class PersonSpecifications {
}
/**
* Name specification
*
* Name specification.
*/
public static class NameEqualSpec implements Specification<Person> {
@ -71,7 +69,7 @@ public class PersonSpecifications {
}
/**
* Get predicate
* Get predicate.
*/
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

View File

@ -27,31 +27,30 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Resource Acquisition Is Initialization pattern was developed for exception safe resource
* management by C++ creator Bjarne Stroustrup.
* <p>
* In RAII resource is tied to object lifetime: resource allocation is done during object creation
* while resource deallocation is done during object destruction.
* <p>
* In Java RAII is achieved with try-with-resources statement and interfaces {@link java.io.Closeable} and
* {@link AutoCloseable}. The try-with-resources statement ensures that each resource is closed at
* the end of the statement. Any object that implements {@link java.lang.AutoCloseable}, which
* includes all objects which implement {@link java.io.Closeable}, can be used as a resource.
*
* In this example, {@link SlidingDoor} implements {@link AutoCloseable} and {@link TreasureChest}
* implements {@link java.io.Closeable}. Running the example, we can observe that both resources are
* automatically closed.
* <p>
* http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html
* <p>In RAII resource is tied to object lifetime: resource allocation is done during object
* creation while resource deallocation is done during object destruction.
*
* <p>In Java RAII is achieved with try-with-resources statement and interfaces {@link
* java.io.Closeable} and {@link AutoCloseable}. The try-with-resources statement ensures that each
* resource is closed at the end of the statement. Any object that implements {@link
* java.lang.AutoCloseable}, which includes all objects which implement {@link java.io.Closeable},
* can be used as a resource.
*
* <p>In this example, {@link SlidingDoor} implements {@link AutoCloseable} and {@link
* TreasureChest} implements {@link java.io.Closeable}. Running the example, we can observe that
* both resources are automatically closed.
*
* <p>http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html
*/
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point
* Program entry point.
*/
public static void main(String[] args) throws Exception {

View File

@ -27,9 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* SlidingDoor resource
*
* SlidingDoor resource.
*/
public class SlidingDoor implements AutoCloseable {

View File

@ -23,16 +23,13 @@
package com.iluwatar.resource.acquisition.is.initialization;
import java.io.Closeable;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
/**
*
* TreasureChest resource
*
* TreasureChest resource.
*/
public class TreasureChest implements Closeable {

View File

@ -27,34 +27,35 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The <em>Retry</em> pattern enables applications to handle potentially recoverable failures from
* the environment if the business requirements and nature of the failures allow it. By retrying
* The <em>Retry</em> pattern enables applications to handle potentially recoverable failures from
* the environment if the business requirements and nature of the failures allow it. By retrying
* failed operations on external dependencies, the application may maintain stability and minimize
* negative impact on the user experience.
* <p>
* In our example, we have the {@link BusinessOperation} interface as an abstraction over
* all operations that our application performs involving remote systems. The calling code should
* remain decoupled from implementations.
* <p>
* {@link FindCustomer} is a business operation that looks up a customer's record and returns
* its ID. Imagine its job is performed by looking up the customer in our local database and
* returning its ID. We can pass {@link CustomerNotFoundException} as one of its
* {@link FindCustomer#FindCustomer(java.lang.String, com.iluwatar.retry.BusinessException...)
* constructor parameters} in order to simulate not finding the customer.
* <p>
* Imagine that, lately, this operation has experienced intermittent failures due to some weird
* corruption and/or locking in the data. After retrying a few times the customer is found. The
* database is still, however, expected to always be available. While a definitive solution is
* found to the problem, our engineers advise us to retry the operation a set number
* of times with a set delay between retries, although not too many retries otherwise the end user
* will be left waiting for a long time, while delays that are too short will not allow the database
* to recover from the load.
* <p>
* To keep the calling code as decoupled as possible from this workaround, we have implemented the
* retry mechanism as a {@link BusinessOperation} named {@link Retry}.
*
*
* <p>In our example, we have the {@link BusinessOperation} interface as an abstraction over all
* operations that our application performs involving remote systems. The calling code should remain
* decoupled from implementations.
*
* <p>{@link FindCustomer} is a business operation that looks up a customer's record and returns
* its ID. Imagine its job is performed by looking up the customer in our local database and
* returning its ID. We can pass {@link CustomerNotFoundException} as one of its {@link
* FindCustomer#FindCustomer(java.lang.String, com.iluwatar.retry.BusinessException...) constructor
* parameters} in order to simulate not finding the customer.
*
* <p>Imagine that, lately, this operation has experienced intermittent failures due to some weird
* corruption and/or locking in the data. After retrying a few times the customer is found. The
* database is still, however, expected to always be available. While a definitive solution is
* found to the problem, our engineers advise us to retry the operation a set number of times with a
* set delay between retries, although not too many retries otherwise the end user will be left
* waiting for a long time, while delays that are too short will not allow the database to recover
* from the load.
*
* <p>To keep the calling code as decoupled as possible from this workaround, we have implemented
* the retry mechanism as a {@link BusinessOperation} named {@link Retry}.
*
* @author George Aristy (george.aristy@gmail.com)
* @see <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry">Retry pattern (Microsoft Azure Docs)</a>
* @see <a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry">Retry pattern
* (Microsoft Azure Docs)</a>
*/
public final class App {
private static final Logger LOG = LoggerFactory.getLogger(App.class);
@ -62,7 +63,7 @@ public final class App {
/**
* Entry point.
*
*
* @param args not used
* @throws Exception not expected
*/
@ -99,22 +100,22 @@ public final class App {
final String customerId = op.perform();
LOG.info(String.format(
"However, retrying the operation while ignoring a recoverable error will eventually yield "
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
));
}
private static void errorWithRetryExponentialBackoff() throws Exception {
final RetryExponentialBackoff<String> retry = new RetryExponentialBackoff<>(
new FindCustomer("123", new CustomerNotFoundException("not found")),
6, //6 attempts
30000, //30 s max delay between attempts
e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass())
);
);
op = retry;
final String customerId = op.perform();
LOG.info(String.format(
"However, retrying the operation while ignoring a recoverable error will eventually yield "
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
));
"However, retrying the operation while ignoring a recoverable error will eventually yield "
+ "the result %s after a number of attempts %s", customerId, retry.attempts()
));
}
}

View File

@ -24,10 +24,10 @@
package com.iluwatar.retry;
/**
* The top-most type in our exception hierarchy that signifies that an unexpected error
* condition occurred. Its use is reserved as a "catch-all" for cases where no other subtype
* captures the specificity of the error condition in question. Calling code is not expected to
* be able to handle this error and should be reported to the maintainers immediately.
* The top-most type in our exception hierarchy that signifies that an unexpected error condition
* occurred. Its use is reserved as a "catch-all" for cases where no other subtype captures the
* specificity of the error condition in question. Calling code is not expected to be able to handle
* this error and should be reported to the maintainers immediately.
*
* @author George Aristy (george.aristy@gmail.com)
*/
@ -35,8 +35,8 @@ public class BusinessException extends Exception {
private static final long serialVersionUID = 6235833142062144336L;
/**
* Ctor
*
* Ctor.
*
* @param message the error message
*/
public BusinessException(String message) {

View File

@ -26,18 +26,18 @@ package com.iluwatar.retry;
/**
* Performs some business operation.
*
* @author George Aristy (george.aristy@gmail.com)
* @param <T> the return type
* @author George Aristy (george.aristy@gmail.com)
*/
@FunctionalInterface
public interface BusinessOperation<T> {
/**
* Performs some business operation, returning a value {@code T} if successful, otherwise throwing
* an exception if an error occurs.
*
*
* @return the return value
* @throws BusinessException if the operation fails. Implementations are allowed to throw more
* specific subtypes depending on the error conditions
* specific subtypes depending on the error conditions
*/
T perform() throws BusinessException;
}

View File

@ -24,10 +24,11 @@
package com.iluwatar.retry;
/**
* Indicates that the customer was not found.
* <p>
* The severity of this error is bounded by its context: was the search for the customer triggered
* by an input from some end user, or were the search parameters pulled from your database?
* Indicates that the customer was not found.
*
* <p>The severity of this error is bounded by its context: was the search for the customer
* triggered by an input from some end user, or were the search parameters pulled from your
* database?
*
* @author George Aristy (george.aristy@gmail.com)
*/
@ -36,7 +37,7 @@ public final class CustomerNotFoundException extends BusinessException {
/**
* Ctor.
*
*
* @param message the error message
*/
public CustomerNotFoundException(String message) {

View File

@ -33,7 +33,7 @@ public final class DatabaseNotAvailableException extends BusinessException {
/**
* Ctor.
*
*
* @param message the error message
*/
public DatabaseNotAvailableException(String message) {

View File

@ -29,9 +29,9 @@ import java.util.List;
/**
* Finds a customer, returning its ID from our records.
* <p>
* This is an imaginary operation that, for some imagined input, returns the ID for a customer.
* However, this is a "flaky" operation that is supposed to fail intermittently, but for the
*
* <p>This is an imaginary operation that, for some imagined input, returns the ID for a customer.
* However, this is a "flaky" operation that is supposed to fail intermittently, but for the
* purposes of this example it fails in a programmed way depending on the constructor parameters.
*
* @author George Aristy (george.aristy@gmail.com)
@ -42,15 +42,15 @@ public final class FindCustomer implements BusinessOperation<String> {
/**
* Ctor.
*
*
* @param customerId the final result of the remote operation
* @param errors the errors to throw before returning {@code customerId}
* @param errors the errors to throw before returning {@code customerId}
*/
public FindCustomer(String customerId, BusinessException... errors) {
this.customerId = customerId;
this.errors = new ArrayDeque<>(List.of(errors));
}
@Override
public String perform() throws BusinessException {
if (!this.errors.isEmpty()) {

View File

@ -33,8 +33,8 @@ import java.util.function.Predicate;
/**
* Decorates {@link BusinessOperation business operation} with "retry" capabilities.
*
* @author George Aristy (george.aristy@gmail.com)
* @param <T> the remote op's return type
* @author George Aristy (george.aristy@gmail.com)
*/
public final class Retry<T> implements BusinessOperation<T> {
private final BusinessOperation<T> op;
@ -46,18 +46,18 @@ public final class Retry<T> implements BusinessOperation<T> {
/**
* Ctor.
*
* @param op the {@link BusinessOperation} to retry
*
* @param op the {@link BusinessOperation} to retry
* @param maxAttempts number of times to retry
* @param delay delay (in milliseconds) between attempts
* @param delay delay (in milliseconds) between attempts
* @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
* will be ignored if no tests are given
* will be ignored if no tests are given
*/
@SafeVarargs
public Retry(
BusinessOperation<T> op,
int maxAttempts,
long delay,
BusinessOperation<T> op,
int maxAttempts,
long delay,
Predicate<Exception>... ignoreTests
) {
this.op = op;
@ -70,7 +70,7 @@ public final class Retry<T> implements BusinessOperation<T> {
/**
* The errors encountered while retrying, in the encounter order.
*
*
* @return the errors encountered while retrying
*/
public List<Exception> errors() {
@ -79,7 +79,7 @@ public final class Retry<T> implements BusinessOperation<T> {
/**
* The number of retries performed.
*
*
* @return the number of retries performed
*/
public int attempts() {
@ -93,7 +93,7 @@ public final class Retry<T> implements BusinessOperation<T> {
return this.op.perform();
} catch (BusinessException e) {
this.errors.add(e);
if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(e)) {
throw e;
}
@ -104,7 +104,6 @@ public final class Retry<T> implements BusinessOperation<T> {
//ignore
}
}
}
while (true);
} while (true);
}
}

View File

@ -34,8 +34,8 @@ import java.util.function.Predicate;
/**
* Decorates {@link BusinessOperation business operation} with "retry" capabilities.
*
* @author George Aristy (george.aristy@gmail.com)
* @param <T> the remote op's return type
* @author George Aristy (george.aristy@gmail.com)
*/
public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
private static final Random RANDOM = new Random();
@ -46,20 +46,20 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
private final Predicate<Exception> test;
private final List<Exception> errors;
/**
* Ctor.
*
* @param op the {@link BusinessOperation} to retry
* @param maxAttempts number of times to retry
* @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
* will be ignored if no tests are given
*/
/**
* Ctor.
*
* @param op the {@link BusinessOperation} to retry
* @param maxAttempts number of times to retry
* @param ignoreTests tests to check whether the remote exception can be ignored. No exceptions
* will be ignored if no tests are given
*/
@SafeVarargs
public RetryExponentialBackoff(
BusinessOperation<T> op,
int maxAttempts,
long maxDelay,
Predicate<Exception>... ignoreTests
BusinessOperation<T> op,
int maxAttempts,
long maxDelay,
Predicate<Exception>... ignoreTests
) {
this.op = op;
this.maxAttempts = maxAttempts;
@ -69,20 +69,20 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
this.errors = new ArrayList<>();
}
/**
* The errors encountered while retrying, in the encounter order.
*
* @return the errors encountered while retrying
*/
/**
* The errors encountered while retrying, in the encounter order.
*
* @return the errors encountered while retrying
*/
public List<Exception> errors() {
return Collections.unmodifiableList(this.errors);
}
/**
* The number of retries performed.
*
* @return the number of retries performed
*/
/**
* The number of retries performed.
*
* @return the number of retries performed
*/
public int attempts() {
return this.attempts.intValue();
}
@ -107,8 +107,7 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
//ignore
}
}
}
while (true);
} while (true);
}
}