Using Java Static Meta Classes
Motivation
To fully leverage the power of Aspect Models during application development generating the Java classes for Aspect Models is usually the first step. However, while it allows to easily and quickly build an application that uses the data of single Aspects, it does not help to build applications that can handle the data of Aspects in a generic way, that means: using the Meta Model.
What would be an example for such a case? Let’s assume a very simple application, that still shows very well, why using the Meta Model is mandatory despite the problem at hand seeming not very complex at first.
Let’s assume we’re starting our application with the well-known Movement
Aspect, but possibly more Aspects are to come in the future.
To easily get a quick overview of the Aspect data we want to write a console application that pulls the data
from the Aspect and prints it to console nicely formatted. Using a built-in mechanism like generated
toString
methods might work for a very first version and for simple Aspect Models, but our Aspect already
contains nested entity structures and future ones could contain even more complex ones, with lists of entities
within lists. And apart from that, we want to print them really nicely, with indentation and other formatting
applied, maybe like this:
[Movement]
- Is moving: YES
- Speed: 35 km/h
- Speed limit warning: yellow
- Position:
- Latitude: 45.2
- Longitude: 32.7
- Altitude: 17.0
...
Now, one possibility would of course be to write specific code that does exactly that for this Aspect.
Code like that however has multiple drawbacks: it’s tedious to write (maybe not for one or two Aspects, but
most applications will work with way more Aspects), error-prone and - most importantly - it
requires touching every time your Aspect Model changes.
A second option that often comes to mind when working with models of any kind is code generation, just as we use it for the Java POJOs through the respective tooling. While code generation is a great tool for situations where the input might change quite often, but the targeted use case does not, it isn’t the best fit for use cases, that most likely will evolve over time. Building applications with Aspect Models is most of the time exactly that - how you use the Aspect Data is often subject to different changes. Adapting a generator or template for that all the time in the end does not buy you much.
What we would need is a possibility to work on the Meta Model and Aspect Model at the same time, to be able to explicitly work with or iterate over its properties. Taking the Aspect Model from above, we could e.g. write code like this:
Movement movement = retrieveMovement();
for ( Property p : getAllProperties() ) {
System.out.println( String.format( "- %s: %s", p.getName(), p.getValue( movement ) ) );
}
This code may not look very special at first glance or you might ask the question where’s the big difference compared to the other attempts we discussed above? Let’s take a deeper look.
Most obviously we are able to handle all properties of an Aspect with exactly one line - we print the property name and the value. This is the first major advantage, because as long as we do not have the need to apply very specific formatting to single elements, this single line will be a fit for any property of the Aspect - also those, that we might add to (or also remove from) the Aspect Model in the future.
This directly brings us to a related advantage, that is hidden in the loop itself: the getAllProperties()
method.
Where does this come from? We’ll see later on, that we use Static Meta Classes for that, but for now let’s
assume, that this part will be generated. Remember that we said, code generation is great for anything where
the use case does only rarely change? Getting a list of our Model’s properties is exactly such a use case.
We want a list and that’s that.
This means, whenever we change our Aspect Model, we run the code generation step once to get the updated artifacts, and after that the code above will work still exactly the same - but it also will automatically pick up the changes to the list of our properties.
Even this over-simplified example already shows the great benefits of this approach, as it allows developers to write very stable generic code that can easily also perform much more complex tasks, as long as the input that is required is made up of an Aspect Model and its data.
Granted, code like above could also be written using a dynamically loaded Aspect Model (note the missing Static).
So in order to close this motivation let’s assume another small requirement for our application that is at least
rather ugly to implement: our Aspects not only carry the data we need for our daily work, but also a lot of
data which is not strictly required to be printed - or maybe we just want to print it when some kind of verbose
mode is enabled.
Something like this requires filtering the list of properties we use. Now how to do this? Of course, we apply
some filter predicate and skip let’s say the properties called speed
and speedLimitWarning
. As we have
to use string matching on human knowledge about the Aspect Model, we again can quickly identify two issues:
-
string matching code is always ugly and subject to typing errors
-
if we ever remove e.g. the property
speedLimitWarning
this will not break our code, but leave us with something that can be surprising to another developer taking over - code, that’s outdated and nobody noticed.
How would this requirement look like, if we now assume we already have such an accompanying static meta class being
called MetaMovement
for our Aspect?
Movement movement = retrieveMovement();
List<Property> allProperties = getAllProperties();
if (mode != "VERBOSE") {
allProperties.removeAll( List.of( MetaMovement.SPEED_LIMIT, MetaMovement.SPEED_LIMIT_WARNING ) );
}
for ( Property p : getAllProperties() ) {
System.out.println( String.format( "- %s: %s", p.getName(), p.getValue( movement ) ) );
}
This solves both problems described above and adds convenience on top:
-
the static class is generated from the Aspect Model - no need to repeat the property names by hand
-
the static class contains actual Meta Model elements - even no need for strings at all!
-
the intent of the code is easier to grasp
-
if we ever remove the property
speedLimitWarning
, the above code will not compile until we remove the now invalid property reference - we gained compile-time safety!