diff --git a/data-bus/.gitignore b/data-bus/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/data-bus/.gitignore @@ -0,0 +1 @@ +/target diff --git a/data-bus/README.md b/data-bus/README.md new file mode 100644 index 000000000..257192e25 --- /dev/null +++ b/data-bus/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Data Bus +folder: data-bus +permalink: /patterns/data-bus/ + +categories: Architectural +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent + +Allows send of messages/events between components of an application +without them needing to know about each other. They only need to know +about the type of the message/event being sent. + +![data bus pattern uml diagram](./etc/data-bus.urm.png "Data Bus pattern") + +## Applicability +Use Data Bus pattern when + +* you want your components to decide themselves which messages/events they want to receive +* you want to have many-to-many communication +* you want your components to know nothing about each other + +## Related Patterns +Data Bus is similar to + +* Mediator pattern with Data Bus Members deciding for themselves if they want to accept any given message +* Observer pattern but supporting many-to-many communication +* Publish/Subscribe pattern with the Data Bus decoupling the publisher and the subscriber diff --git a/data-bus/etc/data-bus.urm.png b/data-bus/etc/data-bus.urm.png new file mode 100644 index 000000000..8bd214817 Binary files /dev/null and b/data-bus/etc/data-bus.urm.png differ diff --git a/data-bus/etc/data-bus.urm.puml b/data-bus/etc/data-bus.urm.puml new file mode 100644 index 000000000..9ca0ab602 --- /dev/null +++ b/data-bus/etc/data-bus.urm.puml @@ -0,0 +1,77 @@ +@startuml +package com.iluwatar.databus { + class AbstractDataType { + - dataBus : DataBus + + AbstractDataType() + + getDataBus() : DataBus + + setDataBus(dataBus : DataBus) + } + ~class App { + - log : Logger {static} + ~ App() + + main(args : String[]) {static} + } + class DataBus { + - INSTANCE : DataBus {static} + - listeners : Set + + DataBus() + + getInstance() : DataBus {static} + + publish(event : DataType) + + subscribe(member : Member) + + unsubscribe(member : Member) + } + interface DataType { + + getDataBus() : DataBus {abstract} + + setDataBus(DataBus) {abstract} + } + interface Member { + + accept(DataType) {abstract} + } +} +package com.iluwatar.databus.data { + class MessageData { + - message : String + + MessageData(message : String) + + getMessage() : String + + of(message : String) : DataType {static} + } + class StartingData { + - when : LocalDateTime + + StartingData(when : LocalDateTime) + + getWhen() : LocalDateTime + + of(when : LocalDateTime) : DataType {static} + } + class StoppingData { + - when : LocalDateTime + + StoppingData(when : LocalDateTime) + + getWhen() : LocalDateTime + + of(when : LocalDateTime) : DataType {static} + } +} +package com.iluwatar.databus.members { + class CounterMember { + - log : Logger {static} + - name : String + + CounterMember(name : String) + + accept(data : DataType) + - handleEvent(data : MessageData) + } + class StatusMember { + - id : int + - log : Logger {static} + + StatusMember(id : int) + + accept(data : DataType) + - handleEvent(data : StartingData) + - handleEvent(data : StoppingData) + } +} +AbstractDataType --> "-dataBus" DataBus +DataBus --> "-INSTANCE" DataBus +DataBus --> "-listeners" Member +AbstractDataType ..|> DataType +MessageData --|> AbstractDataType +StartingData --|> AbstractDataType +StoppingData --|> AbstractDataType +CounterMember ..|> Member +StatusMember ..|> Member +@enduml \ No newline at end of file diff --git a/data-bus/pom.xml b/data-bus/pom.xml new file mode 100644 index 000000000..68065f00a --- /dev/null +++ b/data-bus/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + 1.16.14 + + + com.iluwatar + java-design-patterns + 1.16.0-SNAPSHOT + + data-bus + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/data-bus/src/main/java/com/iluwatar/databus/AbstractDataType.java b/data-bus/src/main/java/com/iluwatar/databus/AbstractDataType.java new file mode 100644 index 000000000..5ba6653de --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/AbstractDataType.java @@ -0,0 +1,45 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Paul Campbell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package com.iluwatar.databus; + +/** + * Base for data to send via the Data-Bus. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class AbstractDataType implements DataType { + + private DataBus dataBus; + + @Override + public DataBus getDataBus() { + return dataBus; + } + + @Override + public void setDataBus(DataBus dataBus) { + this.dataBus = dataBus; + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/App.java b/data-bus/src/main/java/com/iluwatar/databus/App.java new file mode 100644 index 000000000..748bb6af0 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/App.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus; + +import com.iluwatar.databus.data.MessageData; +import com.iluwatar.databus.data.StartingData; +import com.iluwatar.databus.data.StoppingData; +import com.iluwatar.databus.members.MessageCollectorMember; +import com.iluwatar.databus.members.StatusMember; + +import java.time.LocalDateTime; + +/** + * The Data Bus pattern + *

+ *

{@see http://wiki.c2.com/?DataBusPattern}

+ *

+ *

The Data-Bus pattern provides a method where different parts of an application may + * pass messages between each other without needing to be aware of the other's existence.

+ *

Similar to the {@code ObserverPattern}, members register themselves with the {@link DataBus} + * and may then receive each piece of data that is published to the Data-Bus. The member + * may react to any given message or not.

+ *

It allows for Many-to-Many distribution of data, as there may be any number of + * publishers to a Data-Bus, and any number of members receiving the data. All members + * will receive the same data, the order each receives a given piece of data, is an + * implementation detail.

+ *

Members may unsubscribe from the Data-Bus to stop receiving data.

+ *

This example of the pattern implements a Synchronous Data-Bus, meaning that + * when data is published to the Data-Bus, the publish method will not return until + * all members have received the data and returned.

+ *

The {@link DataBus} class is a Singleton.

+ *

Members of the Data-Bus must implement the {@link Member} interface.

+ *

Data to be published via the Data-Bus must implement the {@link DataType} interface.

+ *

The {@code data} package contains example {@link DataType} implementations.

+ *

The {@code members} package contains example {@link Member} implementations.

+ *

The {@link StatusMember} demonstrates using the DataBus to publish a message + * to the Data-Bus when it receives a message.

+ * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +class App { + + public static void main(String[] args) { + final DataBus bus = DataBus.getInstance(); + bus.subscribe(new StatusMember(1)); + bus.subscribe(new StatusMember(2)); + final MessageCollectorMember foo = new MessageCollectorMember("Foo"); + final MessageCollectorMember bar = new MessageCollectorMember("Bar"); + bus.subscribe(foo); + bus.publish(StartingData.of(LocalDateTime.now())); + bus.publish(MessageData.of("Only Foo should see this")); + bus.subscribe(bar); + bus.publish(MessageData.of("Foo and Bar should see this")); + bus.unsubscribe(foo); + bus.publish(MessageData.of("Only Bar should see this")); + bus.publish(StoppingData.of(LocalDateTime.now())); + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/DataBus.java b/data-bus/src/main/java/com/iluwatar/databus/DataBus.java new file mode 100644 index 000000000..edaefe623 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/DataBus.java @@ -0,0 +1,73 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus; + +import java.util.HashSet; +import java.util.Set; + +/** + * The Data-Bus implementation. + * + *

This implementation uses a Singleton.

+ * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class DataBus { + + private static final DataBus INSTANCE = new DataBus(); + + private final Set listeners = new HashSet<>(); + + public static DataBus getInstance() { + return INSTANCE; + } + + /** + * Register a member with the data-bus to start receiving events. + * + * @param member The member to register + */ + public void subscribe(final Member member) { + this.listeners.add(member); + } + + /** + * Deregister a member to stop receiving events. + * + * @param member The member to deregister + */ + public void unsubscribe(final Member member) { + this.listeners.remove(member); + } + + /** + * Publish and event to all members. + * + * @param event The event + */ + public void publish(final DataType event) { + event.setDataBus(this); + listeners.forEach(listener -> listener.accept(event)); + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/DataType.java b/data-bus/src/main/java/com/iluwatar/databus/DataType.java new file mode 100644 index 000000000..e5729c19d --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/DataType.java @@ -0,0 +1,48 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Paul Campbell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package com.iluwatar.databus; + +/** + * Events are sent via the Data-Bus. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ + +public interface DataType { + + /** + * Returns the data-bus the event is being sent on. + * + * @return The data-bus + */ + DataBus getDataBus(); + + /** + * Set the data-bus the event will be sent on. + * + * @param dataBus The data-bus + */ + void setDataBus(DataBus dataBus); +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/Member.java b/data-bus/src/main/java/com/iluwatar/databus/Member.java new file mode 100644 index 000000000..d5ecb0152 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/Member.java @@ -0,0 +1,37 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Paul Campbell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package com.iluwatar.databus; + +import java.util.function.Consumer; + +/** + * Members receive events from the Data-Bus. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public interface Member extends Consumer { + + void accept(DataType event); +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/data/MessageData.java b/data-bus/src/main/java/com/iluwatar/databus/data/MessageData.java new file mode 100644 index 000000000..ac541cf20 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/data/MessageData.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus.data; + +import com.iluwatar.databus.AbstractDataType; +import com.iluwatar.databus.DataType; + +/** + * An event raised when a string message is sent. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class MessageData extends AbstractDataType { + + private final String message; + + public MessageData(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public static DataType of(final String message) { + return new MessageData(message); + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/data/StartingData.java b/data-bus/src/main/java/com/iluwatar/databus/data/StartingData.java new file mode 100644 index 000000000..ac7391f1a --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/data/StartingData.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus.data; + +import com.iluwatar.databus.AbstractDataType; +import com.iluwatar.databus.DataType; + +import java.time.LocalDateTime; + +/** + * An event raised when applications starts, containing the start time of the application. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class StartingData extends AbstractDataType { + + private final LocalDateTime when; + + public StartingData(LocalDateTime when) { + this.when = when; + } + + public LocalDateTime getWhen() { + return when; + } + + public static DataType of(final LocalDateTime when) { + return new StartingData(when); + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/data/StoppingData.java b/data-bus/src/main/java/com/iluwatar/databus/data/StoppingData.java new file mode 100644 index 000000000..976fdc4e9 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/data/StoppingData.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus.data; + +import com.iluwatar.databus.AbstractDataType; +import com.iluwatar.databus.DataType; + +import java.time.LocalDateTime; + +/** + * An event raised when applications stops, containing the stop time of the application. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class StoppingData extends AbstractDataType { + + private final LocalDateTime when; + + public StoppingData(LocalDateTime when) { + this.when = when; + } + + public LocalDateTime getWhen() { + return when; + } + + public static DataType of(final LocalDateTime when) { + return new StoppingData(when); + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java b/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java new file mode 100644 index 000000000..a8ee94ad5 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus.members; + +import com.iluwatar.databus.DataType; +import com.iluwatar.databus.Member; +import com.iluwatar.databus.data.MessageData; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +/** + * Receiver of Data-Bus events that collects the messages from each {@link MessageData}. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class MessageCollectorMember implements Member { + + private static final Logger LOGGER = Logger.getLogger(MessageCollectorMember.class.getName()); + + private final String name; + + private List messages = new ArrayList<>(); + + public MessageCollectorMember(String name) { + this.name = name; + } + + @Override + public void accept(final DataType data) { + if (data instanceof MessageData) { + handleEvent((MessageData) data); + } + } + + private void handleEvent(MessageData data) { + LOGGER.info(String.format("%s sees message %s", name, data.getMessage())); + messages.add(data.getMessage()); + } + + public List getMessages() { + return Collections.unmodifiableList(messages); + } +} diff --git a/data-bus/src/main/java/com/iluwatar/databus/members/StatusMember.java b/data-bus/src/main/java/com/iluwatar/databus/members/StatusMember.java new file mode 100644 index 000000000..803e2df20 --- /dev/null +++ b/data-bus/src/main/java/com/iluwatar/databus/members/StatusMember.java @@ -0,0 +1,82 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.databus.members; + +import com.iluwatar.databus.DataType; +import com.iluwatar.databus.Member; +import com.iluwatar.databus.data.MessageData; +import com.iluwatar.databus.data.StartingData; +import com.iluwatar.databus.data.StoppingData; + +import java.time.LocalDateTime; +import java.util.logging.Logger; + +/** + * Receiver of Data-Bus events. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class StatusMember implements Member { + + private static final Logger LOGGER = Logger.getLogger(StatusMember.class.getName()); + + private final int id; + + private LocalDateTime started; + + private LocalDateTime stopped; + + public StatusMember(int id) { + this.id = id; + } + + @Override + public void accept(final DataType data) { + if (data instanceof StartingData) { + handleEvent((StartingData) data); + } else if (data instanceof StoppingData) { + handleEvent((StoppingData) data); + } + } + + private void handleEvent(StartingData data) { + started = data.getWhen(); + LOGGER.info(String.format("Receiver #%d sees application started at %s", id, started)); + } + + private void handleEvent(StoppingData data) { + stopped = data.getWhen(); + LOGGER.info(String.format("Receiver #%d sees application stopping at %s", id, stopped)); + LOGGER.info(String.format("Receiver #%d sending goodbye message", id)); + data.getDataBus().publish(MessageData.of(String.format("Goodbye cruel world from #%d!", id))); + } + + public LocalDateTime getStarted() { + return started; + } + + public LocalDateTime getStopped() { + return stopped; + } +} diff --git a/data-bus/src/test/java/com/iluwatar/databus/DataBusTest.java b/data-bus/src/test/java/com/iluwatar/databus/DataBusTest.java new file mode 100644 index 000000000..90c326ebf --- /dev/null +++ b/data-bus/src/test/java/com/iluwatar/databus/DataBusTest.java @@ -0,0 +1,52 @@ +package com.iluwatar.databus; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.never; + +/** + * Tests for {@link DataBus}. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class DataBusTest { + + @Mock + private Member member; + + @Mock + private DataType event; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void publishedEventIsReceivedBySubscribedMember() { + //given + final DataBus dataBus = DataBus.getInstance(); + dataBus.subscribe(member); + //when + dataBus.publish(event); + //then + then(member).should().accept(event); + } + + @Test + public void publishedEventIsNotReceivedByMemberAfterUnsubscribing() { + //given + final DataBus dataBus = DataBus.getInstance(); + dataBus.subscribe(member); + dataBus.unsubscribe(member); + //when + dataBus.publish(event); + //then + then(member).should(never()).accept(event); + } + +} diff --git a/data-bus/src/test/java/com/iluwatar/databus/members/MessageCollectorMemberTest.java b/data-bus/src/test/java/com/iluwatar/databus/members/MessageCollectorMemberTest.java new file mode 100644 index 000000000..96fc090ee --- /dev/null +++ b/data-bus/src/test/java/com/iluwatar/databus/members/MessageCollectorMemberTest.java @@ -0,0 +1,40 @@ +package com.iluwatar.databus.members; + +import com.iluwatar.databus.data.MessageData; +import com.iluwatar.databus.data.StartingData; +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDateTime; + +/** + * Tests for {@link MessageCollectorMember}. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class MessageCollectorMemberTest { + + @Test + public void collectMessageFromMessageData() { + //given + final String message = "message"; + final MessageData messageData = new MessageData(message); + final MessageCollectorMember collector = new MessageCollectorMember("collector"); + //when + collector.accept(messageData); + //then + Assert.assertTrue(collector.getMessages().contains(message)); + } + + @Test + public void collectIgnoresMessageFromOtherDataTypes() { + //given + final StartingData startingData = new StartingData(LocalDateTime.now()); + final MessageCollectorMember collector = new MessageCollectorMember("collector"); + //when + collector.accept(startingData); + //then + Assert.assertEquals(0, collector.getMessages().size()); + } + +} diff --git a/data-bus/src/test/java/com/iluwatar/databus/members/StatusMemberTest.java b/data-bus/src/test/java/com/iluwatar/databus/members/StatusMemberTest.java new file mode 100644 index 000000000..e5983dcea --- /dev/null +++ b/data-bus/src/test/java/com/iluwatar/databus/members/StatusMemberTest.java @@ -0,0 +1,57 @@ +package com.iluwatar.databus.members; + +import com.iluwatar.databus.DataBus; +import com.iluwatar.databus.data.MessageData; +import com.iluwatar.databus.data.StartingData; +import com.iluwatar.databus.data.StoppingData; +import org.junit.Assert; +import org.junit.Test; + +import java.time.LocalDateTime; +import java.time.Month; + +/** + * Tests for {@link StatusMember}. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +public class StatusMemberTest { + + @Test + public void statusRecordsTheStartTime() { + //given + final LocalDateTime startTime = LocalDateTime.of(2017, Month.APRIL, 1, 19, 9); + final StartingData startingData = new StartingData(startTime); + final StatusMember statusMember = new StatusMember(1); + //when + statusMember.accept(startingData); + //then + Assert.assertEquals(startTime, statusMember.getStarted()); + } + + @Test + public void statusRecordsTheStopTime() { + //given + final LocalDateTime stop = LocalDateTime.of(2017, Month.APRIL, 1, 19, 12); + final StoppingData stoppingData = new StoppingData(stop); + stoppingData.setDataBus(DataBus.getInstance()); + final StatusMember statusMember = new StatusMember(1); + //when + statusMember.accept(stoppingData); + //then + Assert.assertEquals(stop, statusMember.getStopped()); + } + + @Test + public void statusIgnoresMessageData() { + //given + final MessageData messageData = new MessageData("message"); + final StatusMember statusMember = new StatusMember(1); + //when + statusMember.accept(messageData); + //then + Assert.assertNull(statusMember.getStarted()); + Assert.assertNull(statusMember.getStopped()); + } + +} diff --git a/pom.xml b/pom.xml index 4b9740857..a45f0e1f3 100644 --- a/pom.xml +++ b/pom.xml @@ -135,6 +135,7 @@ event-asynchronous queue-load-leveling object-mother + data-bus converter guarded-suspension balking