Factory Pattern and Dependency Injection
While writing my listener, I needed to instantiate a new handler upon receiving a request. Originally, I would simply insatiate a new handler object and spawn it up in a new thread to handle the given request. This was working fine until my tests started to get too large. Anytime I tested the listener, I was essentially testing the entire system. While testing I had no control over what happened inside the listener. So, in order to make my tests more focused, I decided to implement a handler factory. Now, I simply pass in a factory to my listener and the listener calls the factory when it needs to instantiate a new handler. This is great because I can now pass in a mock factory that returns mock handlers. This allows me to test just the listener and not the handlers at the same time. Along the same reason as using the factory pattern, I have found the Dependency Injection has made my test 100% more testable.
Hand Rolled Mocks
As per the requirement of the task, I was not allowed to use any outside libraries other than JUnit and the Java Standard Library. Even so, I have found it very helpful to not have a mocking framework to mess around with. I've found that hand rolling mocks is significantly simpler than rolling mocks via a framework. While they may not be as powerful out of the box, they take a lot less time to set up and are easy to add whatever functionality you need. The only problem I have had with hand rolled mocks is keeping their function signatures in sync with their parent. However, I suppose if I had used the proper refactoring tools of IntelliJ it wouldn't have been a problem.
My biggest hang ups so far have been Java related, Logging and Shutdown events. Logging in Java is uncharacteristically complex. You are unable to instantiate a logger and use it. Loggers are singletons, and they are assigned by name. So to get a logger you have to call the static method getLogger(String name) with the name of the Logger you would like to retrieve. In addition each logger can have multiple handlers, which allows writing logs to any number of places (console, file, memory, etc.). This is nice, but I was not aware that there is a default logger that writes to the console. So when I attempted to add another handler that wrote the console, I was receiving lots of output. So, in the future, I will be sure to either disable the default handler or use it appropriately.
Shutdown events were another headache. In Java, you cannot simply intercept a SIGINT as with other languages. I needed to intercept the SIGINT in order to shutdown the server properly before exit. The only was to do this in Java is to add a Shutdown event handler. So, I added a handler and some logging statements within the shutdown handler to notify me if it was running. However, the shutdown handler was very sporadic, sometimes it would run and sometimes it would not. However, after a lot of debugging, I discover that the handler was being run every time but only the log messages where not being run every time. This is because every shutdown event handler runs concurrently. In another thread, the logger was being closed. So, sometimes I would log a message before it would close and sometimes it would close first.
All in all, programming the server in Java has been fun, albeit a challenge at some points.