In large projects, dependencies between modules and code organization can prove a large challenge. This was the reason for creating RRiBbit, a new Open Source Java application framework that eliminates dependencies and simplifies code structure. It is inspired by the Eventbus pattern, but improves upon this by being compatible with existing code and allowing bidirectional communication between components.
In applications with lots of classes, it can be difficult to organize those classes into different components. Even if you succeed, you most likely will need all of them to compile, because different components often have dependencies on one another, either directly or indirectly. There is nothing inherently wrong with that, if you want to integrate different components in a good way, they simply have to talk to one another. In Java, we often "talk to interfaces", which is a good thing! You can easily swap the implementation without the other components being affected. But when requirements and logic change, so do the interfaces, meaning that all components have to be modified again.
Wouldn't it be nice if you could create and compile components completely independently? If components could talk to each other without having compile time dependencies, but at the same time maintaining simplicity and strong typing? And wouldn't it be even nicer when you could achieve this by using your existing Java code, without significant refactoring?
The name RRiBbit comes from "Request-Response-Bus". It's a shiny new Java application framework offering just that. Methods can send requests to the RequestResponseBus and receive responses back, but no-one has to implement any verbose interfaces with "onRequest()" methods or anything like that. Let's dig right into it, with an extremely simple example that demonstrates RRiBbit's capabilities. I will explain more details afterwards.
Let's assume we have an OrderPage, where people can place orders. when someone submits an order, it needs to be registered by the UserService and paid by the PaymentService. The PaymentService asks the MailService to send an email and the MailService asks the UserService for an email address. Hmm, maybe not so simple after all, but let's take a look:
The "talking to interfaces" part is omitted for simplicity, in reality, you will probably have this. If the application stays as simple as this, it's quite manageable, but I think you can imagine an application with a hundred business-logic classes, with thousands of methods and a build time long enough to take a lunch break. In those cases, you want to get rid of your dependencies, so that you can develop and build components independently. Let's see what RRiBbit can do:
public class OrderPage { private PaymentService paymentService; private UserService userService; public void submitOrder() { Integer userId = 1; paymentService.doPayment(userId, amount); userService.registerPayment(userId, amount); } } public class PaymentService { private MailService mailService; //Do payment... mailService.sendPaymentEmail(userId, amount); } } public class UserService { return "foo@bar.com"; } //Register payment in database... } } public class MailService { private UserService userService; //Send email... } }
Basically, every method call has been replaced by a call to the RequestResponseBus. The send() method takes a String as the first parameter, which corresponds to the hint of the appropriate Listener and a variable number of Objects as parameters to the corresponding method. Getting a response back (the email address) is no problem. It is generically typed and allows the classes to focus on their tasks without having to worry about the rest of the application. We have total decoupling, no class depends on one of the others anymore and we can easily swap one Listener for another and as you can see, we are using our existing methods! The RequestResponseBus has many more methods for sending requests, which we will discuss later.
public class OrderPage { private RequestResponseBus rrb; public void submitOrder() { Integer userId = 1; rrb.send("doPayment", userId, amount); rrb.send("registerPayment", userId, amount); } } public class PaymentService { private RequestResponseBus rrb; @Listener(hint="doPayment") //Do payment... rrb.send("sendPaymentEmail", userId, amount); } } public class UserService { @Listener(hint="getUserEmail") return "foo@bar.com"; } @Listener(hint="registerPayment") //Register payment in database... } } public class MailService { private RequestResponseBus rrb; @Listener(hint="sendPaymentEmail") //Send email... } }
We will first take the example one step further! When you send a request to the RRB, it will execute all Listeners that match that request, so you can call multiple Listeners in one call:
Look at the code now! The OrderPage only has to call "processPayment" and that's it! The services too, can just focus on their own problems, without having to look to the others. High cohesion, low coupling. When multiple Listeners match, the execution order is unspecified, so if that is a problem, you have to use two separate calls. RRiBbit does support concurrent execution of Listeners, so if the execution order does not matter, you get multi-threaded performance for free!
public class OrderPage { private RequestResponseBus rrb; public void submitOrder() { Integer userId = 1; rrb.send("processPayment", userId, amount); } } public class PaymentService { @Listener(hint="processPayment") //Do payment... } } public class UserService { @Listener(hint="getUserEmail") return "foo@bar.com"; } @Listener(hint="processPayment") //Register payment in database... } } public class MailService { private RequestResponseBus rrb; @Listener(hint="processPayment") //Send email... } }
So, what other methods does the RequestResponseBus have? I'll discuss them now shortly. For more comprehensive documentation, please refer to the Javadocs.
- <T> T sendForSingleOfClass(Class<T> returnType, Object... parameters);
With this method, you can send a request to the RequestResponseBus and get exactly one Object back as a response. All Listeners that have the specified returnType and match the given parameters will get invoked. When multiple Listeners match the request, only one return value is returned. It's unspecified which one. - <T> Collection<T> sendForMultipleOfClass(Class<T> returnType, Object... parameters);
Same as above, but now you get a Collection of all return values of all invoked Listeners back. - void sendForNothing(Object... parameters);
All Listeners that match the given parameters will get invoked and you get nothing back. - <T> T sendForSingleWithHint(String hint, Object... parameters);
All Listeners that match the given hint (specified in the Listener annotation) and the given parameters will get invoked, you get one return value back. - <T> Collection<T> sendForMultipleWithHint(String hint, Object... parameters);
Same as above, but now you get a Collection of all return values of all invoked Listeners back.
- void sendForNothingWithHint(String hint, Object... parameters);
- <T> T sendForSingleOfClassWithHint(Class<T> returnType, String hint, Object... parameters);
- <T> Collection<T> sendForMultipleOfClassWithHint(Class<T> returnType, String hint, Object... parameters);
- <T> T send(String hint, Object... parameters);
This one is the one used in the above code. It is required to do the exact same thing as sendForSingleWithHint, meaning that it's just a convenience method. sendForSingleWithHint was chosen, because it's probably the most widely used one.
So, how about exception handling? Simple. If one of the executed Listeners throws a Throwable, then that Throwable is thrown as is. If more than one Listener throws anything, then a MultipleThrowablesOccurredException is thrown, containing all thrown Throwables.
We're almost there! I will now discuss some classes that you need to make all this work! Don't worry, it's really simple and 3 lines of code will get you on your way with RRiBbit!
Lets start with the ListenerObjectCreationStrategy, this is the object that creates the listeners. Listeners are represented by so-called ListenerObjects, that contain everything they need to be able to handle requests, such as:
- Their return type
- The java.lang.reflect.Method object that needs to be run
- A target Object to run that Method on
- (Optional) The hint String from the Listener annotation
Once the Collection of ListenerObjects is created, RRiBbit must be able to search it for matches when requests are being made. This is done by an implementation of ListenerObjectRetrievalStrategy. There is currently only one of them, the DefaultListenerObjectRetrievalStrategy, that simply inspects all of them and returns the ones that match. I know that this is not very efficient, but RRiBbit will be extended by more implementations that provide more efficient algorithms, promise!
And finally, RRiBbit needs to actually execute the ListenerObjects upon a request. This is done by by an implementation of ListenerObjectExecutionStrategy. Currently, there are two of them. One performs execution of a set of ListenerObjects in sequential (although unspecified) order, whereas the other one does it concurrently. The latter one is recommended when running on multi-core hardware.
Of course, you could create your own implementation of each of these interfaces, but to use RRiBbit, you simply have to specify which one you want to use, by creating an instance and passing it to the RequestResponseBus implementation. Simple as that! RRiBbit even has a class called RRB, that provides static, global access to a RequestResponseBus instance, so that you don't have to pass it around everywhere. You can then simply use RRB.get() from anywhere in your code to pass a request to the bus.
And that's about it. In the future, RRiBbit will be extended with more functionality, such as:
- Unit test tools. Currently, RRiBbit already simplifies unit testing, since it's really easy to create dummy Listeners for test code that replace real Listeners, so that the classes that make requests, can be tested in isolation, but further tooling on this will improve this even more.
- An eclipse plugin. One downside of RRiBbit is that your IDE will no longer be able to tell you what code exactly will run when you make a request, as opposed to calling an old fashioned method. An eclipse plugin will bring back this handy trick.
- As I mentioned earlier, a more efficient implementation of the ListenerObjectRetrievalStrategy will be provided as well.
- And even more things are also on the way. I won't discuss them all, because I want to keep some surprises for the future :-)
Update: rribbit.org is now online! Please go to this site, because there might be versions newer than 1.0.0 there.
- rribbit-1.0.0.jar
- rribbit-1.0.0.pom
- rribbit-1.0.0-javadoc.jar
- rribbit-1.0.0-sources.jar
- rribbit-1.0.0-test-sources.jar
Back to blog-index
I really like this idea! Are you planning any remote or distributed type features in the future?
Isn’t that the same as http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#context-functionality-events
And IMHO, always using events like this will likely end in a greater mess than traditionnal layered softwares.
For example, A send event to B who send event to A => undetected circular dependency which is often a code smell….
Basically, what is wrong in your example is that MailService should be at a higher level :
User user = userService.read(userId);
paymentService.doPayment(user, amount);
mailService.sendPaymentMail(user, amount);
userService.registerPayment(user, amount);
Or if you use the spring solution :
paymentService.sendEvent(new PaymentEvent(this, user, amount)));
PaymentMailService implements Listener {
public void onEvent(PaymentEvent e) {
mailService.sendPaymentMail(e.getUSer(), amount)
]
}
your comment system is messing up with < and gt;
you likely have XSS somewhere.
alert(‘xss’)
Hello Curtis and Chris!
Thanks for your comments!
@Curtis: I hadn’t thought of this yet, but it could be an interesting idea! Do you have any more specific suggestions?
@Chris: Yes, there are more EventBus based frameworks, but in all of them (including Spring), you have to implement Listener interfaces with onEvent or onRequest methods. Also, the listeners can’t send anything back to the sender. With RRiBbit, you can use existing business methods and listeners can respond as well as receive! RRiBbit also supports multi-threading for concurrent execution of listeners.
I know that the example would probably not exist in the real world in this form, but it’s just an example :-) It symbolizes an application with dozens of classes and hundreds (if not thousands) of methods. Even when neatly layered, they still have compile time dependencies and therefore make it difficult to modularize the application.
As far as the XSS vulnerability is concerned: This entire website is built with WordPress, so I think it’s safe to assume that they have their stuff sorted out :-)
Ok for the xss but indeed the html tags are swallowed (i.e generics in java are swallowed) ;)
Concerning the need to return something from an event and “even when neatly layered, they still have compile time dependencies and therefore make it difficult to modularize the application.”, I think this is wrong because you can easily use an adapter pattern.
public class PaymentService {
public static interface PaymentMail {
public void send(String userId, int amount);
}
private PaymentMail mail;
mail.send(userId, amount);
then you give a fake for the tests and in real world :
public class PaymentMailAdapter implmements PaymentMail {
UserService userService;
MilService mailService;
/*ctor omitted to init both private fields*/
public void send(String userId, int amount) {
mailService.send(userService.readEmail(userId), …)
}
}
BTW, my point is that events hurts more in the long run compared to traditionnal layered software.
There are cases where events are good (i.e observer pattern, i.e the sender does not care about listeners). For example, GUI is a good fit for events.
But if all you logic is tied by events, you lose the mental model of the business users (and developpers) and you are lost in communication problem.
In the example, the user thinks “make the payment, register it in the system and send a mail” (which is exactly what my code express) :
paymentService.doPayment(user, amount);
mailService.sendPaymentMail(user, amount);
userService.registerPayment(user, amount);
even with thousands classes you can keep it that way
But isn’t this code with the adapter much more verbose than using RRiBbit? And you may no longer have test-time dependencies, but you still have compile time dependencies.
About “having the overview and the mental model”: I think that the point of low coupling and high cohesion is, that you don’t necessarily need a complete overview anymore, that each component can focus on its own problems without worrying about the rest. When working with lots of people on a large project, it’s almost impossible to have the overview anyway. It’s a matter of opinion, I guess :-) But I do get your point and that’s why I will work on an eclipse plugin in the future, to make the “call hierarchy” visible again.
how testable is the code written with this framework going to be?
Hello elron, thanks for your comment!
As for the answer, I would say “very testable”! When it comes to RRiBbit code, you have methods that call listeners and methods that respond to calls (and methods that are both).
A method that calls listeners can very easily be tested by making sure that the RequestResponseBus in the unit test has appropriate listeners listening to the requests of the method. These can be dummy / mock listeners that return predefined values, that are necessary for the tests. Basically, you can use RRiBbit to mock a method’s dependencies very easily, so that you can test that method in isolation. In future releases, RRiBbit will provide additional tooling to make this even easier.
A method that responds to requests (meaning it’s annotated with @Listener) can be tested like any other method. You don’t have to use RRiBbit to test it, it’s just another method. The method’s behaviour when called through RRiBbit will be identical to plain method calls.
If you want to test an entire set of classes (and make sure that they all work together and perform the correct requests to RRiBbit), you can just setup the RequestResponseBus and the listeners identically to how it’s setup in a real environment (maybe mocking the DB or other external systems). Then, instead of the view layer making the requests to the RequestResponseBus, it will be your unit tests.