Java 11 migrate remaining q-r (#1121)

* Moves queue-load-leveling to Java 11

* Moves reactor to Java 11

* Moves reader-writer-lock to Java 11

* Moves repository to Java 11

* Moves resource-acquisition-is-initialization to Java 11

* Moves retry to Java 11

* Moves role-object to Java 11
This commit is contained in:
Anurag Agarwal 2020-01-04 22:13:12 +05:30 committed by Ilkka Seppälä
parent cd2a2e7711
commit 20ea465b7f
52 changed files with 424 additions and 554 deletions

View File

@ -78,17 +78,17 @@ public class App {
try { try {
// Create a MessageQueue object. // Create a MessageQueue object.
MessageQueue msgQueue = new MessageQueue(); var msgQueue = new MessageQueue();
LOGGER.info("Submitting TaskGenerators and ServiceExecutor threads."); LOGGER.info("Submitting TaskGenerators and ServiceExecutor threads.");
// Create three TaskGenerator threads. Each of them will submit different number of jobs. // Create three TaskGenerator threads. Each of them will submit different number of jobs.
final Runnable taskRunnable1 = new TaskGenerator(msgQueue, 5); final var taskRunnable1 = new TaskGenerator(msgQueue, 5);
final Runnable taskRunnable2 = new TaskGenerator(msgQueue, 1); final var taskRunnable2 = new TaskGenerator(msgQueue, 1);
final Runnable taskRunnable3 = new TaskGenerator(msgQueue, 2); final var taskRunnable3 = new TaskGenerator(msgQueue, 2);
// Create e service which should process the submitted jobs. // Create e service which should process the submitted jobs.
final Runnable srvRunnable = new ServiceExecutor(msgQueue); final var srvRunnable = new ServiceExecutor(msgQueue);
// Create a ThreadPool of 2 threads and // Create a ThreadPool of 2 threads and
// submit all Runnable task for execution to executor.. // submit all Runnable task for execution to executor..

View File

@ -40,7 +40,7 @@ public class MessageQueue {
// Default constructor when called creates Blocking Queue object. // Default constructor when called creates Blocking Queue object.
public MessageQueue() { public MessageQueue() {
this.blkQueue = new ArrayBlockingQueue<Message>(1024); this.blkQueue = new ArrayBlockingQueue<>(1024);
} }
/** /**
@ -62,13 +62,11 @@ public class MessageQueue {
* them. Retrieves and removes the head of this queue, or returns null if this queue is empty. * them. Retrieves and removes the head of this queue, or returns null if this queue is empty.
*/ */
public Message retrieveMsg() { public Message retrieveMsg() {
Message retrievedMsg = null;
try { try {
retrievedMsg = blkQueue.poll(); return blkQueue.poll();
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e.getMessage()); LOGGER.error(e.getMessage());
} }
return null;
return retrievedMsg;
} }
} }

View File

@ -46,7 +46,7 @@ public class ServiceExecutor implements Runnable {
public void run() { public void run() {
try { try {
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
Message msg = msgQueue.retrieveMsg(); var msg = msgQueue.retrieveMsg();
if (null != msg) { if (null != msg) {
LOGGER.info(msg.toString() + " is served."); LOGGER.info(msg.toString() + " is served.");

View File

@ -63,12 +63,11 @@ public class TaskGenerator implements Task, Runnable {
* submission TaskGenerator thread will sleep for 1 second. * submission TaskGenerator thread will sleep for 1 second.
*/ */
public void run() { public void run() {
var count = this.msgCount;
int count = this.msgCount;
try { try {
while (count > 0) { while (count > 0) {
String statusMsg = "Message-" + count + " submitted by " + Thread.currentThread().getName(); var statusMsg = "Message-" + count + " submitted by " + Thread.currentThread().getName();
this.submit(new Message(statusMsg)); this.submit(new Message(statusMsg));
LOGGER.info(statusMsg); LOGGER.info(statusMsg);

View File

@ -25,15 +25,12 @@ package com.iluwatar.queue.load.leveling;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException;
/** /**
* Application Test * Application Test
*/ */
public class AppTest { public class AppTest {
@Test @Test
public void test() throws IOException { public void test() {
String[] args = {}; App.main(new String[]{});
App.main(args);
} }
} }

View File

@ -23,25 +23,23 @@
package com.iluwatar.queue.load.leveling; package com.iluwatar.queue.load.leveling;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/** /**
*
* Test case for submitting and retrieving messages from Blocking Queue. * Test case for submitting and retrieving messages from Blocking Queue.
*
*/ */
public class MessageQueueTest { public class MessageQueueTest {
@Test @Test
public void messageQueueTest() { public void messageQueueTest() {
MessageQueue msgQueue = new MessageQueue(); var msgQueue = new MessageQueue();
// submit message // submit message
msgQueue.submitMsg(new Message("MessageQueue Test")); msgQueue.submitMsg(new Message("MessageQueue Test"));
// retrieve message // retrieve message
assertEquals("MessageQueue Test", msgQueue.retrieveMsg().getMsg()); assertEquals("MessageQueue Test", msgQueue.retrieveMsg().getMsg());
} }

View File

@ -23,23 +23,21 @@
package com.iluwatar.queue.load.leveling; package com.iluwatar.queue.load.leveling;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/** /**
*
* Test case for creating and checking the Message. * Test case for creating and checking the Message.
*
*/ */
public class MessageTest { public class MessageTest {
@Test @Test
public void messageTest() { public void messageTest() {
// Parameterized constructor test. // Parameterized constructor test.
String testMsg = "Message Test"; var testMsg = "Message Test";
Message msg = new Message(testMsg); var msg = new Message(testMsg);
assertEquals(testMsg, msg.getMsg()); assertEquals(testMsg, msg.getMsg());
} }
} }

View File

@ -26,25 +26,23 @@ package com.iluwatar.queue.load.leveling;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* * Test case for submitting Message to Blocking Queue by TaskGenerator and retrieve the message by
* Test case for submitting Message to Blocking Queue by TaskGenerator * ServiceExecutor.
* and retrieve the message by ServiceExecutor.
*
*/ */
public class TaskGenSrvExeTest { public class TaskGenSrvExeTest {
@Test @Test
public void taskGeneratorTest() { public void taskGeneratorTest() {
MessageQueue msgQueue = new MessageQueue(); var msgQueue = new MessageQueue();
// Create a task generator thread with 1 job to submit. // Create a task generator thread with 1 job to submit.
Runnable taskRunnable = new TaskGenerator(msgQueue, 1); var taskRunnable = new TaskGenerator(msgQueue, 1);
Thread taskGenThr = new Thread(taskRunnable); var taskGenThr = new Thread(taskRunnable);
taskGenThr.start(); taskGenThr.start();
// Create a service executor thread. // Create a service executor thread.
Runnable srvRunnable = new ServiceExecutor(msgQueue); var srvRunnable = new ServiceExecutor(msgQueue);
Thread srvExeThr = new Thread(srvRunnable); var srvExeThr = new Thread(srvRunnable);
srvExeThr.start(); srvExeThr.start();
} }

View File

@ -124,15 +124,17 @@ public class App {
* This represents application specific business logic that dispatcher will call on appropriate * This represents application specific business logic that dispatcher will call on appropriate
* events. These events are read events in our example. * events. These events are read events in our example.
*/ */
LoggingHandler loggingHandler = new LoggingHandler(); var loggingHandler = new LoggingHandler();
/* /*
* Our application binds to multiple channels and uses same logging handler to handle incoming * Our application binds to multiple channels and uses same logging handler to handle incoming
* log requests. * log requests.
*/ */
reactor.registerChannel(tcpChannel(6666, loggingHandler)) reactor
.registerChannel(tcpChannel(6666, loggingHandler))
.registerChannel(tcpChannel(6667, loggingHandler)) .registerChannel(tcpChannel(6667, loggingHandler))
.registerChannel(udpChannel(6668, loggingHandler)).start(); .registerChannel(udpChannel(6668, loggingHandler))
.start();
} }
/** /**
@ -144,20 +146,20 @@ public class App {
public void stop() throws InterruptedException, IOException { public void stop() throws InterruptedException, IOException {
reactor.stop(); reactor.stop();
dispatcher.stop(); dispatcher.stop();
for (AbstractNioChannel channel : channels) { for (var channel : channels) {
channel.getJavaChannel().close(); channel.getJavaChannel().close();
} }
} }
private AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException { private AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException {
NioServerSocketChannel channel = new NioServerSocketChannel(port, handler); var channel = new NioServerSocketChannel(port, handler);
channel.bind(); channel.bind();
channels.add(channel); channels.add(channel);
return channel; return channel;
} }
private AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException { private AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException {
NioDatagramChannel channel = new NioDatagramChannel(port, handler); var channel = new NioDatagramChannel(port, handler);
channel.bind(); channel.bind();
channels.add(channel); channels.add(channel);
return channel; return channel;

View File

@ -25,7 +25,6 @@ package com.iluwatar.reactor.app;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
@ -55,7 +54,7 @@ public class AppClient {
* @throws IOException if any I/O error occurs. * @throws IOException if any I/O error occurs.
*/ */
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
AppClient appClient = new AppClient(); var appClient = new AppClient();
appClient.start(); appClient.start();
} }
@ -118,8 +117,8 @@ public class AppClient {
@Override @Override
public void run() { public void run() {
try (Socket socket = new Socket(InetAddress.getLocalHost(), serverPort)) { try (Socket socket = new Socket(InetAddress.getLocalHost(), serverPort)) {
OutputStream outputStream = socket.getOutputStream(); var outputStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(outputStream); var writer = new PrintWriter(outputStream);
sendLogRequests(writer, socket.getInputStream()); sendLogRequests(writer, socket.getInputStream());
} catch (IOException e) { } catch (IOException e) {
LOGGER.error("error sending requests", e); LOGGER.error("error sending requests", e);
@ -128,12 +127,12 @@ public class AppClient {
} }
private void sendLogRequests(PrintWriter writer, InputStream inputStream) throws IOException { private void sendLogRequests(PrintWriter writer, InputStream inputStream) throws IOException {
for (int i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
writer.println(clientName + " - Log request: " + i); writer.println(clientName + " - Log request: " + i);
writer.flush(); writer.flush();
byte[] data = new byte[1024]; var data = new byte[1024];
int read = inputStream.read(data, 0, data.length); var read = inputStream.read(data, 0, data.length);
if (read == 0) { if (read == 0) {
LOGGER.info("Read zero bytes"); LOGGER.info("Read zero bytes");
} else { } else {
@ -167,17 +166,17 @@ public class AppClient {
@Override @Override
public void run() { public void run() {
try (DatagramSocket socket = new DatagramSocket()) { try (var socket = new DatagramSocket()) {
for (int i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
String message = clientName + " - Log request: " + i; var message = clientName + " - Log request: " + i;
DatagramPacket request = var bytes = message.getBytes();
new DatagramPacket(message.getBytes(), message.getBytes().length, remoteAddress); var request = new DatagramPacket(bytes, bytes.length, remoteAddress);
socket.send(request); socket.send(request);
byte[] data = new byte[1024]; var data = new byte[1024];
DatagramPacket reply = new DatagramPacket(data, data.length); var reply = new DatagramPacket(data, data.length);
socket.receive(reply); socket.receive(reply);
if (reply.getLength() == 0) { if (reply.getLength() == 0) {
LOGGER.info("Read zero bytes"); LOGGER.info("Read zero bytes");

View File

@ -54,7 +54,7 @@ public class LoggingHandler implements ChannelHandler {
doLogging((ByteBuffer) readObject); doLogging((ByteBuffer) readObject);
sendReply(channel, key); sendReply(channel, key);
} else if (readObject instanceof DatagramPacket) { } else if (readObject instanceof DatagramPacket) {
DatagramPacket datagram = (DatagramPacket) readObject; var datagram = (DatagramPacket) readObject;
doLogging(datagram.getData()); doLogging(datagram.getData());
sendReply(channel, datagram, key); sendReply(channel, datagram, key);
} else { } else {
@ -71,14 +71,14 @@ public class LoggingHandler implements ChannelHandler {
* Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming * Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming
* message. * message.
*/ */
DatagramPacket replyPacket = new DatagramPacket(ByteBuffer.wrap(ACK)); var replyPacket = new DatagramPacket(ByteBuffer.wrap(ACK));
replyPacket.setReceiver(incomingPacket.getSender()); replyPacket.setReceiver(incomingPacket.getSender());
channel.write(replyPacket, key); channel.write(replyPacket, key);
} }
private static void sendReply(AbstractNioChannel channel, SelectionKey key) { private static void sendReply(AbstractNioChannel channel, SelectionKey key) {
ByteBuffer buffer = ByteBuffer.wrap(ACK); var buffer = ByteBuffer.wrap(ACK);
channel.write(buffer, key); channel.write(buffer, key);
} }

View File

@ -46,8 +46,7 @@ public abstract class AbstractNioChannel {
private final SelectableChannel channel; private final SelectableChannel channel;
private final ChannelHandler handler; private final ChannelHandler handler;
private final Map<SelectableChannel, Queue<Object>> channelToPendingWrites = private final Map<SelectableChannel, Queue<Object>> channelToPendingWrites;
new ConcurrentHashMap<>();
private NioReactor reactor; private NioReactor reactor;
/** /**
@ -59,6 +58,7 @@ public abstract class AbstractNioChannel {
public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) { public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) {
this.handler = handler; this.handler = handler;
this.channel = channel; this.channel = channel;
this.channelToPendingWrites = new ConcurrentHashMap<>();
} }
/** /**
@ -117,18 +117,14 @@ public abstract class AbstractNioChannel {
* whole pending block of data at once. * whole pending block of data at once.
*/ */
void flush(SelectionKey key) throws IOException { void flush(SelectionKey key) throws IOException {
Queue<Object> pendingWrites = channelToPendingWrites.get(key.channel()); var pendingWrites = channelToPendingWrites.get(key.channel());
while (true) { Object pendingWrite;
Object pendingWrite = pendingWrites.poll(); while ((pendingWrite = pendingWrites.poll()) != null) {
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 // ask the concrete channel to make sense of data and write it to java channel
doWrite(pendingWrite, key); doWrite(pendingWrite, key);
} }
// We don't have anything more to write so channel is interested in reading more data
reactor.changeOps(key, SelectionKey.OP_READ);
} }
/** /**
@ -162,7 +158,7 @@ public abstract class AbstractNioChannel {
* @param key the key which is writable. * @param key the key which is writable.
*/ */
public void write(Object data, SelectionKey key) { public void write(Object data, SelectionKey key) {
Queue<Object> pendingWrites = this.channelToPendingWrites.get(key.channel()); var pendingWrites = this.channelToPendingWrites.get(key.channel());
if (pendingWrites == null) { if (pendingWrites == null) {
synchronized (this.channelToPendingWrites) { synchronized (this.channelToPendingWrites) {
pendingWrites = this.channelToPendingWrites.get(key.channel()); pendingWrites = this.channelToPendingWrites.get(key.channel());

View File

@ -73,15 +73,15 @@ public class NioDatagramChannel extends AbstractNioChannel {
*/ */
@Override @Override
public DatagramPacket read(SelectionKey key) throws IOException { public DatagramPacket read(SelectionKey key) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024); var buffer = ByteBuffer.allocate(1024);
SocketAddress sender = ((DatagramChannel) key.channel()).receive(buffer); var sender = ((DatagramChannel) key.channel()).receive(buffer);
/* /*
* It is required to create a DatagramPacket because we need to preserve which socket address * It is required to create a DatagramPacket because we need to preserve which socket address
* acts as destination for sending reply packets. * acts as destination for sending reply packets.
*/ */
buffer.flip(); buffer.flip();
DatagramPacket packet = new DatagramPacket(buffer); var packet = new DatagramPacket(buffer);
packet.setSender(sender); packet.setSender(sender);
return packet; return packet;
@ -115,7 +115,7 @@ public class NioDatagramChannel extends AbstractNioChannel {
*/ */
@Override @Override
protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException {
DatagramPacket pendingPacket = (DatagramPacket) pendingWrite; var pendingPacket = (DatagramPacket) pendingWrite;
getJavaChannel().send(pendingPacket.getData(), pendingPacket.getReceiver()); getJavaChannel().send(pendingPacket.getData(), pendingPacket.getReceiver());
} }

View File

@ -27,10 +27,7 @@ import java.io.IOException;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.Selector; import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel; import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Queue; import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -119,20 +116,15 @@ public class NioReactor {
* @throws IOException if any I/O error occurs. * @throws IOException if any I/O error occurs.
*/ */
public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { public NioReactor registerChannel(AbstractNioChannel channel) throws IOException {
SelectionKey key = channel.getJavaChannel().register(selector, channel.getInterestedOps()); var key = channel.getJavaChannel().register(selector, channel.getInterestedOps());
key.attach(channel); key.attach(channel);
channel.setReactor(this); channel.setReactor(this);
return this; return this;
} }
private void eventLoop() throws IOException { private void eventLoop() throws IOException {
while (true) { // honor interrupt request
while (!Thread.interrupted()) {
// honor interrupt request
if (Thread.interrupted()) {
break;
}
// honor any pending commands first // honor any pending commands first
processPendingCommands(); processPendingCommands();
@ -145,12 +137,11 @@ public class NioReactor {
/* /*
* Represents the events that have occurred on registered handles. * Represents the events that have occurred on registered handles.
*/ */
Set<SelectionKey> keys = selector.selectedKeys(); var keys = selector.selectedKeys();
var iterator = keys.iterator();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
SelectionKey key = iterator.next(); var key = iterator.next();
if (!key.isValid()) { if (!key.isValid()) {
iterator.remove(); iterator.remove();
continue; continue;
@ -162,9 +153,9 @@ public class NioReactor {
} }
private void processPendingCommands() { private void processPendingCommands() {
Iterator<Runnable> iterator = pendingCommands.iterator(); var iterator = pendingCommands.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Runnable command = iterator.next(); var command = iterator.next();
command.run(); command.run();
iterator.remove(); iterator.remove();
} }
@ -185,15 +176,14 @@ public class NioReactor {
} }
private static void onChannelWritable(SelectionKey key) throws IOException { private static void onChannelWritable(SelectionKey key) throws IOException {
AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); var channel = (AbstractNioChannel) key.attachment();
channel.flush(key); channel.flush(key);
} }
private void onChannelReadable(SelectionKey key) { private void onChannelReadable(SelectionKey key) {
try { try {
// reads the incoming data in context of reactor main loop. Can this be improved? // reads the incoming data in context of reactor main loop. Can this be improved?
Object readObject = ((AbstractNioChannel) key.attachment()).read(key); var readObject = ((AbstractNioChannel) key.attachment()).read(key);
dispatchReadEvent(key, readObject); dispatchReadEvent(key, readObject);
} catch (IOException e) { } catch (IOException e) {
try { try {
@ -212,10 +202,10 @@ public class NioReactor {
} }
private void onChannelAcceptable(SelectionKey key) throws IOException { private void onChannelAcceptable(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); var serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept(); var socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false); socketChannel.configureBlocking(false);
SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ); var readKey = socketChannel.register(selector, SelectionKey.OP_READ);
readKey.attach(key.attachment()); readKey.attach(key.attachment());
} }

View File

@ -83,9 +83,9 @@ public class NioServerSocketChannel extends AbstractNioChannel {
*/ */
@Override @Override
public ByteBuffer read(SelectionKey key) throws IOException { public ByteBuffer read(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel(); var socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024); var buffer = ByteBuffer.allocate(1024);
int read = socketChannel.read(buffer); var read = socketChannel.read(buffer);
buffer.flip(); buffer.flip();
if (read == -1) { if (read == -1) {
throw new IOException("Socket closed"); throw new IOException("Socket closed");
@ -100,9 +100,9 @@ public class NioServerSocketChannel extends AbstractNioChannel {
*/ */
@Override @Override
public void bind() throws IOException { public void bind() throws IOException {
getJavaChannel().socket().bind( var javaChannel = getJavaChannel();
new InetSocketAddress(InetAddress.getLocalHost(), port)); javaChannel.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
getJavaChannel().configureBlocking(false); javaChannel.configureBlocking(false);
LOGGER.info("Bound TCP socket at port: {}", port); LOGGER.info("Bound TCP socket at port: {}", port);
} }
@ -112,7 +112,7 @@ public class NioServerSocketChannel extends AbstractNioChannel {
*/ */
@Override @Override
protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException { protected void doWrite(Object pendingWrite, SelectionKey key) throws IOException {
ByteBuffer pendingBuffer = (ByteBuffer) pendingWrite; var pendingBuffer = (ByteBuffer) pendingWrite;
((SocketChannel) key.channel()).write(pendingBuffer); ((SocketChannel) key.channel()).write(pendingBuffer);
} }
} }

View File

@ -25,14 +25,12 @@ package com.iluwatar.reactor.app;
import com.iluwatar.reactor.framework.SameThreadDispatcher; import com.iluwatar.reactor.framework.SameThreadDispatcher;
import com.iluwatar.reactor.framework.ThreadPoolDispatcher; import com.iluwatar.reactor.framework.ThreadPoolDispatcher;
import java.io.IOException;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
/** /**
*
* This class tests the Distributed Logging service by starting a Reactor and then sending it * This class tests the Distributed Logging service by starting a Reactor and then sending it
* concurrent logging requests using multiple clients. * concurrent logging requests using multiple clients.
*/ */
@ -42,17 +40,17 @@ public class ReactorTest {
/** /**
* Test the application using pooled thread dispatcher. * Test the application using pooled thread dispatcher.
* *
* @throws IOException if any I/O error occurs. * @throws IOException if any I/O error occurs.
* @throws InterruptedException if interrupted while stopping the application. * @throws InterruptedException if interrupted while stopping the application.
*/ */
@Test @Test
public void testAppUsingThreadPoolDispatcher() throws IOException, InterruptedException { public void testAppUsingThreadPoolDispatcher() throws IOException, InterruptedException {
LOGGER.info("testAppUsingThreadPoolDispatcher start"); LOGGER.info("testAppUsingThreadPoolDispatcher start");
App app = new App(new ThreadPoolDispatcher(2)); var app = new App(new ThreadPoolDispatcher(2));
app.start(); app.start();
AppClient client = new AppClient(); var client = new AppClient();
client.start(); client.start();
// allow clients to send requests. Artificial delay. // allow clients to send requests. Artificial delay.
@ -70,17 +68,17 @@ public class ReactorTest {
/** /**
* Test the application using same thread dispatcher. * Test the application using same thread dispatcher.
* *
* @throws IOException if any I/O error occurs. * @throws IOException if any I/O error occurs.
* @throws InterruptedException if interrupted while stopping the application. * @throws InterruptedException if interrupted while stopping the application.
*/ */
@Test @Test
public void testAppUsingSameThreadDispatcher() throws IOException, InterruptedException { public void testAppUsingSameThreadDispatcher() throws IOException, InterruptedException {
LOGGER.info("testAppUsingSameThreadDispatcher start"); LOGGER.info("testAppUsingSameThreadDispatcher start");
App app = new App(new SameThreadDispatcher()); var app = new App(new SameThreadDispatcher());
app.start(); app.start();
AppClient client = new AppClient(); var client = new AppClient();
client.start(); client.start();
// allow clients to send requests. Artificial delay. // allow clients to send requests. Artificial delay.

View File

@ -23,11 +23,9 @@
package com.iluwatar.reader.writer.lock; package com.iluwatar.reader.writer.lock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -58,19 +56,21 @@ public class App {
*/ */
public static void main(String[] args) { public static void main(String[] args) {
ExecutorService executeService = Executors.newFixedThreadPool(10); var executeService = Executors.newFixedThreadPool(10);
ReaderWriterLock lock = new ReaderWriterLock(); var lock = new ReaderWriterLock();
// Start writers // Start writers
IntStream.range(0, 5) for (var i = 0; i < 5; i++) {
.forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock(), var writingTime = ThreadLocalRandom.current().nextLong(5000);
ThreadLocalRandom.current().nextLong(5000)))); executeService.submit(new Writer("Writer " + i, lock.writeLock(), writingTime));
}
LOGGER.info("Writers added..."); LOGGER.info("Writers added...");
// Start readers // Start readers
IntStream.range(0, 5) for (var i = 0; i < 5; i++) {
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock(), var readingTime = ThreadLocalRandom.current().nextLong(10);
ThreadLocalRandom.current().nextLong(10)))); executeService.submit(new Reader("Reader " + i, lock.readLock(), readingTime));
}
LOGGER.info("Readers added..."); LOGGER.info("Readers added...");
try { try {
@ -81,9 +81,10 @@ public class App {
} }
// Start readers // Start readers
IntStream.range(6, 10) for (var i = 6; i < 10; i++) {
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock(), var readingTime = ThreadLocalRandom.current().nextLong(10);
ThreadLocalRandom.current().nextLong(10)))); executeService.submit(new Reader("Reader " + i, lock.readLock(), readingTime));
}
LOGGER.info("More readers added..."); LOGGER.info("More readers added...");

View File

@ -43,7 +43,7 @@ public class ReaderWriterLock implements ReadWriteLock {
private static final Logger LOGGER = LoggerFactory.getLogger(ReaderWriterLock.class); private static final Logger LOGGER = LoggerFactory.getLogger(ReaderWriterLock.class);
private Object readerMutex = new Object(); private final Object readerMutex = new Object();
private int currentReaderCount; private int currentReaderCount;
@ -57,7 +57,7 @@ public class ReaderWriterLock implements ReadWriteLock {
* *
* <p>This is the most important field in this class to control the access for reader/writer. * <p>This is the most important field in this class to control the access for reader/writer.
*/ */
private Set<Object> globalMutex = new HashSet<>(); private final Set<Object> globalMutex = new HashSet<>();
private ReadLock readerLock = new ReadLock(); private ReadLock readerLock = new ReadLock();
private WriteLock writerLock = new WriteLock(); private WriteLock writerLock = new WriteLock();
@ -114,8 +114,8 @@ public class ReaderWriterLock implements ReadWriteLock {
try { try {
globalMutex.wait(); globalMutex.wait();
} catch (InterruptedException e) { } catch (InterruptedException e) {
LOGGER var message = "InterruptedException while waiting for globalMutex in acquireForReaders";
.info("InterruptedException while waiting for globalMutex in acquireForReaders", e); LOGGER.info(message, e);
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
} }
@ -125,7 +125,6 @@ public class ReaderWriterLock implements ReadWriteLock {
@Override @Override
public void unlock() { public void unlock() {
synchronized (readerMutex) { synchronized (readerMutex) {
currentReaderCount--; currentReaderCount--;
// Release the lock only when it is the last reader, it is ensure that the lock is released // Release the lock only when it is the last reader, it is ensure that the lock is released
@ -142,7 +141,7 @@ public class ReaderWriterLock implements ReadWriteLock {
} }
@Override @Override
public void lockInterruptibly() throws InterruptedException { public void lockInterruptibly() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -152,7 +151,7 @@ public class ReaderWriterLock implements ReadWriteLock {
} }
@Override @Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { public boolean tryLock(long time, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -170,7 +169,6 @@ public class ReaderWriterLock implements ReadWriteLock {
@Override @Override
public void lock() { public void lock() {
synchronized (globalMutex) { synchronized (globalMutex) {
// Wait until the lock is free. // Wait until the lock is free.
@ -189,7 +187,6 @@ public class ReaderWriterLock implements ReadWriteLock {
@Override @Override
public void unlock() { public void unlock() {
synchronized (globalMutex) { synchronized (globalMutex) {
globalMutex.remove(this); globalMutex.remove(this);
// Notify the waiter, other writer or reader // Notify the waiter, other writer or reader
@ -198,7 +195,7 @@ public class ReaderWriterLock implements ReadWriteLock {
} }
@Override @Override
public void lockInterruptibly() throws InterruptedException { public void lockInterruptibly() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -208,7 +205,7 @@ public class ReaderWriterLock implements ReadWriteLock {
} }
@Override @Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { public boolean tryLock(long time, TimeUnit unit) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -31,9 +31,8 @@ import org.junit.jupiter.api.Test;
public class AppTest { public class AppTest {
@Test @Test
public void test() throws Exception { public void test() {
String[] args = {}; App.main(new String[]{});
App.main(args);
} }
} }

View File

@ -23,19 +23,17 @@
package com.iluwatar.reader.writer.lock; package com.iluwatar.reader.writer.lock;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.iluwatar.reader.writer.lock.utils.InMemoryAppender; import com.iluwatar.reader.writer.lock.utils.InMemoryAppender;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* @author hongshuwei@gmail.com * @author hongshuwei@gmail.com
*/ */
@ -61,12 +59,12 @@ public class ReaderAndWriterTest {
@Test @Test
public void testReadAndWrite() throws Exception { public void testReadAndWrite() throws Exception {
ReaderWriterLock lock = new ReaderWriterLock(); var lock = new ReaderWriterLock();
Reader reader1 = new Reader("Reader 1", lock.readLock()); var reader1 = new Reader("Reader 1", lock.readLock());
Writer writer1 = new Writer("Writer 1", lock.writeLock()); var writer1 = new Writer("Writer 1", lock.writeLock());
ExecutorService executeService = Executors.newFixedThreadPool(2); var executeService = Executors.newFixedThreadPool(2);
executeService.submit(reader1); executeService.submit(reader1);
// Let reader1 execute first // Let reader1 execute first
Thread.sleep(150); Thread.sleep(150);
@ -91,11 +89,11 @@ public class ReaderAndWriterTest {
@Test @Test
public void testWriteAndRead() throws Exception { public void testWriteAndRead() throws Exception {
ExecutorService executeService = Executors.newFixedThreadPool(2); var executeService = Executors.newFixedThreadPool(2);
ReaderWriterLock lock = new ReaderWriterLock(); var lock = new ReaderWriterLock();
Reader reader1 = new Reader("Reader 1", lock.readLock()); var reader1 = new Reader("Reader 1", lock.readLock());
Writer writer1 = new Writer("Writer 1", lock.writeLock()); var writer1 = new Writer("Writer 1", lock.writeLock());
executeService.submit(writer1); executeService.submit(writer1);
// Let writer1 execute first // Let writer1 execute first

View File

@ -23,20 +23,18 @@
package com.iluwatar.reader.writer.lock; package com.iluwatar.reader.writer.lock;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.spy;
import com.iluwatar.reader.writer.lock.utils.InMemoryAppender; import com.iluwatar.reader.writer.lock.utils.InMemoryAppender;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.spy;
/** /**
* @author hongshuwei@gmail.com * @author hongshuwei@gmail.com
*/ */
@ -62,11 +60,11 @@ public class ReaderTest {
@Test @Test
public void testRead() throws Exception { public void testRead() throws Exception {
ExecutorService executeService = Executors.newFixedThreadPool(2); var executeService = Executors.newFixedThreadPool(2);
ReaderWriterLock lock = new ReaderWriterLock(); var lock = new ReaderWriterLock();
Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); var reader1 = spy(new Reader("Reader 1", lock.readLock()));
Reader reader2 = spy(new Reader("Reader 2", lock.readLock())); var reader2 = spy(new Reader("Reader 2", lock.readLock()));
executeService.submit(reader1); executeService.submit(reader1);
Thread.sleep(150); Thread.sleep(150);

View File

@ -23,20 +23,18 @@
package com.iluwatar.reader.writer.lock; package com.iluwatar.reader.writer.lock;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.spy;
import com.iluwatar.reader.writer.lock.utils.InMemoryAppender; import com.iluwatar.reader.writer.lock.utils.InMemoryAppender;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.spy;
/** /**
* @author hongshuwei@gmail.com * @author hongshuwei@gmail.com
*/ */
@ -62,11 +60,11 @@ public class WriterTest {
@Test @Test
public void testWrite() throws Exception { public void testWrite() throws Exception {
ExecutorService executeService = Executors.newFixedThreadPool(2); var executeService = Executors.newFixedThreadPool(2);
ReaderWriterLock lock = new ReaderWriterLock(); var lock = new ReaderWriterLock();
Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); var writer1 = spy(new Writer("Writer 1", lock.writeLock()));
Writer writer2 = spy(new Writer("Writer 2", lock.writeLock())); var writer2 = spy(new Writer("Writer 2", lock.writeLock()));
executeService.submit(writer1); executeService.submit(writer1);
// Let write1 execute first // Let write1 execute first

View File

@ -26,10 +26,9 @@ package com.iluwatar.reader.writer.lock.utils;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.AppenderBase;
import org.slf4j.LoggerFactory;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.slf4j.LoggerFactory;
/** /**
* InMemory Log Appender Util. * InMemory Log Appender Util.

View File

@ -24,7 +24,6 @@
package com.iluwatar.repository; package com.iluwatar.repository;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
@ -55,14 +54,13 @@ public class App {
*/ */
public static void main(String[] args) { public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( var context = new ClassPathXmlApplicationContext("applicationContext.xml");
"applicationContext.xml"); var repository = context.getBean(PersonRepository.class);
PersonRepository repository = context.getBean(PersonRepository.class);
Person peter = new Person("Peter", "Sagan", 17); var peter = new Person("Peter", "Sagan", 17);
Person nasta = new Person("Nasta", "Kuzminova", 25); var nasta = new Person("Nasta", "Kuzminova", 25);
Person john = new Person("John", "lawrence", 35); var john = new Person("John", "lawrence", 35);
Person terry = new Person("Terry", "Law", 36); var terry = new Person("Terry", "Law", 36);
// Add new Person records // Add new Person records
repository.save(peter); repository.save(peter);
@ -74,17 +72,15 @@ public class App {
LOGGER.info("Count Person records: {}", repository.count()); LOGGER.info("Count Person records: {}", repository.count());
// Print all records // Print all records
List<Person> persons = (List<Person>) repository.findAll(); var persons = (List<Person>) repository.findAll();
for (Person person : persons) { persons.stream().map(Person::toString).forEach(LOGGER::info);
LOGGER.info(person.toString());
}
// Update Person // Update Person
nasta.setName("Barbora"); nasta.setName("Barbora");
nasta.setSurname("Spotakova"); nasta.setSurname("Spotakova");
repository.save(nasta); repository.save(nasta);
LOGGER.info("Find by id 2: {}", repository.findById(2L).get()); repository.findById(2L).ifPresent(p -> LOGGER.info("Find by id 2: {}", p));
// Remove record from Person // Remove record from Person
repository.deleteById(2L); repository.deleteById(2L);
@ -93,16 +89,15 @@ public class App {
LOGGER.info("Count Person records: {}", repository.count()); LOGGER.info("Count Person records: {}", repository.count());
// find by name // find by name
Optional<Person> p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); repository
LOGGER.info("Find by John is {}", p.get()); .findOne(new PersonSpecifications.NameEqualSpec("John"))
.ifPresent(p -> LOGGER.info("Find by John is {}", p));
// find by age // find by age
persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
LOGGER.info("Find Person with age between 20,40: "); LOGGER.info("Find Person with age between 20,40: ");
for (Person person : persons) { persons.stream().map(Person::toString).forEach(LOGGER::info);
LOGGER.info(person.toString());
}
repository.deleteAll(); repository.deleteAll();

View File

@ -25,7 +25,6 @@ package com.iluwatar.repository;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.dbcp.BasicDataSource;
@ -55,7 +54,7 @@ public class AppConfig {
*/ */
@Bean(destroyMethod = "close") @Bean(destroyMethod = "close")
public DataSource dataSource() { public DataSource dataSource() {
BasicDataSource basicDataSource = new BasicDataSource(); var basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("org.h2.Driver"); basicDataSource.setDriverClassName("org.h2.Driver");
basicDataSource.setUrl("jdbc:h2:~/databases/person"); basicDataSource.setUrl("jdbc:h2:~/databases/person");
basicDataSource.setUsername("sa"); basicDataSource.setUsername("sa");
@ -68,13 +67,11 @@ public class AppConfig {
*/ */
@Bean @Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() { public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManager = var entityManager = new LocalContainerEntityManagerFactoryBean();
new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(dataSource()); entityManager.setDataSource(dataSource());
entityManager.setPackagesToScan("com.iluwatar"); entityManager.setPackagesToScan("com.iluwatar");
entityManager.setPersistenceProvider(new HibernatePersistenceProvider()); entityManager.setPersistenceProvider(new HibernatePersistenceProvider());
entityManager.setJpaProperties(jpaProperties()); entityManager.setJpaProperties(jpaProperties());
return entityManager; return entityManager;
} }
@ -82,7 +79,7 @@ public class AppConfig {
* Properties for Jpa. * Properties for Jpa.
*/ */
private static Properties jpaProperties() { private static Properties jpaProperties() {
Properties properties = new Properties(); var properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
return properties; return properties;
@ -93,7 +90,7 @@ public class AppConfig {
*/ */
@Bean @Bean
public JpaTransactionManager transactionManager() throws SQLException { public JpaTransactionManager transactionManager() throws SQLException {
JpaTransactionManager transactionManager = new JpaTransactionManager(); var transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager; return transactionManager;
} }
@ -104,15 +101,13 @@ public class AppConfig {
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(AppConfig.class);
var repository = context.getBean(PersonRepository.class);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( var peter = new Person("Peter", "Sagan", 17);
AppConfig.class); var nasta = new Person("Nasta", "Kuzminova", 25);
PersonRepository repository = context.getBean(PersonRepository.class); var john = new Person("John", "lawrence", 35);
var terry = new Person("Terry", "Law", 36);
Person peter = new Person("Peter", "Sagan", 17);
Person nasta = new Person("Nasta", "Kuzminova", 25);
Person john = new Person("John", "lawrence", 35);
Person terry = new Person("Terry", "Law", 36);
// Add new Person records // Add new Person records
repository.save(peter); repository.save(peter);
@ -124,17 +119,15 @@ public class AppConfig {
LOGGER.info("Count Person records: {}", repository.count()); LOGGER.info("Count Person records: {}", repository.count());
// Print all records // Print all records
List<Person> persons = (List<Person>) repository.findAll(); var persons = (List<Person>) repository.findAll();
for (Person person : persons) { persons.stream().map(Person::toString).forEach(LOGGER::info);
LOGGER.info(person.toString());
}
// Update Person // Update Person
nasta.setName("Barbora"); nasta.setName("Barbora");
nasta.setSurname("Spotakova"); nasta.setSurname("Spotakova");
repository.save(nasta); repository.save(nasta);
LOGGER.info("Find by id 2: {}", repository.findById(2L).get()); repository.findById(2L).ifPresent(p -> LOGGER.info("Find by id 2: {}", p));
// Remove record from Person // Remove record from Person
repository.deleteById(2L); repository.deleteById(2L);
@ -143,16 +136,15 @@ public class AppConfig {
LOGGER.info("Count Person records: {}", repository.count()); LOGGER.info("Count Person records: {}", repository.count());
// find by name // find by name
Optional<Person> p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); repository
LOGGER.info("Find by John is {}", p.get()); .findOne(new PersonSpecifications.NameEqualSpec("John"))
.ifPresent(p -> LOGGER.info("Find by John is {}", p));
// find by age // find by age
persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
LOGGER.info("Find Person with age between 20,40: "); LOGGER.info("Find Person with age between 20,40: ");
for (Person person : persons) { persons.stream().map(Person::toString).forEach(LOGGER::info);
LOGGER.info(person.toString());
}
context.close(); context.close();

View File

@ -92,9 +92,8 @@ public class Person {
@Override @Override
public int hashCode() { public int hashCode() {
final var prime = 31;
final int prime = 31; var result = 1;
int result = 1;
result = prime * result + age; result = prime * result + age;
result = prime * result + (id == null ? 0 : id.hashCode()); result = prime * result + (id == null ? 0 : id.hashCode());
result = prime * result + (name == null ? 0 : name.hashCode()); result = prime * result + (name == null ? 0 : name.hashCode());
@ -113,7 +112,7 @@ public class Person {
if (getClass() != obj.getClass()) { if (getClass() != obj.getClass()) {
return false; return false;
} }
Person other = (Person) obj; var other = (Person) obj;
if (age != other.age) { if (age != other.age) {
return false; return false;
} }
@ -132,13 +131,9 @@ public class Person {
return false; return false;
} }
if (surname == null) { if (surname == null) {
if (other.surname != null) { return other.surname == null;
return false;
}
} else if (!surname.equals(other.surname)) {
return false;
} }
return true; return surname.equals(other.surname);
} }
} }

View File

@ -50,9 +50,7 @@ public class PersonSpecifications {
@Override @Override
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) { public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.between(root.get("age"), from, to); return cb.between(root.get("age"), from, to);
} }
} }
@ -72,9 +70,7 @@ public class PersonSpecifications {
* Get predicate. * Get predicate.
*/ */
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) { public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get("name"), this.name); return cb.equal(root.get("name"), this.name);
} }
} }

View File

@ -23,7 +23,13 @@
package com.iluwatar.repository; package com.iluwatar.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -31,51 +37,41 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query * Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query
* by {@link org.springframework.data.jpa.domain.Specification} are also test. * by {@link org.springframework.data.jpa.domain.Specification} are also test.
*
*/ */
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@SpringBootTest(classes = { AppConfig.class }) @SpringBootTest(classes = {AppConfig.class})
public class AnnotationBasedRepositoryTest { public class AnnotationBasedRepositoryTest {
@Resource @Resource
private PersonRepository repository; private PersonRepository repository;
Person peter = new Person("Peter", "Sagan", 17); private Person peter = new Person("Peter", "Sagan", 17);
Person nasta = new Person("Nasta", "Kuzminova", 25); private Person nasta = new Person("Nasta", "Kuzminova", 25);
Person john = new Person("John", "lawrence", 35); private Person john = new Person("John", "lawrence", 35);
Person terry = new Person("Terry", "Law", 36); private Person terry = new Person("Terry", "Law", 36);
List<Person> persons = List.of(peter, nasta, john, terry); private List<Person> persons = List.of(peter, nasta, john, terry);
/** /**
* Prepare data for test * Prepare data for test
*/ */
@BeforeEach @BeforeEach
public void setup() { public void setup() {
repository.saveAll(persons); repository.saveAll(persons);
} }
@Test @Test
public void testFindAll() { public void testFindAll() {
var actuals = Lists.newArrayList(repository.findAll());
List<Person> actuals = Lists.newArrayList(repository.findAll());
assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals)); assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals));
} }
@Test @Test
public void testSave() { public void testSave() {
var terry = repository.findByName("Terry");
Person terry = repository.findByName("Terry");
terry.setSurname("Lee"); terry.setSurname("Lee");
terry.setAge(47); terry.setAge(47);
repository.save(terry); repository.save(terry);
@ -87,8 +83,7 @@ public class AnnotationBasedRepositoryTest {
@Test @Test
public void testDelete() { public void testDelete() {
var terry = repository.findByName("Terry");
Person terry = repository.findByName("Terry");
repository.delete(terry); repository.delete(terry);
assertEquals(3, repository.count()); assertEquals(3, repository.count());
@ -97,31 +92,26 @@ public class AnnotationBasedRepositoryTest {
@Test @Test
public void testCount() { public void testCount() {
assertEquals(4, repository.count()); assertEquals(4, repository.count());
} }
@Test @Test
public void testFindAllByAgeBetweenSpec() { public void testFindAllByAgeBetweenSpec() {
var persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
List<Person> persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
assertEquals(3, persons.size()); assertEquals(3, persons.size());
assertTrue(persons.stream().allMatch((item) -> { assertTrue(persons.stream().allMatch((item) -> item.getAge() > 20 && item.getAge() < 40));
return item.getAge() > 20 && item.getAge() < 40;
}));
} }
@Test @Test
public void testFindOneByNameEqualSpec() { public void testFindOneByNameEqualSpec() {
var actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry"));
Optional<Person> actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry")); assertTrue(actual.isPresent());
assertEquals(terry, actual.get()); assertEquals(terry, actual.get());
} }
@AfterEach @AfterEach
public void cleanup() { public void cleanup() {
repository.deleteAll(); repository.deleteAll();
} }

View File

@ -23,6 +23,12 @@
package com.iluwatar.repository; package com.iluwatar.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -30,19 +36,11 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/** /**
* This case is Just for test the Annotation Based configuration * This case is Just for test the Annotation Based configuration
*
*/ */
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@SpringBootTest(classes = { AppConfig.class }) @SpringBootTest(classes = {AppConfig.class})
public class AppConfigTest { public class AppConfigTest {
@Autowired @Autowired
@ -62,12 +60,11 @@ public class AppConfigTest {
@Test @Test
@Transactional @Transactional
public void testQuery() throws SQLException { public void testQuery() throws SQLException {
ResultSet resultSet = dataSource.getConnection().createStatement().executeQuery("SELECT 1"); var resultSet = dataSource.getConnection().createStatement().executeQuery("SELECT 1");
var expected = "1";
String result = null; String result = null;
String expected = "1";
while (resultSet.next()) { while (resultSet.next()) {
result = resultSet.getString(1); result = resultSet.getString(1);
} }
assertEquals(expected, result); assertEquals(expected, result);
} }

View File

@ -25,15 +25,12 @@ package com.iluwatar.repository;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException;
/** /**
* Tests that Repository example runs without errors. * Tests that Repository example runs without errors.
*/ */
public class AppTest { public class AppTest {
@Test @Test
public void test() { public void test() {
String[] args = {}; App.main(new String[]{});
App.main(args);
} }
} }

View File

@ -23,7 +23,13 @@
package com.iluwatar.repository; package com.iluwatar.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List;
import javax.annotation.Resource;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -31,50 +37,41 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query * Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query
* by {@link org.springframework.data.jpa.domain.Specification} are also test. * by {@link org.springframework.data.jpa.domain.Specification} are also test.
*/ */
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
@SpringBootTest(properties = { "locations=classpath:applicationContext.xml" }) @SpringBootTest(properties = {"locations=classpath:applicationContext.xml"})
public class RepositoryTest { public class RepositoryTest {
@Resource @Resource
private PersonRepository repository; private PersonRepository repository;
Person peter = new Person("Peter", "Sagan", 17); private Person peter = new Person("Peter", "Sagan", 17);
Person nasta = new Person("Nasta", "Kuzminova", 25); private Person nasta = new Person("Nasta", "Kuzminova", 25);
Person john = new Person("John", "lawrence", 35); private Person john = new Person("John", "lawrence", 35);
Person terry = new Person("Terry", "Law", 36); private Person terry = new Person("Terry", "Law", 36);
List<Person> persons = List.of(peter, nasta, john, terry); private List<Person> persons = List.of(peter, nasta, john, terry);
/** /**
* Prepare data for test * Prepare data for test
*/ */
@BeforeEach @BeforeEach
public void setup() { public void setup() {
repository.saveAll(persons); repository.saveAll(persons);
} }
@Test @Test
public void testFindAll() { public void testFindAll() {
var actuals = Lists.newArrayList(repository.findAll());
List<Person> actuals = Lists.newArrayList(repository.findAll());
assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals)); assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals));
} }
@Test @Test
public void testSave() { public void testSave() {
var terry = repository.findByName("Terry");
Person terry = repository.findByName("Terry");
terry.setSurname("Lee"); terry.setSurname("Lee");
terry.setAge(47); terry.setAge(47);
repository.save(terry); repository.save(terry);
@ -86,8 +83,7 @@ public class RepositoryTest {
@Test @Test
public void testDelete() { public void testDelete() {
var terry = repository.findByName("Terry");
Person terry = repository.findByName("Terry");
repository.delete(terry); repository.delete(terry);
assertEquals(3, repository.count()); assertEquals(3, repository.count());
@ -96,14 +92,12 @@ public class RepositoryTest {
@Test @Test
public void testCount() { public void testCount() {
assertEquals(4, repository.count()); assertEquals(4, repository.count());
} }
@Test @Test
public void testFindAllByAgeBetweenSpec() { public void testFindAllByAgeBetweenSpec() {
var persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
List<Person> persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40));
assertEquals(3, persons.size()); assertEquals(3, persons.size());
assertTrue(persons.stream().allMatch(item -> item.getAge() > 20 && item.getAge() < 40)); assertTrue(persons.stream().allMatch(item -> item.getAge() > 20 && item.getAge() < 40));
@ -111,14 +105,13 @@ public class RepositoryTest {
@Test @Test
public void testFindOneByNameEqualSpec() { public void testFindOneByNameEqualSpec() {
var actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry"));
Optional<Person> actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry")); assertTrue(actual.isPresent());
assertEquals(terry, actual.get()); assertEquals(terry, actual.get());
} }
@AfterEach @AfterEach
public void cleanup() { public void cleanup() {
repository.deleteAll(); repository.deleteAll();
} }

View File

@ -54,11 +54,11 @@ public class App {
*/ */
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
try (SlidingDoor slidingDoor = new SlidingDoor()) { try (var ignored = new SlidingDoor()) {
LOGGER.info("Walking in."); LOGGER.info("Walking in.");
} }
try (TreasureChest treasureChest = new TreasureChest()) { try (var ignored = new TreasureChest()) {
LOGGER.info("Looting contents."); LOGGER.info("Looting contents.");
} }
} }

View File

@ -38,7 +38,7 @@ public class SlidingDoor implements AutoCloseable {
} }
@Override @Override
public void close() throws Exception { public void close() {
LOGGER.info("Sliding door closes."); LOGGER.info("Sliding door closes.");
} }
} }

View File

@ -24,7 +24,6 @@
package com.iluwatar.resource.acquisition.is.initialization; package com.iluwatar.resource.acquisition.is.initialization;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -40,7 +39,7 @@ public class TreasureChest implements Closeable {
} }
@Override @Override
public void close() throws IOException { public void close() {
LOGGER.info("Treasure chest closes."); LOGGER.info("Treasure chest closes.");
} }
} }

View File

@ -26,15 +26,12 @@ package com.iluwatar.resource.acquisition.is.initialization;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
*
* Application test * Application test
*
*/ */
public class AppTest { public class AppTest {
@Test @Test
public void test() throws Exception { public void test() throws Exception {
String[] args = {}; App.main(new String[]{});
App.main(args);
} }
} }

View File

@ -23,19 +23,18 @@
package com.iluwatar.resource.acquisition.is.initialization; package com.iluwatar.resource.acquisition.is.initialization;
import static org.junit.jupiter.api.Assertions.assertTrue;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.AppenderBase;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Date: 12/28/15 - 9:31 PM * Date: 12/28/15 - 9:31 PM
* *
@ -56,8 +55,8 @@ public class ClosableTest {
} }
@Test @Test
public void testOpenClose() throws Exception { public void testOpenClose() {
try (final SlidingDoor door = new SlidingDoor(); final TreasureChest chest = new TreasureChest()) { try (final var ignored = new SlidingDoor(); final var ignored1 = new TreasureChest()) {
assertTrue(appender.logContains("Sliding door opens.")); assertTrue(appender.logContains("Sliding door opens."));
assertTrue(appender.logContains("Treasure chest opens.")); assertTrue(appender.logContains("Treasure chest opens."));
} }

View File

@ -78,7 +78,7 @@ to recover from this error.
We can model a 'recoverable' scenario by instantiating `FindCustomer` like this: We can model a 'recoverable' scenario by instantiating `FindCustomer` like this:
```java ```java
final BusinessOperation<String> op = new FindCustomer( final var op = new FindCustomer(
"12345", "12345",
new CustomerNotFoundException("not found"), new CustomerNotFoundException("not found"),
new CustomerNotFoundException("still not found"), new CustomerNotFoundException("still not found"),
@ -97,7 +97,7 @@ worker thread in the database subsystem typically needs 50ms to
this: this:
```java ```java
final BusinessOperation<String> op = new Retry<>( final var op = new Retry<>(
new FindCustomer( new FindCustomer(
"1235", "1235",
new CustomerNotFoundException("not found"), new CustomerNotFoundException("not found"),

View File

@ -90,14 +90,14 @@ public final class App {
} }
private static void errorWithRetry() throws Exception { private static void errorWithRetry() throws Exception {
final Retry<String> retry = new Retry<>( final var retry = new Retry<>(
new FindCustomer("123", new CustomerNotFoundException("not found")), new FindCustomer("123", new CustomerNotFoundException("not found")),
3, //3 attempts 3, //3 attempts
100, //100 ms delay between attempts 100, //100 ms delay between attempts
e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass()) e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass())
); );
op = retry; op = retry;
final String customerId = op.perform(); final var customerId = op.perform();
LOG.info(String.format( LOG.info(String.format(
"However, retrying the operation while ignoring a recoverable error will eventually yield " "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()
@ -105,14 +105,14 @@ public final class App {
} }
private static void errorWithRetryExponentialBackoff() throws Exception { private static void errorWithRetryExponentialBackoff() throws Exception {
final RetryExponentialBackoff<String> retry = new RetryExponentialBackoff<>( final var retry = new RetryExponentialBackoff<>(
new FindCustomer("123", new CustomerNotFoundException("not found")), new FindCustomer("123", new CustomerNotFoundException("not found")),
6, //6 attempts 6, //6 attempts
30000, //30 s max delay between attempts 30000, //30 s max delay between attempts
e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass()) e -> CustomerNotFoundException.class.isAssignableFrom(e.getClass())
); );
op = retry; op = retry;
final String customerId = op.perform(); final var customerId = op.perform();
LOG.info(String.format( LOG.info(String.format(
"However, retrying the operation while ignoring a recoverable error will eventually yield " "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()

View File

@ -100,8 +100,8 @@ public final class RetryExponentialBackoff<T> implements BusinessOperation<T> {
} }
try { try {
long testDelay = (long) Math.pow(2, this.attempts()) * 1000 + RANDOM.nextInt(1000); var testDelay = (long) Math.pow(2, this.attempts()) * 1000 + RANDOM.nextInt(1000);
long delay = testDelay < this.maxDelay ? testDelay : maxDelay; var delay = Math.min(testDelay, this.maxDelay);
Thread.sleep(delay); Thread.sleep(delay);
} catch (InterruptedException f) { } catch (InterruptedException f) {
//ignore //ignore

View File

@ -23,12 +23,12 @@
package com.iluwatar.retry; package com.iluwatar.retry;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
/** /**
* Unit tests for {@link FindCustomer}. * Unit tests for {@link FindCustomer}.
* *
@ -40,33 +40,29 @@ public class FindCustomerTest {
*/ */
@Test @Test
public void noExceptions() throws Exception { public void noExceptions() throws Exception {
assertThat( assertThat(new FindCustomer("123").perform(), is("123"));
new FindCustomer("123").perform(),
is("123")
);
} }
/** /**
* Throws the given exception. * Throws the given exception.
* *
* @throws Exception the expected exception * @throws Exception the expected exception
*/ */
@Test @Test
public void oneException() { public void oneException() {
assertThrows(BusinessException.class, () -> { var findCustomer = new FindCustomer("123", new BusinessException("test"));
new FindCustomer("123", new BusinessException("test")).perform(); assertThrows(BusinessException.class, findCustomer::perform);
});
} }
/** /**
* Should first throw the given exceptions, then return the given result. * Should first throw the given exceptions, then return the given result.
* *
* @throws Exception not an expected exception * @throws Exception not an expected exception
*/ */
@Test @Test
public void resultAfterExceptions() throws Exception { public void resultAfterExceptions() throws Exception {
final BusinessOperation<String> op = new FindCustomer( final var op = new FindCustomer(
"123", "123",
new CustomerNotFoundException("not found"), new CustomerNotFoundException("not found"),
new DatabaseNotAvailableException("not available") new DatabaseNotAvailableException("not available")
); );
@ -81,9 +77,6 @@ public class FindCustomerTest {
//ignore //ignore
} }
assertThat( assertThat(op.perform(), is("123"));
op.perform(),
is("123")
);
} }
} }

View File

@ -23,26 +23,27 @@
package com.iluwatar.retry; package com.iluwatar.retry;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
/** /**
* Unit tests for {@link Retry}. * Unit tests for {@link Retry}.
* *
* @author George Aristy (george.aristy@gmail.com) * @author George Aristy (george.aristy@gmail.com)
*/ */
public class RetryExponentialBackoffTest { public class RetryExponentialBackoffTest {
/** /**
* Should contain all errors thrown. * Should contain all errors thrown.
*/ */
@Test @Test
public void errors() throws Exception { public void errors() {
final BusinessException e = new BusinessException("unhandled"); final var e = new BusinessException("unhandled");
final RetryExponentialBackoff<String> retry = new RetryExponentialBackoff<>( final var retry = new RetryExponentialBackoff<String>(
() -> { () -> {
throw e; throw e;
}, },
2, 2,
0 0
@ -53,22 +54,19 @@ public class RetryExponentialBackoffTest {
//ignore //ignore
} }
assertThat( assertThat(retry.errors(), hasItem(e));
retry.errors(),
hasItem(e)
);
} }
/** /**
* No exceptions will be ignored, hence final number of attempts should be 1 even if we're asking * No exceptions will be ignored, hence final number of attempts should be 1 even if we're asking
* it to attempt twice. * it to attempt twice.
*/ */
@Test @Test
public void attempts() { public void attempts() {
final BusinessException e = new BusinessException("unhandled"); final var e = new BusinessException("unhandled");
final RetryExponentialBackoff<String> retry = new RetryExponentialBackoff<>( final var retry = new RetryExponentialBackoff<String>(
() -> { () -> {
throw e; throw e;
}, },
2, 2,
0 0
@ -79,22 +77,19 @@ public class RetryExponentialBackoffTest {
//ignore //ignore
} }
assertThat( assertThat(retry.attempts(), is(1));
retry.attempts(),
is(1)
);
} }
/** /**
* Final number of attempts should be equal to the number of attempts asked because we are * Final number of attempts should be equal to the number of attempts asked because we are asking
* asking it to ignore the exception that will be thrown. * it to ignore the exception that will be thrown.
*/ */
@Test @Test
public void ignore() throws Exception { public void ignore() {
final BusinessException e = new CustomerNotFoundException("customer not found"); final var e = new CustomerNotFoundException("customer not found");
final RetryExponentialBackoff<String> retry = new RetryExponentialBackoff<>( final var retry = new RetryExponentialBackoff<String>(
() -> { () -> {
throw e; throw e;
}, },
2, 2,
0, 0,
@ -106,9 +101,6 @@ public class RetryExponentialBackoffTest {
//ignore //ignore
} }
assertThat( assertThat(retry.attempts(), is(2));
retry.attempts(),
is(2)
);
} }
} }

View File

@ -23,12 +23,12 @@
package com.iluwatar.retry; package com.iluwatar.retry;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.jupiter.api.Test;
/** /**
* Unit tests for {@link Retry}. * Unit tests for {@link Retry}.
* *
@ -40,10 +40,11 @@ public class RetryTest {
*/ */
@Test @Test
public void errors() { public void errors() {
final BusinessException e = new BusinessException("unhandled"); final var e = new BusinessException("unhandled");
final Retry<String> retry = new Retry<>( final var retry = new Retry<String>(
() -> { () -> {
throw e; }, throw e;
},
2, 2,
0 0
); );
@ -53,10 +54,7 @@ public class RetryTest {
//ignore //ignore
} }
assertThat( assertThat(retry.errors(), hasItem(e));
retry.errors(),
hasItem(e)
);
} }
/** /**
@ -65,10 +63,11 @@ public class RetryTest {
*/ */
@Test @Test
public void attempts() { public void attempts() {
final BusinessException e = new BusinessException("unhandled"); final var e = new BusinessException("unhandled");
final Retry<String> retry = new Retry<>( final var retry = new Retry<String>(
() -> { () -> {
throw e; }, throw e;
},
2, 2,
0 0
); );
@ -78,22 +77,20 @@ public class RetryTest {
//ignore //ignore
} }
assertThat( assertThat(retry.attempts(), is(1));
retry.attempts(),
is(1)
);
} }
/** /**
* Final number of attempts should be equal to the number of attempts asked because we are * Final number of attempts should be equal to the number of attempts asked because we are asking
* asking it to ignore the exception that will be thrown. * it to ignore the exception that will be thrown.
*/ */
@Test @Test
public void ignore() throws Exception { public void ignore() {
final BusinessException e = new CustomerNotFoundException("customer not found"); final var e = new CustomerNotFoundException("customer not found");
final Retry<String> retry = new Retry<>( final var retry = new Retry<String>(
() -> { () -> {
throw e; }, throw e;
},
2, 2,
0, 0,
ex -> CustomerNotFoundException.class.isAssignableFrom(ex.getClass()) ex -> CustomerNotFoundException.class.isAssignableFrom(ex.getClass())
@ -104,10 +101,7 @@ public class RetryTest {
//ignore //ignore
} }
assertThat( assertThat(retry.attempts(), is(2));
retry.attempts(),
is(2)
);
} }
} }

View File

@ -30,37 +30,31 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* The Role Object pattern suggests to model context-specific views * The Role Object pattern suggests to model context-specific views of an object as separate role
* of an object as separate role objects which are * objects which are dynamically attached to and removed from the core object. We call the resulting
* dynamically attached to and removed from the core object. * composite object structure, consisting of the core and its role objects, a subject. A subject
* We call the resulting composite object structure, * often plays several roles and the same role is likely to be played by different subjects. As an
* consisting of the core and its role objects, a subject. * example consider two different customers playing the role of borrower and investor, respectively.
* A subject often plays several roles and the same role is likely to * Both roles could as well be played by a single {@link Customer} object. The common superclass for
* be played by different subjects. * customer-specific roles is provided by {@link CustomerRole}, which also supports the {@link
* As an example consider two different customers playing the role of borrower and * Customer} interface.
* investor, respectively. Both roles could as well be played by a single {@link Customer} object.
* The common superclass for customer-specific roles is provided by {@link CustomerRole},
* which also supports the {@link Customer} interface.
* *
* <p>The {@link CustomerRole} class is abstract and not meant to be instantiated. * <p>The {@link CustomerRole} class is abstract and not meant to be instantiated.
* Concrete subclasses of {@link CustomerRole}, for example {@link BorrowerRole} * Concrete subclasses of {@link CustomerRole}, for example {@link BorrowerRole} or {@link
* or {@link InvestorRole}, define and implement the interface for specific roles. It is only * InvestorRole}, define and implement the interface for specific roles. It is only these subclasses
* these subclasses which are instantiated at runtime. * which are instantiated at runtime. The {@link BorrowerRole} class defines the context-specific
* The {@link BorrowerRole} class defines the context-specific view of {@link Customer} * view of {@link Customer} objects as needed by the loan department. It defines additional
* objects as needed by the loan department. * operations to manage the customers credits and securities. Similarly, the {@link InvestorRole}
* It defines additional operations to manage the customers * class adds operations specific to the investment departments view of customers. A client like
* credits and securities. Similarly, the {@link InvestorRole} class adds operations specific * the loan application may either work with objects of the {@link CustomerRole} class, using the
* to the investment departments view of customers. * interface class {@link Customer}, or with objects of concrete {@link CustomerRole} subclasses.
* A client like the loan application may either work with objects of the {@link CustomerRole} * Suppose the loan application knows a particular {@link Customer} instance through its {@link
* class, using the interface class {@link Customer}, or with objects of concrete * Customer} interface. The loan application may want to check whether the {@link Customer} object
* {@link CustomerRole} subclasses. Suppose the loan application knows a particular * plays the role of Borrower. To this end it calls {@link Customer#hasRole(Role)} with a suitable
* {@link Customer} instance through its {@link Customer} interface. The loan application * role specification. For the purpose of our example, lets assume we can name roles with enum. If
* may want to check whether the {@link Customer} object plays the role of Borrower. * the {@link Customer} object can play the role named Borrower, the loan application will ask it
* To this end it calls {@link Customer#hasRole(Role)} with a suitable role specification. For * to return a reference to the corresponding object. The loan application may now use this
* the purpose of our example, lets assume we can name roles with enum. * reference to call Borrower-specific operations.
* If the {@link Customer} object can play the role named Borrower, the loan application will
* ask it to return a reference to the corresponding object.
* The loan application may now use this reference to call Borrower-specific operations.
*/ */
public class ApplicationRoleObject { public class ApplicationRoleObject {
@ -72,13 +66,13 @@ public class ApplicationRoleObject {
* @param args program arguments * @param args program arguments
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Customer customer = Customer.newCustomer(Borrower, Investor); var customer = Customer.newCustomer(Borrower, Investor);
logger.info(" the new customer created : {}", customer); logger.info(" the new customer created : {}", customer);
boolean hasBorrowerRole = customer.hasRole(Borrower); var hasBorrowerRole = customer.hasRole(Borrower);
logger.info(" customer has a borrowed role - {}", hasBorrowerRole); logger.info(" customer has a borrowed role - {}", hasBorrowerRole);
boolean hasInvestorRole = customer.hasRole(Investor); var hasInvestorRole = customer.hasRole(Investor);
logger.info(" customer has an investor role - {}", hasInvestorRole); logger.info(" customer has an investor role - {}", hasInvestorRole);
customer.getRole(Investor, InvestorRole.class) customer.getRole(Investor, InvestorRole.class)

View File

@ -23,6 +23,7 @@
package com.iluwatar.roleobject; package com.iluwatar.roleobject;
import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
/** /**
@ -32,6 +33,7 @@ public abstract class Customer {
/** /**
* Add specific role @see {@link Role}. * Add specific role @see {@link Role}.
*
* @param role to add * @param role to add
* @return true if the operation has been successful otherwise false * @return true if the operation has been successful otherwise false
*/ */
@ -39,6 +41,7 @@ public abstract class Customer {
/** /**
* Check specific role @see {@link Role}. * Check specific role @see {@link Role}.
*
* @param role to check * @param role to check
* @return true if the role exists otherwise false * @return true if the role exists otherwise false
*/ */
@ -47,6 +50,7 @@ public abstract class Customer {
/** /**
* Remove specific role @see {@link Role}. * Remove specific role @see {@link Role}.
*
* @param role to remove * @param role to remove
* @return true if the operation has been successful otherwise false * @return true if the operation has been successful otherwise false
*/ */
@ -54,6 +58,7 @@ public abstract class Customer {
/** /**
* Get specific instance associated with this role @see {@link Role}. * Get specific instance associated with this role @see {@link Role}.
*
* @param role to get * @param role to get
* @param expectedRole instance class expected to get * @param expectedRole instance class expected to get
* @return optional with value if the instance exists and corresponds expected class * @return optional with value if the instance exists and corresponds expected class
@ -67,14 +72,13 @@ public abstract class Customer {
/** /**
* Create {@link Customer} with given roles. * Create {@link Customer} with given roles.
*
* @param role roles * @param role roles
* @return Customer * @return Customer
*/ */
public static Customer newCustomer(Role... role) { public static Customer newCustomer(Role... role) {
Customer customer = newCustomer(); var customer = newCustomer();
for (Role r : role) { Arrays.stream(role).forEach(customer::addRole);
customer.addRole(r);
}
return customer; return customer;
} }

View File

@ -73,7 +73,7 @@ public class CustomerCore extends Customer {
@Override @Override
public String toString() { public String toString() {
String roles = Arrays.toString(this.roles.keySet().toArray()); var roles = Arrays.toString(this.roles.keySet().toArray());
return "Customer{roles=" + roles + "}"; return "Customer{roles=" + roles + "}";
} }
} }

View File

@ -26,5 +26,5 @@ package com.iluwatar.roleobject;
/** /**
* Key abstraction for segregated roles. * Key abstraction for segregated roles.
*/ */
public abstract class CustomerRole extends CustomerCore{ public abstract class CustomerRole extends CustomerCore {
} }

View File

@ -24,7 +24,6 @@
package com.iluwatar.roleobject; package com.iluwatar.roleobject;
import java.util.Optional; import java.util.Optional;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -48,7 +47,7 @@ public enum Role {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends CustomerRole> Optional<T> instance() { public <T extends CustomerRole> Optional<T> instance() {
Class<? extends CustomerRole> typeCst = this.typeCst; var typeCst = this.typeCst;
try { try {
return (Optional<T>) Optional.of(typeCst.newInstance()); return (Optional<T>) Optional.of(typeCst.newInstance());
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {

View File

@ -26,8 +26,8 @@ import org.junit.Test;
public class ApplicationRoleObjectTest { public class ApplicationRoleObjectTest {
@Test @Test
public void mainTest() { public void mainTest() {
ApplicationRoleObject.main(new String[]{}); ApplicationRoleObject.main(new String[]{});
} }
} }

View File

@ -25,16 +25,12 @@ package com.iluwatar.roleobject;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*;
public class BorrowerRoleTest { public class BorrowerRoleTest {
@Test @Test
public void borrowTest() { public void borrowTest() {
BorrowerRole borrowerRole = new BorrowerRole(); var borrowerRole = new BorrowerRole();
borrowerRole.setName("test"); borrowerRole.setName("test");
String res = "Borrower test wants to get some money."; Assert.assertEquals(borrowerRole.borrow(), "Borrower test wants to get some money.");
}
Assert.assertEquals(borrowerRole.borrow(),res);
}
} }

View File

@ -22,82 +22,72 @@
*/ */
package com.iluwatar.roleobject; package com.iluwatar.roleobject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test; import org.junit.Test;
import java.util.Optional;
import static org.junit.Assert.*;
public class CustomerCoreTest { public class CustomerCoreTest {
@Test @Test
public void addRole() { public void addRole() {
CustomerCore core = new CustomerCore(); var core = new CustomerCore();
boolean add = core.addRole(Role.Borrower); assertTrue(core.addRole(Role.Borrower));
assertTrue(add); }
} @Test
public void hasRole() {
var core = new CustomerCore();
core.addRole(Role.Borrower);
assertTrue(core.hasRole(Role.Borrower));
assertFalse(core.hasRole(Role.Investor));
}
@Test @Test
public void hasRole() { public void remRole() {
CustomerCore core = new CustomerCore(); var core = new CustomerCore();
core.addRole(Role.Borrower); core.addRole(Role.Borrower);
boolean has = core.hasRole(Role.Borrower); var bRole = core.getRole(Role.Borrower, BorrowerRole.class);
assertTrue(has); assertTrue(bRole.isPresent());
boolean notHas = core.hasRole(Role.Investor); assertTrue(core.remRole(Role.Borrower));
assertFalse(notHas);
}
@Test var empt = core.getRole(Role.Borrower, BorrowerRole.class);
public void remRole() { assertFalse(empt.isPresent());
CustomerCore core = new CustomerCore(); }
core.addRole(Role.Borrower);
Optional<BorrowerRole> bRole = core.getRole(Role.Borrower, BorrowerRole.class); @Test
assertTrue(bRole.isPresent()); public void getRole() {
var core = new CustomerCore();
core.addRole(Role.Borrower);
boolean res = core.remRole(Role.Borrower); var bRole = core.getRole(Role.Borrower, BorrowerRole.class);
assertTrue(res); assertTrue(bRole.isPresent());
Optional<BorrowerRole> empt = core.getRole(Role.Borrower, BorrowerRole.class); var nonRole = core.getRole(Role.Borrower, InvestorRole.class);
assertFalse(empt.isPresent()); assertFalse(nonRole.isPresent());
} var invRole = core.getRole(Role.Investor, InvestorRole.class);
assertFalse(invRole.isPresent());
@Test }
public void getRole() {
CustomerCore core = new CustomerCore();
core.addRole(Role.Borrower);
Optional<BorrowerRole> bRole = core.getRole(Role.Borrower, BorrowerRole.class);
assertTrue(bRole.isPresent());
Optional<InvestorRole> nonRole = core.getRole(Role.Borrower, InvestorRole.class);
assertFalse(nonRole.isPresent());
Optional<InvestorRole> invRole = core.getRole(Role.Investor, InvestorRole.class);
assertFalse(invRole.isPresent());
} @Test
public void toStringTest() {
var core = new CustomerCore();
core.addRole(Role.Borrower);
assertEquals(core.toString(), "Customer{roles=[Borrower]}");
core = new CustomerCore();
core.addRole(Role.Investor);
assertEquals(core.toString(), "Customer{roles=[Investor]}");
core = new CustomerCore();
assertEquals(core.toString(), "Customer{roles=[]}");
@Test }
public void toStringTest() {
CustomerCore core = new CustomerCore();
core.addRole(Role.Borrower);
assertEquals(core.toString(), "Customer{roles=[Borrower]}");
core = new CustomerCore();
core.addRole(Role.Investor);
assertEquals(core.toString(), "Customer{roles=[Investor]}");
core = new CustomerCore();
assertEquals(core.toString(), "Customer{roles=[]}");
}
} }

View File

@ -27,12 +27,11 @@ import org.junit.Test;
public class InvestorRoleTest { public class InvestorRoleTest {
@Test @Test
public void investTest() { public void investTest() {
InvestorRole investorRole = new InvestorRole(); var investorRole = new InvestorRole();
investorRole.setName("test"); investorRole.setName("test");
investorRole.setAmountToInvest(10); investorRole.setAmountToInvest(10);
String res = "Investor test has invested 10 dollars"; Assert.assertEquals(investorRole.invest(), "Investor test has invested 10 dollars");
Assert.assertEquals(investorRole.invest(), res); }
}
} }

View File

@ -25,16 +25,12 @@ package com.iluwatar.roleobject;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.Optional;
import static org.junit.Assert.*;
public class RoleTest { public class RoleTest {
@Test @Test
public void instanceTest() { public void instanceTest() {
Optional<CustomerRole> instance = Role.Borrower.instance(); var instance = Role.Borrower.instance();
Assert.assertTrue(instance.isPresent()); Assert.assertTrue(instance.isPresent());
Assert.assertEquals(instance.get().getClass(),BorrowerRole.class); Assert.assertEquals(instance.get().getClass(), BorrowerRole.class);
} }
} }