Skip to content

Commit c8a2ef0

Browse files
noamgrinchohbus
andauthored
pattern: Active-Object pattern. (iluwatar#1660)
* Closes iluwatar#65. * Removed * Removed unnecessary files. Added logging. Closes Fixes iluwatar#1660. * Added Terminition condition. * Logger implemented. Removed maven wrapper. * Added module to parent POM. removed .gitignore * Replaced tabs with whitespaces, added Javadocs. * Fixed more whitespaces problems. * Fixed more checkstyle errors * More checkstyle errors. * Checkstyle errors. * Final checkstyle cleanup * Added UML file. Changed System.exit() to Runtime. * Changed buisiness logic and readme.md file * Changed typos and readme.md file * Fixed checkstyle errors * Fixed grammer errors and CircleCI bugs. * Wrong readme.md * Added Thread.interrupt() for after catching exception. * Fixed SonarCloud code smells. * Removed unused brackets. * Changed main program exit logic. Added tests. * Reverted abstract-factory * Cleaned code * Added static to loggers. cleaned code smells. * Checkstyle errors. * Code Smells. Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
1 parent cbf1847 commit c8a2ef0

File tree

11 files changed

+438
-10
lines changed

11 files changed

+438
-10
lines changed

abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,16 @@
2828
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2929

3030
/**
31-
* Tests that Abstract Factory example runs without errors.
31+
* Issue: Add at least one assertion to this test case.
32+
*
33+
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
34+
* throws an exception.
3235
*/
3336
class AppTest {
34-
35-
/**
36-
* Issue: Add at least one assertion to this test case.
37-
*
38-
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
39-
* throws an exception.
40-
*/
41-
37+
4238
@Test
4339
void shouldExecuteApplicationWithoutException() {
4440

45-
assertDoesNotThrow(() -> App.main(new String[]{}));
41+
assertDoesNotThrow(() -> App.main(new String[]{}));
4642
}
4743
}

active-object/README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
layout: pattern
3+
title: Active Object
4+
folder: active-object
5+
permalink: /patterns/active-object/
6+
categories: Concurrency
7+
tags:
8+
- Performance
9+
---
10+
11+
12+
## Intent
13+
The active object design pattern decouples method execution from method invocation for objects that each reside in their thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests.
14+
15+
## Explanation
16+
17+
The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods.
18+
19+
Real-world example
20+
21+
>The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior.
22+
23+
To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern.
24+
25+
26+
**Programmatic Example**
27+
28+
```java
29+
public abstract class ActiveCreature{
30+
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
31+
32+
private BlockingQueue<Runnable> requests;
33+
34+
private String name;
35+
36+
private Thread thread;
37+
38+
public ActiveCreature(String name) {
39+
this.name = name;
40+
this.requests = new LinkedBlockingQueue<Runnable>();
41+
thread = new Thread(new Runnable() {
42+
@Override
43+
public void run() {
44+
while (true) {
45+
try {
46+
requests.take().run();
47+
} catch (InterruptedException e) {
48+
logger.error(e.getMessage());
49+
}
50+
}
51+
}
52+
}
53+
);
54+
thread.start();
55+
}
56+
57+
public void eat() throws InterruptedException {
58+
requests.put(new Runnable() {
59+
@Override
60+
public void run() {
61+
logger.info("{} is eating!",name());
62+
logger.info("{} has finished eating!",name());
63+
}
64+
}
65+
);
66+
}
67+
68+
public void roam() throws InterruptedException {
69+
requests.put(new Runnable() {
70+
@Override
71+
public void run() {
72+
logger.info("{} has started to roam and the wastelands.",name());
73+
}
74+
}
75+
);
76+
}
77+
78+
public String name() {
79+
return this.name;
80+
}
81+
}
82+
83+
```
84+
85+
We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods.
86+
87+
For example, the Orc class:
88+
```java
89+
public class Orc extends ActiveCreature {
90+
91+
public Orc(String name) {
92+
super(name);
93+
}
94+
95+
}
96+
```
97+
98+
Now, we can create multiple creatures such as Orcs, tell them to eat and roam and they will execute it on their own thread of control:
99+
```java
100+
public static void main(String[] args) {
101+
var app = new App();
102+
app.run();
103+
}
104+
105+
@Override
106+
public void run() {
107+
ActiveCreature creature;
108+
try {
109+
for (int i = 0;i < creatures;i++) {
110+
creature = new Orc(Orc.class.getSimpleName().toString() + i);
111+
creature.eat();
112+
creature.roam();
113+
}
114+
Thread.sleep(1000);
115+
} catch (InterruptedException e) {
116+
logger.error(e.getMessage());
117+
}
118+
Runtime.getRuntime().exit(1);
119+
}
120+
```
121+
122+
## Class diagram
123+
124+
![alt text](./etc/active-object.urm.png "Active Object class diagram")
19 KB
Loading
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@startuml
2+
package com.iluwatar.activeobject {
3+
abstract class ActiveCreature {
4+
- logger : Logger
5+
- name : String
6+
- requests : BlockingQueue<Runnable>
7+
- thread : Thread
8+
+ ActiveCreature(name : String)
9+
+ eat()
10+
+ name() : String
11+
+ roam()
12+
}
13+
class App {
14+
- creatures : Integer
15+
- logger : Logger
16+
+ App()
17+
+ main(args : String[]) {static}
18+
+ run()
19+
}
20+
class Orc {
21+
+ Orc(name : String)
22+
}
23+
}
24+
Orc --|> ActiveCreature
25+
@enduml

active-object/pom.xml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
4+
The MIT License
5+
Copyright © 2014-2021 Ilkka Seppälä
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
25+
-->
26+
<project
27+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
28+
xmlns="http://maven.apache.org/POM/4.0.0"
29+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
30+
<modelVersion>4.0.0</modelVersion>
31+
<parent>
32+
<groupId>com.iluwatar</groupId>
33+
<artifactId>java-design-patterns</artifactId>
34+
<version>1.24.0-SNAPSHOT</version>
35+
</parent>
36+
<artifactId>active-object</artifactId>
37+
<dependencies>
38+
<dependency>
39+
<groupId>org.junit.jupiter</groupId>
40+
<artifactId>junit-jupiter-engine</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
</dependencies>
44+
<build>
45+
<plugins>
46+
<!-- Maven assembly plugin is invoked with default setting which we have
47+
in parent pom and specifying the class having main method -->
48+
<plugin>
49+
<groupId>org.apache.maven.plugins</groupId>
50+
<artifactId>maven-assembly-plugin</artifactId>
51+
<executions>
52+
<execution>
53+
<configuration>
54+
<archive>
55+
<manifest>
56+
<mainClass>com.iluwatar.activeobject.App</mainClass>
57+
</manifest>
58+
</archive>
59+
</configuration>
60+
</execution>
61+
</executions>
62+
</plugin>
63+
</plugins>
64+
</build>
65+
</project>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.iluwatar.activeobject;
2+
3+
import java.util.concurrent.BlockingQueue;
4+
import java.util.concurrent.LinkedBlockingQueue;
5+
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
/**
10+
* ActiveCreature class is the base of the active object example.
11+
* @author Noam Greenshtain
12+
*
13+
*/
14+
public abstract class ActiveCreature {
15+
16+
private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
17+
18+
private BlockingQueue<Runnable> requests;
19+
20+
private String name;
21+
22+
private Thread thread; // Thread of execution.
23+
24+
private int status; // status of the thread of execution.
25+
26+
/**
27+
* Constructor and initialization.
28+
*/
29+
protected ActiveCreature(String name) {
30+
this.name = name;
31+
this.status = 0;
32+
this.requests = new LinkedBlockingQueue<>();
33+
thread = new Thread(() -> {
34+
boolean infinite = true;
35+
while (infinite) {
36+
try {
37+
requests.take().run();
38+
} catch (InterruptedException e) {
39+
if (this.status != 0) {
40+
logger.error("Thread was interrupted. --> {}", e.getMessage());
41+
}
42+
infinite = false;
43+
Thread.currentThread().interrupt();
44+
}
45+
}
46+
});
47+
thread.start();
48+
}
49+
50+
/**
51+
* Eats the porridge.
52+
* @throws InterruptedException due to firing a new Runnable.
53+
*/
54+
public void eat() throws InterruptedException {
55+
requests.put(() -> {
56+
logger.info("{} is eating!",name());
57+
logger.info("{} has finished eating!",name());
58+
});
59+
}
60+
61+
/**
62+
* Roam in the wastelands.
63+
* @throws InterruptedException due to firing a new Runnable.
64+
*/
65+
public void roam() throws InterruptedException {
66+
requests.put(() ->
67+
logger.info("{} has started to roam in the wastelands.",name())
68+
);
69+
}
70+
71+
/**
72+
* Returns the name of the creature.
73+
* @return the name of the creature.
74+
*/
75+
public String name() {
76+
return this.name;
77+
}
78+
79+
/**
80+
* Kills the thread of execution.
81+
* @param status of the thread of execution. 0 == OK, the rest is logging an error.
82+
*/
83+
public void kill(int status) {
84+
this.status = status;
85+
this.thread.interrupt();
86+
}
87+
88+
/**
89+
* Returns the status of the thread of execution.
90+
* @return the status of the thread of execution.
91+
*/
92+
public int getStatus() {
93+
return this.status;
94+
}
95+
}

0 commit comments

Comments
 (0)