Scala Akka Cheatsheet

Featured

A quick wrap-up from the hello-akka example of Typesafe Inc. (http://typesafe.com/activator/template/hello-akka) so nobody has to weed thru the docs to remember the syntax after being over to the Java/C++ world for a while.

One Actor is no Actor. Actors come in systems.

Carl Hewitt , inventor of the Actor Model

Agenda

  • Create Message classes that are exchanged between Actors.
  • Define an Actor by defining a receive routine.
  • Create an ActorSystem and ask it to create an Actor for us
  • Send messages to the Actor and examine the response with the help of the Inbox.

Define messages as case objects and classes

To remember:

  • Messages must be immutable and should be Serializable (for remoting).
  • Scala’s case classes come in handy because the compiler does a lot of plumbing (http://www.scala-lang.org/old/node/107)
case object Greet
case class WhoToGreet(who: String)
case class Greeting(message: String)

Define an Actor

  • An actor usually has state.
  • Inherits from Actor base class.

Basic Pattern

def receive = { case M1 => ...; case M2 => ... }
class Greeter extends Actor {
 var greeting = ""

 def receive = {
   // Sophisticated Actors will usually use pattern matching
   case WhoToGreet(who) => greeting = s"hello, $who"
   case Greet           => sender ! Greeting(greeting)
  }
 }

Create infrastructure

  • Construct an ActorSystem using the companion Object.
  • Give the ActorSystem a name.
val system = ActorSystem("helloakka")

Create an instance of your Actor

  • Use the ActorSystem’s  actorOf  method, passing Props[Greeter] and an identifying name. Greeter is the type of our Actor.
val greeter = system.actorOf(Props[Greeter], "greeter")

Send a message

  • Use the exclamation mark method (or tell) and pass a message object that is covered by the actors case statement.
  • I tend to prefer a more explicit notation.
greeter ! WhoToGreet("akka")
greeter tell WhoToGreet("akka")
greeter.tell(WhoToGreet("akka"))
  • Akka uses Scala’s implicit parameter feature (parameter derived from context) to pass the sender’s ActorRef.
  • Use the “sender” reference provided by the Actor base class to reply with a message.

Test the Actor

Inbox is a class that is kind of a non-responding Actor that is handy to trigger an Actor (that again triggers other Actors) and collect its responses.

val inbox = Inbox.create(system)
greeter tell WhoToGreet("akka")
inbox.send(greeter, Greet)
val Greeting(message) = inbox.receive(5.seconds)

assert(message == "akka")

Thoughts on Automated Testing

Depending on the complexity of the logic it may come in handy to treat the Actor as an integration component without business logic and create separate classes that do business logic. Decoding messages and responding to them seems enough responsibility for one class. Real world receive routines quickly become convoluted or the actor class gets too many method.

Object-Oriented Software Constructions and Actor Systems

You will find that the style from Object-Oriented Software Construction is an excellent basis for writing actor systems. Controlling side-effects is essential to not violating the Actor Models rules.

Note how Actors must always be in a stable state before and after they handle an incoming message. A well-constructed real-world Actor will have explicit preconditions, postconditions and invariants. In the end it is a managed, stateful object.

Disclaimer

All example code and wisdom was adapted from the hello-akka example created by Typesafe Inc. and is not my original work.

Advertisements

“Is TDD dead?” – No, it’s still the Future (But Ruby-on-Rails is dead.)

Here’s some thoughts on Google+ hangout series on Is TDD dead? by David Heinemeier Hannson (Ruby on Rails), Martin Fowler (do I have to list anything?) and Kent Beck (author of the book TDD by example). I’d like to provide a short summary of their opinions and discuss the key points in regard to other experiences and some empirical data.

For those who are on a short coffee break: Of course TDD is not dead, Heinemeier Hanson just likes to code like in the good’ol days of wizard programming ;-).

Short Summary of Beck, Heinermeier Hannson and Fowlers points

Kent Beck, author of TDD by example, obviously propagates TDD. He explains how it fits his way of tackling complex problems and how it helps to overcome anxiety when facing complicated tasks. A developer is allowed to have confidence that his code will work and he claims TDD provides just that. The question of too much tests and test to production code ratio is interesting, as he explains why the ratio depends on the type of project and basically on the amount of coupling between components. Beck points out that deleting tests is not only valid but also necessary. This is also already mentioned in his book.

The corner stone of TDD is that breaking problems into smaller problems always yields a next step, some achievable task at hand. Chop up a hard task until it’s easy to write a test, come up with a solution, then refactor to have a SOLID foundation for the next layer. (OK, I made the SOLID up myself 😉 ).

Fun quote in response to Heinemeier Hannsons claim that unit-testing always leads to bad, layer-bloated design (Martin Fowler laughing really hard):

David, you know a lot more about driving a car than me. But here in Oregon, if you get out of your car and you are some place that you don’t wanna be, getting a new car is not gonna fix that.

I have to remember that one ;-).

David Heinemeier Hansen states with firm conviction that TDD does not fit his way of work – he does simply not like TDD. He claims that code written by TDD is not better than conventionally tested code, often even worse because of the use of Mocks. He presents a bad example of code, probably derived by TDD. That’s where Beck responds with the fun quote. In the last episode he argues that because it is so difficult to scientifically prove the superiority of one approach over another we shouldn’t even try.

He points that he once tried TDD but when it came to web programming he found it just didn’t work for him. He firmly relies on the regression test suite, though. To summarize, he rejects Unit-Testing more or less as a whole which includes TDD.

Martin Fowler points out that he likes to have self-testing code that can be shown to work with the press of a button. He doesn’t mind where it comes from, which does include TDD. He says that getting the right amount of test coverage for a person individually and a team is a calibration process – you have to find the patterns where you screw up and write a test for them next time. That may lead to an overshoot when developers tend cover each feature. He also points out that it is always valid to reject practices that do not fit and one should not apply practices without reflecting on them – self-evident but still important.

On personalities

I must admit that I find it extremely hard to listen to Heinemeier Hannson. His way of argumentation is very subjective without leaving too much room for other opinions. Every statement – or should I call it rant? – serves as a kind of (lame) excuse for his way of work which he claims is as just as good or even superior to other approaches. It sometimes feels like Fowler and Beck are discussing with a Creationist about Evolution. He is probably very convincing to those who do not have a (computer) science background and where anxiety of leaving the good’ol unstructured trial and error process behind for something unknown might cause one to fail. I hope that my discomfort with the personality does not influence the conclusions too much.

A matter of discipline

Going with Heinemeiner Hannson, from what I understood is, if the problem is too hard, when it’s to boring to figure out the details, it is all right to go with trial-and-error. Yes, doing HTTP/HTML/Database stuff can be boring some time and you want quick results. But with an approach like TDD finding the right abstractions can be both fun while still being disciplined engineering. Heinemeier Hannson states that the ActiveRecord pattern cannot be well unit-tested in isolation. But bad testability is just an indicator that ActiveRecord is probably just an anti-pattern.

Heinemeier Hannson claims that TDD leads to too many tests (“overtesting”) which makes it hard to refactor. I’ve seen this happen without using TDD going to extremes, so, again this is not related to TDD but related to coverage requirements (“more than 80% unit-test coverage”).  Some CI Test-Coverage Plugins require the default public no-arg constructor in a class full of static helper methods to be untested. A class of helper methods should trigger an architecture alert by itself, but to get 100% test coverage you can be sure that some developer is writing a test to make the warning go away. Or he creates a private no-arg constructor. I don’t know what’s worse.

He also falls for a common misunderstanding. Having good black box tests does not make white box tests irrelevant. I don’t mean those beginner’s white box unit-tests where they peek around in objects using reflection to do state verification, but a minimal setup that puts the tested component in the center of attention.

Imagine the development of a car. Even if you test a car under all circumstances in the real world this does not eliminate the need to test the engine, the gear and the electronics separately (and the subcomponents they are made of). After that you will want to test various combinations of  integration setups. Even if there were only one type of engine and gear, it would simply be a waste of resources to only to high-level black box testing because most problems would have shown earlier in a much cheaper test. Additionally, testing error cases or distater recovery is often just not possible without a special setup. Wasting high-end gears to test your high-end engines in a high-end chassis on a desert race-track is nothing you want to do on a regular basis, if only because it takes too long.

Maintaining the developer test suite(s) is as much as hard a problem as maintaining the production code. It may be viable to even have a separate role for that, something like a Test-Code Manager or Archtitect. I find myself sometimes in a situation where I discover that somebody else already wrote a test that I could have extended or adapted and refactored. It was just in an unexpected class, had a misleading name or I was just too lazy to search for it. In general I found, test code quality is often way below production code quality. In part this is because writing tests in some circumstances (like web applications) is more a chore than the fun part. You would have to refactor big parts of the application or even drop a bad framework to make the test look good. Hardly anybody does that.

Where you would reject a 50 line production code method, developers easily get away with a 50 line @Test method that mainly replicates the fixture and verification logic from the previous @Test method. By writing tests first, test quality usually also improves as it isn’t done in a hurry to meet some metrics. When tests are considered first class citizens developers tend to take greater care to make them as good as they can.

Empirical background on TDD

The software industry lives off its unproved best practices. That’s why Heinemeier Hansen can claim that TDD is dead – there is simply little empirical evidence if TDD is better. But there are some studies that at least are close enough to back up the benefits. Continuous Testing, that is, automatically executing unit-tests after each change in the IDE was actually empirically studied, some background information can be found here. Reducing the time between introducing an error or misconception and fixing it seems fundamental to me. As TDD per se can speed up the cycle from minutes (or hours) to a few seconds it is valid to assume that TDD done right is an improvement over test-last approaches.

Bridging the gap between exploration and engineering

Sometimes is the need to explore systems,  libraries and frameworks, one answer is already in TDD by example: In the chapter Red Bar Patterns there is the Learning Test. Write a test for externally produced software. This not the only way. Visualization by writing a simple UI is good start. But to verify this knowledge, just create a test.

As an example, setting the read-only flag using the DOS view on a folder in Windows does not make the folder unwritable. But removing the permission WRITE_DATA for the current user or group does. Here’s where TDD kicks in – state what you expect – but don’t expect that what you state is necessarily correct.

Conclusions

There are certainly some points from the 3hrs+ videos that I dropped or missed out. The talk clearly showed to me that TDD is not dead. Given a focus on quality and longevity of a software product it is more alive than ever. It delivers excellent results in the engineering categories of correctness, robustness, maintainability and extensibility. It is a natural barrier towards rushing a system into production that is just not well designed. In the aircraft and space industry there is a saying:

We do not build anything until we know how to test it.

If you ever worked in the industry you know they mean every single unit and every little component down to a screw.

Classical engineering is also one of the main sources where software engineers can gather knowledge from. There’s 150+ years of experience, from building cars, to power plants to space ships. If you are a serious software engineer you will want to benefit from this experience.

Why do we write tests in the first place?

We want our software to be a delight for the customer. It deploy without error, it should start without an error we could have prevented and it should help the customer doing what he wants it to do. If there is some undesired situation, like disk full, low memory or an unreachable server, we want to point out as precisely as possible to the stakeholders that are interested what happened at an appropriate level of detail. And obviously, stack overflows, off-by-one errors or null references should not occur in a final product. Because we designed it’s contract carefully and tested it. Designing the contract becomes much easier with TDD because for a short time we can change our perspective from Provider to Client.

Some approaches on the other hand, like the one Heinemeier Hansen describes as his own, are hardly engineering. Resisting a disciplined process because it’s not fun (or it shows your design is no good) sounds a bit like handicraft work to me. You simply cannot rely on it. You cannot promise a customer that it takes 12 person days to implement a feature when there was some self-proclaimed code-wizard at work before.

The major personal driver of spreading TDD for me is that my work gets easier when I can rely on results yielded by a disciplined apporach. It happened and still happens too often that I have to work with code that is just not finished, there are some alibi unit-tests but they only cover the happy-path and a few obvious error cases. Much more often than not, I run into the untested or bad designed parts. I don’t like that. It’s not fun. You might have had fun developing that code. I have to clean up the mess. I have to go to our customer and break our promises. Who wants that?

That’s why TDD is not dead, but is still the Future. It’s alife and kickin’. On the other hand, despite it’s undisputed impact on web development in genral, Ruby-on-Rails is dead. It showed that it does not scale, is hard-to-test, slow and hard-to-maintain. I call it the Deus-Ex-Machina or “Where the hell does does that method come from?” problem. Ironically, with TDD, the design of Ruby-on-Rails might have been up to the challenge.

 

 

 

Build Automation Software is still just Software

Whenever you move out of your cozy Java (-Language)/Eclipse/Maven comfort zone you recognize there is a whole plentitude of build systems coming with different environments. While you would probably associate the classic Java language with ant or Maven (or even 5000+ lines of BASH scripts written a hundred years ago), the polyglot programmer must be aware of:

  • Scala and SBT
  • Groovy and Gradle (becoming more and more popular in Java)
  • Clojure and Leiningen
  • (J)Ruby and Rake

Leaving the JVM community, there’s still C/C++ with Make (with the beloved  automake, autoconf & Co.), NANT for .NET and certainly a lot more.

How languages rub off on their build tools

There seems to be a strong need to leverage the feel and the qualities of the production language to the build system. The bad properties get inherited, too:

  • Ant and Maven rely heavily on XML (remember EJB 2.x?) and I remember that internally, domain values  are mostly Strings.
  • Non-trivial Gradle scripts lead to these “Oh, there is a coniditionally dynamically added method that makes just the release artifacts for the customers fail.”
  • SBT forces you to leave blank lines – wonder when tabbed punchcards come back.
  • Leiningen invents yet another syntax for GAV coordinates.

Business risks of 3rd party tools

Especially one-man shows and small companies should think about the impact of having yet another third-party tool that needs updates twice a year:

  • Maven Plugins have defects fixed in features that you don’t actually use and defects introduced in features your business relies on.
  • The Maven Release Plugin is still not the right way to do continuous delivery.
  • Testing of Maven plugins was cumbersome and still is no fun.
  • Using String ${properties} and not objects that provide the proper level of abstraction remains the source of many hacks in build scripts. javaCompilerConfiguration.invokeJavaC(buildChain, context) is quite different from setting untyped properties for a Maven plugin, right?

Why you might want to roll your own Build Automation System

Many people still think that programming in a general purpose language and programming by configuring a model are basically two different things. They are not. Anneke Kleppe introduced the term Mogram, which is a portmanteau of Model and Program. Think of JSON, which is data as a program. You are not configuring Maven or Gradle, you are programming them.

The problem with, say, a Maven pom.xml is, that it is hard to test its abstract behavior. Does it generate all the artifacts with the content we expect? While the plugin might be correct, you might have missed a configuration parameter or set up a wrong (or no) base path. I am always afraid when someone changes the build system – did they do more than a manual smoke test after the last-minute-change?.

Test-First development of your Build Automation System

Build Automation is extremely relevant for the delivery of the final product, still it is often treated as a kind of glorified AUTOEXEC.BAT: The build system people tinker around with plug-in configurations in the pom.xml like in the old MS-DOS days. But it’s real software!

So, why not treat it as a first class citizen? Having an easily maintainable, customized process written in your production general purpose language prevents 1500+ line build files (your coding stanadard says ~200 lines per unit max, right?), hard coded values and doomsday events on important releases.

A few Ideas

  • Use Test-First Development, TDD, all of it. I want my build system to be at least as good as the final product – when it has a defect or is not exensible I cannot deliver my products with the desired quality or in time.
    • The machines that build a consumer product often outlive the product they build for economical reasons.
  • Apply the same (or even more strict) design and coding rules.
  • Look out for self-similar structures. Even a Build Tool needs a Build Tool – but usually just not that complicated.
  • Do not use Language A to build Language B (except when A is a subset of B).  You will have more tools that may behave wrong after an innocent update.
  • Leverage compiler type checks (an ArtifactId is pretty well defined…) and IDE auto-completion.
  • Favor small build tasks over the Maven do-it-all-and-in-one-file approach (try configuring Maven to distribute an artifact to 5 different repositories, depending on different conditions.).

And please: Don’t invent yet another embedded DSL: A ten line build script is something you only find in textbooks (just like SELECT * FROM ORDERS …).

Has innovation on iOS and Android come to an end?

…and how OOP might help (again) in taking the next step.

Imagine a mobile device like the iPad with the freedom of a SmallTalk environment. When buying apps, you would also get components with documented interfaces. There is an easy to use programming environment. It would be possible to compose a new app in no time by using the geo output of the maps application(s) and feed it to the train (or plain) search, searching for the shortest or cheapest way to travel. The amount of the ticket purchase is automatically fed into your accounting tool – into the correct account, of course. With current mobile systems this is not possible. They are more (iOS) or less (Android) closed systems narrowed down to consuming entertainment.

One could accept the fact that mobile devices are closed/regulated and do not provide much customization of even user-programmability. But for the last two years I did not see very much innovation on mobile applications and that’s a pity. Given that the first iPad came out in 2010 this is also surprising. If you were a company focussing development of applications on mobile devices, what would you do right now, in 2014? Develop another cheesy casual game with loads of In-App purchases to frustrate parents? A high-priced, stand-alone niche application for sailing, model train control or music creation? With Apple’s strict policy on what apps are allowed to do (and how they do it) there is little to expect.

Of course one can always develop applications for established brands and business models. Maybe there will be a new killer-app when NFC-based payment is ready for the market. But this is only commerical innovation, nothing that changes society, enables kids to learn easier in their very own way or boost productivity in work-life.

I expected a great deal of exciting apps. In 2010 and 2011 it looked promising. Star Walk – a glimpse of augmented reality when used outside. Korg’s Polysix and MS-20 – makes perfect sense. Or PocketCAS, an easy-to-use computer algebra system. But these are basically stand-alone applications (yeah I know AudioBus).

Customization and freedom of choice are still considered evil by Apple, but also by Google. While it doesn’t come as as a surprise anymore that Apple considers its customers basically as resources to generate constant revenue streams, “Don’t be evil” Google pushes in the same direction – given that top-of-the line devices do not have SD cards, are built by Samsung and upgrades are late and provided only for a short time.

The excitement on mobile devices is over and this is because of their limited features beyond consuming media. Open systems promise to be more dynamic, productive and powerful than a closed ecosystem where everything is dumbed down to the basic level. I do not say that Apple’s or Google’s mobile business model will fail anytime soon given that people buy new cheap clothes every year because there is “fashion”. But mobile computing is becoming more and more boring.

Typing and Binding and why static Type-Checking is a free, automated Test

[Meyer97] contains a neat chapter with definitions that makes it a lot easier to discuss about programming languages when it comes to typing. Here are the basics.

We should agree on the basic construct of object-oriented computation: the feature call or for SmallTalkers “passing a message to an object” and for Java guys and gals “invoking a method”.

The Basic Construct (feature call)

x.f(arg); // [0..n] arguments

(Meyer97, p.610)

Think of a non-trivial class-hierarchy of visual components, a classic example. There is an interface or an deferred (abstract) base class that requires descendants to provide a feature (method) named paint(context). What happens if the compiler does not check that every concrete class has an implementation? We get a type violation at run-time.

Definition: type violation

A run-time type violation (or just type violation for short) occurs in the execution of a call x.f(arg), where x is attached to an object OBJ, if either

(V1) There is no feature corresponding to f and applicable to OBJ.

(V2) There is such a feature, but arg is not an acceptable argument for it.

(Meyer97, p.612)

Object-oriented typing problem

When do we know whether the execution of an object-oriented system may produce a type violation?

(Meyer97, p.612)

In SmallTalk, the compiler does not check if every instance that declare “I’m a VisualComponent” does have an implementation of the method “paint()”. The developer can only verify the expected operation of the system by exercising the code in tests or production or by manually checking all classes.

Definition: statically typed language

An object-oriented language is statically typed if it is equipped with a set of consistency rules, enforceable by compilers, whose observance by a system text guarantees that no execution of the system can cause a type violation.

Doesn’t every organization crave for a look in the future? (Hint: If you are using a certain system called Oracle you know the answer 🙂 ). Using a statically typed language is basically a look into the future for free. You can see if your system is bound to fail at run-time. Giving that away doesn’t seem too smart.

Here are the characteristic properties when it comes to demixing your preferred language type-wise:

Typing and binding

  • Typing question: When do we know for sure that at run-time there will be an operation corresponding to f and applicable to the object attached to x (with the argument arg)?

  • Binding question: Which operation will the call execute?

(Meyer97, p.621)

Given that early detection of errors results in the lowest fixing costs it is desirable to know the answer to the question “when do we know we have a feature?” at “the earliest possible time”, determined by static type checking at compile time.

The answer to “what feature do we use?” should be “the right feature” – determined by dynamic binding at run-time.

Think of type checking a statically typed language text as an automated test you do not have to write because it is build into the system. I like that because it leaves more quality time to write tests that cannot be created by a machine :-)!

[Meyer97]    Meyer, Bertrand: “Object-Oriented Software Construction, Second Edition”, Prentice Hall, 1997.

A little reminder for me on how to quickly setup Jenkins on Ubuntu

Installation (from Jenkins installation guide)

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins

The package creates the user jenkins. Interesting locations are:

  • Settings (HTTP port!) are at: /etc/default/jenkins
  • Jenkins daemon startup: /etc/init.d/jenkins
  • Log file: /var/log/jenkins/jenkins.log

I prefer a different HTTP port than the 8080 default one, so I am going to change the default settings:

sudo vi /etc/default/jenkins

Find the value HTTP_PORT and change it to a free port (don’t forget to document in your Wiki!), for example 8282. Restart jenkins:

$ sudo service jenkins restart

Future updates are fetched by apt-get like any other package:

sudo apt-get update
sudo apt-get install jenkins

Using standard OO techniques instead of Property Files for I18N and L10N

Using property files / message bundles for I18N / L10N and configuration purposes is problematic. Here’s why:

  • Property files are not refactoring safe.
  • They are in fact interfaces but rarely treated a such.
  • They are often a dump for everything that needed to be configurable a day before the iteration ends.
  • They’re often not well enough documented or not documented at all because documenting property files is a hassle (no standards, no javadoc)
  • …or they consist entirely of comments and commented out statements.
  • They make testing harder than necessary.
  • Often properties are created ad-hoc.
  • Validation logic and default handling inside the application is error-prone.
  • Missing definitions lead to runtime errors, undetected errors or awkward messages like “Error: No {0} in {1} found”.
  • Sometimes the file is not loaded because someone cleaned up src/main/resources.
  • Most property files are loaded on startup or only once.

How can we get rid of property files? I’d like to show you a straight forward solution in Java that will work well if you are not afraid of recompilation. Let me give you an example. This is what is common practice in Java:

# Find dialog
dialog.find.title=Suchen
dialog.find.findlabel.text=Suchen nach
dialog.find.findbutton.text=A very long text that needs to be wrapped but the developer does not know that it's possible - this really happens!
...

Most applications I’ve seen have endless definitions of properties for the UI. I swear I have never ever seen a non-developer change these property files!

The alternative is so simple I’m almost afraid to show it, but it has been extremely useful.

Define a name for each value and add it as a simple read-only property to an interface. Provide concrete implementations for each required language / locale.

 

package com.acme.myapp.presentation.find;

public interface FindDialogResources {
     public String getDialogTitle();
     public String getFindLabelText();
     public String getFindButtonText();
     public String getFindNextButtonText();
     public String getCancelButtonText();
     public String getIgnoreCaseText();
}

// Implementation in myapp-ui-resources-de_DE.jar

package com.acme.myapp.resources;

public class FindDialogResoucesBundle implements FindDialogResources {
    public String getDialogTitle() { return "Suchen"; }
    public String getFindLabelText() { return "Suchen nach";  }
    ....
}

// alternative to enable dynamic change of language:

public class FindDialogResources_de_DE implements ...

public class FindDialogResources_en_US implements ...

Resources that are subject to L18N and I10N must be externalized. But the way of externalization should conform to good software engineering practice.

I like the approach of linking a different resource JAR to smart client applications but you can also load classes by package- or name prefix to enable dynamic switching of languages (for web apps).

Advantages over property files:

  • Clean, minimal, intention-revealing interface (obviously).
  • Refactoring safe.
  • Statically typed and automatically linked by the JVM.
  • Missing definitions are compile-time errors, missing values are easily detected using reflection-based tests.
  • No assumptions about the final implementation.
  • Self-documenting
  • Interface defined in the same module.
  • Implementations can be delivered in other modules or (OSGi) fragments / language packs.

The builder that creates a UI entity requires a concrete implementation of this specific interface as a dependency. This principle can be applied to web applications as well, for example my exposing the resources as a managed bean. Declaring values in Java-Beans style comes in handy for auto-completion in Facelet templates.

public class SwingFindDialogFactory {
    private final FindDialogResources resources;

    public SwingFindDialogFactory (FindDialogResourceBundle resources) {
        requireNonNull(resources); // precondition
        this.resources = resources;
    }

    public FindDialog createInstance(...) {

        ...
        final JLabel findLabel = new JLabel(resources.getFindLabelText());
        final FindButtonAction findAction = new FindButtonAction(resources.getFindButtonText(), resources.getFindNextButtonText());
        ...
    }
}

...

// web app with JSF 2:

@Model
public class Resources implements FindDialogResources {
 private FindDialogResources delegate;

 @PostConstruct
 public initResources() {
     resources = application.getLocalizedFindDialogResourcesForPrincpipal();
 }

 public String getDialogTitle() {
     return delegate.getDialogTitle();
 }

// usage in facelet definition

<h:form>
 ...
 <h:outputText value="{resources.nameLabelText"} />
 <h:inputText value="{myUseCaseForm.name} />
 ...
</h:form>

As a bonus, you can:

  • Fallback to a default language (i.e. EN_us) if translation is not yet complete using a decorator.
  • Automatically validate each ResourceBundle implementation in the CI pipeline for completeness. For example by invoking all methods reflectively and checking results for non-empty values (a pretty good indicator if someone got a call during translation…).
  • You can easily generate and update the manual using simple programs written in Java as I’ve shown in other posts.
  • You can even wrap property files or other data sources if this is required by company policy.