Guava Event Bus

Alican Akkuş bio photo By Alican Akkuş Comment

Merhaba arkadaslar, Bu yazıda Google Guava kütüphanesinde bulunan bir yapıyı aktarmaya çalışacağım. Anlatmak istedğim mantık Event Bus mantığı, implementasyonunu ise Google Guava ile yapıyor olacağız.

Yazının ana konusu Event Bus olacaktır. Event bus, temel de publish/subscribe mantığı ile çalışan event based bir yaklaşımdır. Siz herhnagi birşeye subscribe olursunuz ve işinize devam edersiniz. Subscribe olunan(publisher) kısım ise açık olarak kendisini subscribe olanlara(subscriber) event yollamaz. Benim elimde şöyle bir event var deyip ortalığa atar. Event’ı herhangi bir subsciber’ın alıp almadığı ile ilgilenmez. Bu mantığı birçok yerde görebiliriz, JMS(MQ olaylarında), Reactive programlama konularında başrol oynayan bir kavramdır.

Örneğin JMS ve türevlerinde topic mantığı vardır. Bir topic’e subscribe olursunuz ve yolunuza devam edersiniz. İlgili topic’de herhangi bir aksiyon olursa bundan event ile haberdar edilirsiniz. Veya diğer bir örnek olarak reactive programlama yapısında da event’lere dayalı bir akış söz konusudur.

Yukarıda kısmen belirttiğim gibi bu mantığın asıl avantajı farklı component’lerin açık olarak birbirine register olmadan aralarında publish/subscribe mantığıyla çalışabilmelerini sağlamaktır. Bu sayede farklı componentler arası bağ azalmış olacaktır, loose coupling. Burada publish eden ile subscribe olan yapılar birbirinden bağımsızdır ve biribinden habersizdir. Event’i(mesajı) yollayan publisher, karşı tarafda mesajı alacak olan bir(subsciber) var mı bilinmez. İki tarafda birbirinden habersizdir.Bunların temelinde Observer pattern yatar. Bu bize daha esnek, bağların az(decoupling), bakımı ve takibi kolay yazılımlar geliştirmeyi sağlar.

Google guava içeriisnde event bus yapısını barındıran publish/subscribe mantığında mesajlaşmayı destekleyen bir kütüphanedir. Örnek bir uygulama ile bakalım; Maven projesi oluşturup guava lib’ini ekleyelim.

SimpleEventListener.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.caysever.eventlistener;

import com.google.common.eventbus.Subscribe;

public class SimpleEventListener {

	@Subscribe
	public void todoTask(String newTask){
		System.out.println(this.getClass().getName() + " new todo task received. Task : " + newTask);
	}

	@Subscribe
	public void jobTask(String newTask){
		System.out.println(this.getClass().getName() + " new job task received. Task : " + newTask);
	}

}

Test; SimpleEventListenerTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.caysever.eventlistener;

import org.junit.Before;
import org.junit.Test;

import com.google.common.eventbus.EventBus;

public class SimpleEventListenerTest {

	EventBus eventBus;//create event dispatcher
	SimpleEventListener eventListener;//event listener

	@Before
	public void initialize() {
		eventBus = new EventBus();
		eventListener = new SimpleEventListener();

		eventBus.register(eventListener);//register to event dispatcher
	}

	@Test
	public void eventListenerTest() throws InterruptedException {

		int i = 0;
		while (i < 10) {
			eventBus.post("New task created. Task no : " + i);
			i++;

			Thread.sleep(1000);
		}
    eventBus.unregister(eventListener);//unregister olalım.
	}
}

Yukarıda SimpleEventListener içerisinde @Subscribe ile işaretledğimiz kısımlar alıcılardır. Event handler methodların(alıcı methodlar) tek parametre alması, public access modifiers’a sahip olması ve non-return(void) olması gerekiyor. Parametre, mesaj veya event type olabilir. Date, String, Number, Object vs olabilir.

Yukarıdaki test’in çıktısı şöyledir;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 0
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 0
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 1
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 1
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 2
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 2
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 3
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 3
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 4
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 4
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 5
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 5
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 6
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 6
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 7
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 7
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 8
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 8
com.caysever.eventlistener.SimpleEventListener new job task received. Task : New task created. Task no : 9
com.caysever.eventlistener.SimpleEventListener new todo task received. Task : New task created. Task no : 9

Post ettigimiz event type iki subscriber için de match olduğu için iki alıcıya da mesaj gitmiş olacak. Dikkat ederseniz pusblisher olan kısım, subscriber olan kısımları bilmez, var mı yok mu ilgilenmez. Mesajı/Eventı post eder ve gerisine karışmaz.

Şimdi ise complex bir event type işleyelim. Aşağıdaki kod parçalarını gerekli yere ekleyelim;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.caysever.model;

import java.util.Date;

import com.caysever.utils.DateUtils;

public class Todo {
	String todoName;
	String todoDescription;
	String todoAssignedTo;
	Date todoDueDate;

	public String getTodoName() {
		return todoName;
	}
	public String getTodoDescription() {
		return todoDescription;
	}
	public String getTodoAssignedTo() {
		return todoAssignedTo;
	}
	public Date getTodoDueDate() {
		return todoDueDate;
	}
	public void setTodoName(String todoName) {
		this.todoName = todoName;
	}
	public void setTodoDescription(String todoDescription) {
		this.todoDescription = todoDescription;
	}
	public void setTodoAssignedTo(String todoAssignedTo) {
		this.todoAssignedTo = todoAssignedTo;
	}
	public void setTodoDueDate(Date todoDueDate) {
		this.todoDueDate = todoDueDate;
	}
	@Override
	public String toString() {
		return "Todo [todoName=" + todoName + ", todoDescription=" + todoDescription + ", todoAssignedTo=" + todoAssignedTo + ", todoDueDate=" + DateUtils.getFormattedDate(todoDueDate)
				+ "]";
	}
}

@Subscribe
public void todoTask(Todo todo){
	System.out.println(this.getClass().getName() + " received new todo. Todo details;\n" + todo);
}

@Test
public void complexEventListenerTest() throws InterruptedException {

	int i = 0;
	while (i < 10) {
		Todo todo = new Todo();
		todo.setTodoAssignedTo("çaysever");
		todo.setTodoDescription("some codes required refactoring");
		todo.setTodoName("refactoring");
		todo.setTodoDueDate(new Date());
		eventBus.post(todo);

		Thread.sleep(1000);
		i++;
	}
  eventBus.unregister(eventListener);//unregister olalım.
}

Çıktı şu şekilde olacaktır;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:26:54]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:26:55]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:26:56]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:26:57]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:26:58]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:26:59]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:27:00]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:27:01]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:27:02]
com.caysever.eventlistener.SimpleEventListener received new todo. Todo details;
Todo [todoName=refactoring, todoDescription=some codes required refactoring, todoAssignedTo=çaysever, todoDueDate=30 Oct 2016 21:27:03]

Peki publisher olarak birşey post ettik ama bu event çöpe gitti. Yani herhangi bir subscriber register olmamıs durumda. Bunun için DeadEvent kullancağız. DeadEvent, herhangi bir publish de hiç subscriber olmadığu durumlarda çalışan normal bir event listener’dır.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.caysever.eventlistener;

import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.Subscribe;

public class DeadEventListener {
	boolean notDelivered = false;

	@Subscribehttps://github.com/AlicanAkkus/event-bus
	public void listen(DeadEvent event) {
		notDelivered = true;
		System.err.println("no one was there to receive. Event : " + event.getEvent().toString());
	}

	public boolean isNotDelivered() {
		return notDelivered;
	}
}

Test class’ımızı şu şekilde degistirelim;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.caysever.eventlistener;

import java.util.Date;

import org.junit.Before;
import org.junit.Test;

import com.caysever.model.Todo;
import com.google.common.eventbus.EventBus;

public class SimpleEventListenerTest {

	EventBus eventBus;
	SimpleEventListener eventListener;
	DeadEventListener deadEventListener;

	@Before
	public void initialize() {
		eventBus = new EventBus();
		eventListener = new SimpleEventListener();
		deadEventListener = new DeadEventListener();

		eventBus.register(deadEventListener);
		//eventBus.register(eventListener);
	}

	@Test
	public void eventListenerTest() throws InterruptedException {

		int i = 0;
		while (i < 10) {
			eventBus.post("New task created. Task no : " + i);
			i++;

			Thread.sleep(1000);
		}

		eventBus.unregister(eventListener);
	}
	@Test
	public void complexEventListenerTest() throws InterruptedException {

		int i = 0;
		while (i < 10) {
			Todo todo = new Todo();
			todo.setTodoAssignedTo("çaysever");
			todo.setTodoDescription("some codes required refactoring");
			todo.setTodoName("refactoring");
			todo.setTodoDueDate(new Date());
			eventBus.post(todo);

			Thread.sleep(1000);
			i++;
		}
	}

	//@Test
	public void deadEventListenerTest() throws InterruptedException {
		int i = 0;
		while (i < 10) {
			eventBus.post("New task created. Task no : " + i);
			i++;

			Thread.sleep(1000);
		}
	}

}

Test ettiğimizde ise aşağıdaki çıktıyı görebiliriz;

1
2
3
4
5
6
7
8
9
10
11
12
no one was there to receive. Event : New task created. Task no : 0
no one was there to receive. Event : New task created. Task no : 1
no one was there to receive. Event : New task created. Task no : 2
no one was there to receive. Event : New task created. Task no : 3
no one was there to receive. Event : New task created. Task no : 4
no one was there to receive. Event : New task created. Task no : 5
no one was there to receive. Event : New task created. Task no : 6
no one was there to receive. Event : New task created. Task no : 7
no one was there to receive. Event : New task created. Task no : 8
....
....
....

Örnek projeye şuradan erişebilirsiniz.

Yazıyı burada sonlandırıyorum arkadaslar, hoşçakalın. Bol reactive’li günler :)

A.Akkus

comments powered by Disqus