Showing posts with label Dependency Injection. Show all posts
Showing posts with label Dependency Injection. Show all posts

Tuesday, June 23, 2009

Robocode modules as in version 1.7

In version 1.7 we managed to split Robocode into several modules, introduce dependency injection using PicoContainer. We as well started using Maven 2 for builds. Now I will try to explain it a bit.
Robocode

Modules Description

  • robocode.api - This is really robocode.jar as you know it.
    • But now it contains only Robot API, everything what's visible to the robot in package robocode.
    • It also contains Control API, which you can use to automate the game, for example RoboRumble does exactly that.
    • Main entry point of the application. It finds robocode.core and calls it very soon after start.
    • There are few interfaces and base classes to allow robocode.core and other modules implement them.
    • It is very important to not add any compile time dependency of robocode.api on any other .jar, because that would cause all robot authors would need to have that dependency as well.
  • robocode.core - The Hub.
    • It loads all robocode modules. Module is any .jar file in libs directory with name starting with "robocode". It finds class Module from package of the same name as the .jar file. For example net.sf.robocode.repository.Module. The class should initialize itself in static constructor. That's module entry-point. Good way how to do it is to register components (classes and instances) into PicoContainer. Or as a singleton or as a factory (transient). There is only single container in whole application. Components could be also registered under name, which is interesting for extensions, example is JavaHost.
    • Because core is referenced by every other module, it serves as place where all interfaces are defined. But most of the implementations are outside of core. This helps us to prevent spaghetti of dependencies.
    • In the future, we may have more extension points, they will be defined by interface which any extension should implement. The interface will be added into core if they are not specific to one of existing modules.
    • Currently we do NOT consider any interfaces public API, so if you want to create extension to Robocode, you should talk to us on our discussion email group first.
  • robocode.battle - The rules and main battle loop.
    • It runs the battle thread, which is driving battle life-cycle. Battle has rounds, turns, and each turn have several steps. See Battle.runTurn()
    • The battle thread also creates robots, by using robocode.host, pauses them between turns, reads their commands and produces events for robots, like ScannedRobotEvent or HitByBulletEvent. It transfers the event from GUI to interactive robots.
    • It also produces all events for IBattleListener, they are produces each turn and we call them snapshots, about position of robots, bullets, etc. They are (99%) immutable structures. They also could contain serialized form of drawing commands from robots and text written by robots.
    • This module would expose extension points when we will implement custom extensible battle rules and battle fields. Watch this space!
  • robocode.host - The security sandbox for robot.
    • Loads robot into separate ClassLoader, so he can't access anything but robocode.api. There is bit of magic to load from nested .jar files for team robots.
    • Java SecurityManager is enabled, so we prevent it from all cheating or attacks to user computer.
    • Disk operation are limited to certain directory and volume of data. Files are extracted from robot's .jar file on demand.
    • There are *RobotProxy classes, which transform robot's commands like setFire() into message, which is send each turn, on execute() call. It also validates them.
  • robocode.repository - Is the dabase of robots installed on the system.
    • Robot classes are loaded and registered when the game spots them in robots directory. Robot property file is also extracted. The metadata are then stored in database, which is serialized into one file. The trick there is that the database is faster to read than to load all the classes and examine them. We do reload the classes or properties, when we found that file timestamp is later than last timestamp stored in database.
    • There is quite lot of logic to figure out the naming and versioning conflicts of robots.
  • robocode.tests - Our unit tests and integration tests.
    • There are different kinds of robots made just for the purpose of testing some feature of Robocode. Some of them try to break rules, attack the security system. Others behave interesting way on battlefield and we measure them, to learn if the game physics is OK.
    • The tests are build with junit, there is base class RobotTestBed which prepares and runs the battle. It implements IBattleListener and we could read all the events produced by game and watch the game by code. During the game we make notes and in tear down phase we evaluate them with asserts.
    • There are some regular unit tests as well, but just few of them.
  • robocode.ui - Windows and BattleView.
    • Windows are mostly boring stuff, but there are several problems. One of them is UI thread, which keeps window responsible during the game. But we need to make sure that the UI thread is not used to call battle components or even robot's code (as in pre 1.6). To do that BattleManager is really queue of commands for battle thread. They are executed on battle thread in each turn. Well it's not that simple, and we still have some troubles when the game is paused.
    • Another problem is how to paint all the events produced by battle. They are regullary dispatched on battle thread to anyone registered in BattleManager.addListener(), but for UI it;s different. Imagine that we could run the battle at 9K TPS (turn per second), you could easily see that it doesn't make any sense to render it with same frame rate (FPS). So we introduced AwtBattleAdaptor, which receives them all and dispatches only selected ones to UI thread. It's a bit complicated, because we could skip lots of onTurnEnded, but we need to preserve the text output from robots. So here it comes, we cache the texts per robot and inject it into those snapshots which are really dispatched. (That's only place where we broke immutability of snapshots, and we do that transparently to the recipients). Also bear in mind that we need to deliver all the events of other types, and to do that in correct order in relation to onTurnEnded. We do not dispatch onTurnStarted as current optimization. Anyone on GUI thread could register in AwtBattleAdaptor.addListener() and should properly unregister during disposal.
  • robocode.ui.editor - Robot editor for real beginners. Personally I think we need to rather create Robocode as plugin to Eclipse, any volunteer ? There is also problem with Jikes, it's ages old and have bugs. But not all people have JDK installed.
  • robocode.roborumble - Download robots, run the battles, upload results. Robocode@Home distributed computing, to get scores. We had issues several releases now, so we were unable to contribute to the grid with recent versions. Hopefully it's over now.
  • robocode.sound - Just sound. Easy stuff.
  • robocode.installer - Our hand-made installer.
  • robocode.content - All other files to be deployed.
  • robocode.samples - Our sample robots.

How to create new module

  • Read the Developers Guide
  • Unzip robocode.dummy.zip and rename all dummy files and all dummy things inside files.
  • Implement Module class as described above.
  • Add your module to main pom.xml
  • Try if you are smart and brave enough to make at least small progress alone. And then definitely TALK to us about your idea.
  • Then we decide where your extension belongs. If it's to be installed with main installer, keep it in root directory. If it should have separate installer, move it to plugins directory and create installer for it. There are several unfinished examples already. Next time about that.
Over and out.

Sunday, April 13, 2008

Module management and Dependecy injection

I'm trying to choose some framework which will help us to manage modules and dependencies after modularization of Robocode.
There are several criteria
  • small
  • fast
  • easy to use
  • low impact on code
  • easy to configure when user adds new module at runtime

I would like to hear your opinion about weight of criteria above.
I have hard time to choose right one, because lot of Java specific names are new to me. Half of articles on the Internet is kind of holy war again, so it is thought to find informed and balanced article.
I will  try to summarize what I've learned about possibilities.

We have three problems
  • 1) how to friendly add .jar plugin to classpath
  • 2) how to manage dependencies
  • 3) how to manage different versions of plugins or their dependencies on same classpath
  • 4) we also need to consider licensing. I'm not an licensing expert, though.

Solution for 3) and also 1)

It is quite difficult problem.
There are some standard solutions to the problem
OSGI
http://felix.apache.org/site/index.html
http://www.eclipse.org/equinox/
http://www.knopflerfish.org/

JPF
http://jpf.sourceforge.net/

I propose to ignore the problem, because solution is too heavy for our needs. Ignoring c) may lead to necessity of recompilation of cascade of plugins when some of them will change heavily. I believe that we could live with that.

Update: I have found concierge, which looks pretty small = 80 KB. I should try to use it.

Solution for 2)

This problem is nicely solved by various Dependecy injection frameworks.

Spring, Spring JavaConfig
+ de facto standard
- too heavy

Guice
+ fast
+ quite nice
+ type safe
* no that big, but still ....
- no external configuration (XML)
- code will become dependent on it because annotations

PicoContainer
+ small
* all wiring is done in code
- no external configuration (XML)

NanoContainer (buid on top of PicoContainer)
+basic xml configuration
+ still very small (176KB)
+ able to solve problem 1)
- lack of documentation

Conclusion

For our use case I like most PicoContainer+NanoContainer.
It solves problem 1) and 2), with small size, with possibility of XML configuration, and our components could be independent on it's namespaces. I tested it on small test project with one core and one plugged jar, and with XML config. I was also able to pass parameter to constructor from XML.

I'm interested to hear other opinions. Thanks!