diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg
index af316de..4de1414 100644
--- a/.github/badges/branches.svg
+++ b/.github/badges/branches.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
index 737c552..d314160 100644
--- a/.github/badges/jacoco.svg
+++ b/.github/badges/jacoco.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/README.md b/README.md
index d046e0c..59c44f8 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@ cd java-patterns-and-constructs
### Patterns
* [Abstract Factory](src/main/java/com/penapereira/example/constructs/abstractfactory/)
* [Adapter](src/main/java/com/penapereira/example/constructs/adapter/)
+* [Chain of Responsibility](src/main/java/com/penapereira/example/constructs/chainofresponsibility/)
* [Decorator](src/main/java/com/penapereira/example/constructs/decorator/)
* [Factory](src/main/java/com/penapereira/example/constructs/factory/)
* [Factory Method](src/main/java/com/penapereira/example/constructs/factorymethod/)
diff --git a/src/main/java/com/penapereira/example/constructs/app/AppCommandLineRunner.java b/src/main/java/com/penapereira/example/constructs/app/AppCommandLineRunner.java
index c5842d8..83b4e9d 100644
--- a/src/main/java/com/penapereira/example/constructs/app/AppCommandLineRunner.java
+++ b/src/main/java/com/penapereira/example/constructs/app/AppCommandLineRunner.java
@@ -23,12 +23,14 @@ public class AppCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
- log.info(msg.getGreeting());
- log.info(msg.getHomeUrl());
EventQueue.invokeLater(() -> {
main.initializeFrame();
main.setVisible(true);
});
+ log.info(msg.getSeparator());
+ log.info(msg.getGreeting());
+ log.info(msg.getHomeUrl());
+ log.info(msg.getInstructions());
}
}
diff --git a/src/main/java/com/penapereira/example/constructs/app/ExamplesCommandLineRunner.java b/src/main/java/com/penapereira/example/constructs/app/ExamplesCommandLineRunner.java
index a90d9ab..42946c9 100644
--- a/src/main/java/com/penapereira/example/constructs/app/ExamplesCommandLineRunner.java
+++ b/src/main/java/com/penapereira/example/constructs/app/ExamplesCommandLineRunner.java
@@ -10,6 +10,7 @@
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
+import com.penapereira.example.constructs.app.properties.ApplicationProperties;
import com.penapereira.example.constructs.app.properties.Messages;
@Component
@@ -23,12 +24,17 @@ public class ExamplesCommandLineRunner implements CommandLineRunner {
@Autowired
Messages msg;
+ @Autowired
+ ApplicationProperties props;
+
@Override
public void run(String... args) throws Exception {
List beanNames = Arrays.asList(ctx.getBeanNamesForType(ExampleRunnerInterface.class));
-
- listExamples(beanNames);
- executeExamples(beanNames);
+
+ if (props.getEnableCommandLineRunner()) {
+ listExamples(beanNames);
+ executeExamples(beanNames);
+ }
}
private void listExamples(List beanNames) {
diff --git a/src/main/java/com/penapereira/example/constructs/app/properties/ApplicationProperties.java b/src/main/java/com/penapereira/example/constructs/app/properties/ApplicationProperties.java
index c0a59ef..f624d48 100644
--- a/src/main/java/com/penapereira/example/constructs/app/properties/ApplicationProperties.java
+++ b/src/main/java/com/penapereira/example/constructs/app/properties/ApplicationProperties.java
@@ -20,4 +20,6 @@ public class ApplicationProperties {
@Value("classpath:icon.png")
protected Resource appIcon;
+
+ protected Boolean enableCommandLineRunner;
}
diff --git a/src/main/java/com/penapereira/example/constructs/app/properties/Messages.java b/src/main/java/com/penapereira/example/constructs/app/properties/Messages.java
index 3c46b0a..0e74f79 100644
--- a/src/main/java/com/penapereira/example/constructs/app/properties/Messages.java
+++ b/src/main/java/com/penapereira/example/constructs/app/properties/Messages.java
@@ -16,7 +16,9 @@ public class Messages {
protected String info;
protected String examplesFound;
protected String enableTraceToSeeExamplesDetails;
- protected String enableDebugToSeeExamplesList;
- protected String separator;
- protected String outputTitle;
+ protected String enableDebugToSeeExamplesList;
+ protected String separator;
+ protected String outputTitle;
+ protected String preserveLog;
+ protected String instructions;
}
diff --git a/src/main/java/com/penapereira/example/constructs/app/ui/MainWindow.java b/src/main/java/com/penapereira/example/constructs/app/ui/MainWindow.java
index a112fd3..aed7bfe 100644
--- a/src/main/java/com/penapereira/example/constructs/app/ui/MainWindow.java
+++ b/src/main/java/com/penapereira/example/constructs/app/ui/MainWindow.java
@@ -8,21 +8,25 @@
import java.io.IOException;
import javax.imageio.ImageIO;
+import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
+import javax.swing.JCheckBox;
import javax.swing.SwingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.penapereira.example.constructs.app.properties.ApplicationProperties;
import com.penapereira.example.constructs.app.properties.Messages;
+import com.penapereira.example.constructs.app.ExampleRunnerInterface;
@Component
public class MainWindow extends JFrame {
@@ -31,13 +35,19 @@ public class MainWindow extends JFrame {
private static final long serialVersionUID = 1L;
- private JTextArea outputArea;
+ private JTextArea outputArea;
+ private JCheckBox preserveLogCheck;
@Autowired
Messages msg;
- @Autowired
- ApplicationProperties props;
+ @Autowired
+ ApplicationProperties props;
+
+ @Autowired
+ ApplicationContext ctx;
+
+ private java.util.Map examples;
public MainWindow() {
super();
@@ -73,19 +83,28 @@ private void loadIcon() {
private JPanel getMainComponent() {
JPanel mainPanel = new JPanel(new BorderLayout());
- JPanel infoPanel = new JPanel(new GridLayout(4, 1));
- createCenteredTitle(msg.getGreeting(), infoPanel);
- createCenteredLabelOnPanel(msg.getInfo(), infoPanel);
- createCenteredHyperlink(msg.getHomeUrl(), infoPanel);
- mainPanel.add(infoPanel, BorderLayout.NORTH);
+ JPanel infoPanel = new JPanel(new GridLayout(4, 1));
+ createCenteredTitle(msg.getGreeting(), infoPanel);
+ createCenteredLabelOnPanel(msg.getInfo(), infoPanel);
+ createCenteredHyperlink(msg.getHomeUrl(), infoPanel);
+ mainPanel.add(infoPanel, BorderLayout.NORTH);
+
+ mainPanel.add(createExamplesPanel(), BorderLayout.WEST);
- outputArea = new JTextArea(10, 40);
- outputArea.setEditable(false);
- JScrollPane scrollPane = new JScrollPane(outputArea);
- scrollPane.setBorder(javax.swing.BorderFactory.createTitledBorder(msg.getOutputTitle()));
- mainPanel.add(scrollPane, BorderLayout.CENTER);
+ outputArea = new JTextArea(10, 40);
+ outputArea.setEditable(false);
+ JScrollPane scrollPane = new JScrollPane(outputArea);
+ scrollPane.setBorder(javax.swing.BorderFactory.createTitledBorder(msg.getOutputTitle()));
- return mainPanel;
+ preserveLogCheck = new JCheckBox(msg.getPreserveLog());
+
+ JPanel outputPanel = new JPanel(new BorderLayout());
+ outputPanel.add(preserveLogCheck, BorderLayout.NORTH);
+ outputPanel.add(scrollPane, BorderLayout.CENTER);
+
+ mainPanel.add(outputPanel, BorderLayout.CENTER);
+
+ return mainPanel;
}
private void createCenteredTitle(String text, JPanel panel) {
@@ -117,4 +136,55 @@ public void appendOutput(String text) {
}
}
+ private JPanel createExamplesPanel() {
+ examples = ctx.getBeansOfType(ExampleRunnerInterface.class);
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setBorder(javax.swing.BorderFactory.createTitledBorder(msg.getExamplesFound()));
+
+ javax.swing.JButton allButton = new javax.swing.JButton("Run All");
+ allButton.addActionListener(e -> runAllExamples());
+ panel.add(allButton);
+
+ examples.forEach((name, runner) -> {
+ String clean = name.replaceFirst("ExampleRunner", "");
+ javax.swing.JButton btn = new javax.swing.JButton(clean);
+ btn.addActionListener(e -> runExample(runner));
+ panel.add(btn);
+ });
+
+ return panel;
+ }
+
+ private void runExample(ExampleRunnerInterface runner) {
+ if (!preserveLogCheck.isSelected()) {
+ outputArea.setText("");
+ }
+ new Thread(() -> {
+ try {
+ log.trace(msg.getSeparator());
+ runner.runExample();
+ } catch (Exception e) {
+ log.error("Error executing example", e);
+ }
+ }).start();
+ }
+
+ private void runAllExamples() {
+ if (!preserveLogCheck.isSelected()) {
+ outputArea.setText("");
+ }
+ new Thread(() -> {
+ examples.values().forEach(r -> {
+ try {
+ log.trace(msg.getSeparator());
+ r.runExample();
+ } catch (Exception e) {
+ log.error("Error executing example", e);
+ }
+ });
+ }).start();
+ }
+
}
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/AbstractHandler.java b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/AbstractHandler.java
new file mode 100644
index 0000000..df48194
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/AbstractHandler.java
@@ -0,0 +1,18 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+public abstract class AbstractHandler implements Handler {
+ private Handler next;
+
+ @Override
+ public void setNext(Handler next) {
+ this.next = next;
+ }
+
+ @Override
+ public String handle(int request) {
+ if (next != null) {
+ return next.handle(request);
+ }
+ return "unhandled";
+ }
+}
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/ChainOfResponsibilityExampleRunner.java b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/ChainOfResponsibilityExampleRunner.java
new file mode 100644
index 0000000..e2b2f4c
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/ChainOfResponsibilityExampleRunner.java
@@ -0,0 +1,30 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.penapereira.example.constructs.app.ExampleRunnerInterface;
+
+@Component
+public class ChainOfResponsibilityExampleRunner implements ExampleRunnerInterface {
+
+ private static final Logger log = LoggerFactory.getLogger(ChainOfResponsibilityExampleRunner.class);
+
+ @Override
+ public void runExample() throws Exception {
+ log.trace("Executing Chain of Responsibility Pattern Implementation");
+
+ Handler negative = new NegativeHandler();
+ Handler zero = new ZeroHandler();
+ Handler positive = new PositiveHandler();
+
+ negative.setNext(zero);
+ zero.setNext(positive);
+
+ int[] requests = { -1, 0, 1 };
+ for (int r : requests) {
+ log.trace(" " + r + " is " + negative.handle(r));
+ }
+ }
+}
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/Handler.java b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/Handler.java
new file mode 100644
index 0000000..fecec82
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/Handler.java
@@ -0,0 +1,6 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+public interface Handler {
+ void setNext(Handler next);
+ String handle(int request);
+}
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/NegativeHandler.java b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/NegativeHandler.java
new file mode 100644
index 0000000..d915f05
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/NegativeHandler.java
@@ -0,0 +1,11 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+public class NegativeHandler extends AbstractHandler {
+ @Override
+ public String handle(int request) {
+ if (request < 0) {
+ return "negative";
+ }
+ return super.handle(request);
+ }
+}
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/PositiveHandler.java b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/PositiveHandler.java
new file mode 100644
index 0000000..4f928f8
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/PositiveHandler.java
@@ -0,0 +1,11 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+public class PositiveHandler extends AbstractHandler {
+ @Override
+ public String handle(int request) {
+ if (request > 0) {
+ return "positive";
+ }
+ return super.handle(request);
+ }
+}
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md
new file mode 100644
index 0000000..3953a2c
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/README.md
@@ -0,0 +1,23 @@
+# Chain of Responsibility Pattern
+
+The chain of responsibility pattern passes a request along a chain of handlers until one of them deals with it.
+
+## Class diagram
+
+
+```plantuml
+@startuml
+interface Handler {
+ +setNext(h)
+ +handle(request)
+}
+abstract class AbstractHandler implements Handler {
+ -next : Handler
+}
+class NegativeHandler extends AbstractHandler
+class ZeroHandler extends AbstractHandler
+class PositiveHandler extends AbstractHandler
+Handler <|.. AbstractHandler
+AbstractHandler --> Handler : next
+@enduml
+```
diff --git a/src/main/java/com/penapereira/example/constructs/chainofresponsibility/ZeroHandler.java b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/ZeroHandler.java
new file mode 100644
index 0000000..0381f77
--- /dev/null
+++ b/src/main/java/com/penapereira/example/constructs/chainofresponsibility/ZeroHandler.java
@@ -0,0 +1,11 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+public class ZeroHandler extends AbstractHandler {
+ @Override
+ public String handle(int request) {
+ if (request == 0) {
+ return "zero";
+ }
+ return super.handle(request);
+ }
+}
diff --git a/src/main/java/com/penapereira/example/constructs/decorator/DecoratorExampleRunner.java b/src/main/java/com/penapereira/example/constructs/decorator/DecoratorExampleRunner.java
index 9316a9e..97662bf 100644
--- a/src/main/java/com/penapereira/example/constructs/decorator/DecoratorExampleRunner.java
+++ b/src/main/java/com/penapereira/example/constructs/decorator/DecoratorExampleRunner.java
@@ -6,8 +6,6 @@
import com.penapereira.example.constructs.app.ExampleRunnerInterface;
-import com.penapereira.example.constructs.decorator.ComponentIF;
-
@Component
public class DecoratorExampleRunner implements ExampleRunnerInterface {
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 5fb1a0c..98694dd 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -7,6 +7,7 @@ app.windowMarginX=50
app.windowMarginY=50
app.linkColor=#0645AD
app.linkColorHover=#3366BB
+app.enableCommandLineRunner=false
# Messages
@@ -14,10 +15,11 @@ msg.windowTitle=Java Patterns and Constructs
msg.greeting=Welcome to Java Patterns and Constructs
msg.info=You can check documentation, examples and diagrams at:
msg.homeUrl=https://github.com/lpenap/java-patterns-and-constructs
-msg.examplesFound=Implemented examples found
+msg.examplesFound=Examples
+msg.instructions=To run an example, click on the button for the desired example.
msg.enableTraceToSeeExamplesDetails= [*] Please enable TRACE log level if you want to see examples output
msg.enableDebugToSeeExamplesList= [*] Please enable DEBUG log level if you want to see the examples list
-msg.outputTitle=Output
-
+msg.outputTitle=Logger Output
+msg.preserveLog=Preserve log
msg.separator=-------------------------------------------------------------
\ No newline at end of file
diff --git a/src/test/java/com/penapereira/example/constructs/chainofresponsibility/ChainOfResponsibilityExampleRunnerTests.java b/src/test/java/com/penapereira/example/constructs/chainofresponsibility/ChainOfResponsibilityExampleRunnerTests.java
new file mode 100644
index 0000000..377bb62
--- /dev/null
+++ b/src/test/java/com/penapereira/example/constructs/chainofresponsibility/ChainOfResponsibilityExampleRunnerTests.java
@@ -0,0 +1,10 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+import org.junit.jupiter.api.Test;
+
+class ChainOfResponsibilityExampleRunnerTests {
+ @Test
+ void runExampleRuns() throws Exception {
+ new ChainOfResponsibilityExampleRunner().runExample();
+ }
+}
diff --git a/src/test/java/com/penapereira/example/constructs/chainofresponsibility/ChainTests.java b/src/test/java/com/penapereira/example/constructs/chainofresponsibility/ChainTests.java
new file mode 100644
index 0000000..ae4b36d
--- /dev/null
+++ b/src/test/java/com/penapereira/example/constructs/chainofresponsibility/ChainTests.java
@@ -0,0 +1,20 @@
+package com.penapereira.example.constructs.chainofresponsibility;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class ChainTests {
+ @Test
+ void chainProcessesNumbers() {
+ Handler negative = new NegativeHandler();
+ Handler zero = new ZeroHandler();
+ Handler positive = new PositiveHandler();
+ negative.setNext(zero);
+ zero.setNext(positive);
+
+ assertEquals("negative", negative.handle(-10));
+ assertEquals("zero", negative.handle(0));
+ assertEquals("positive", negative.handle(5));
+ }
+}