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 @@ -branches85.7% \ No newline at end of file +branches80% \ 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 @@ -coverage94.9% \ No newline at end of file +coverage93.9% \ 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)); + } +}