• Post Reply Bookmark Topic Watch Topic
  • New Topic

How to choose between two similar but unrelated classes at runtime?  RSS feed

 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Good afternoon folks,

I am currently facing a problem that I'd like to find an elegant solution for, and I'm looking for a bit of help.

I have a dependency on a class, and from time to time I have to update the code to depend on a new version of that class. However, although the new class is structurally similar to the old class with just some value changes, the two classes are unrelated. The problem I have is that I'd like to be able to choose at runtime to use one version or the other. An example will help.

Let's use this as an example: This FixedOffsetMessageGenerator has the responsibility to generate a message String with each field as a fixed length, where the size of each field is defined by the MessageTemplate class. The MessageTemplate class is autogenerated and lives in an external dependency so there is no opportunity to make any changes to it.

Under normal circumstances when a new version of MessageTemplate comes along it requires a code change. The only change in the client code is to change the package name in the import statement.

We run multiple instances of this application and the requirement is to build in a feature switch that will enable us to configure whether it uses v1 or v2 of the MessageTemplate. If the MessageTemplate classes were part of a class hierarchy then with Polymorphism it'd be easy, but they are unrelated which makes it much more difficult.

Any and all ideas very welcome.

Thanks, Tim
 
Paul Clapham
Sheriff
Posts: 22841
43
Eclipse IDE Firefox Browser MySQL Database
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What came to mind when I read that was this: "That sounds like the way XML parsers are loaded." So if you have a look at the description of DocumentBuilderFactory's newInstance() method, you'll see what I mean. The particular part I had in mind was this part:

Uses the service-provider loading facilities, defined by the ServiceLoader class, to attempt to locate and load an implementation of the service...


I dunno, that might not be flexible enough in your case but maybe it's worth having a look at.
 
Piet Souris
Master Rancher
Posts: 2044
75
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Maybe a Resource file with a key and a path to the class to be loaded? You only need to update that file if there's a key or class update. But I'm not sure I understood the issue.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The key part of that solution is
load an implementation of the service
... which isn't the case here.

The best idea I've had so far is to use the Bridge pattern but haven't tried an implementation of it yet. The straight forward way to implement it isn't great for performance.

There's two things bad about this solution:
1) I now have to create a new instance of the Delegate class everywhere I want to use the Template.
2) The performance hit of having to check the state of 'v2enabled' every time is undesirable.

I haven't come up with anything better yet.
 
Carey Brown
Saloon Keeper
Posts: 3329
46
Eclipse IDE Firefox Browser Java MySQL Database VI Editor Windows
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A possible approach is to have the MessageTemplet class get its values from a properties file.
 
Liutauras Vilda
Sheriff
Posts: 4928
334
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey Brown wrote:A possible approach is to have the MessageTemplet class get its values from a properties file.

Something like?


 
Carey Brown
Saloon Keeper
Posts: 3329
46
Eclipse IDE Firefox Browser Java MySQL Database VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That would be a way to do it, but I'm wondering why you would still needs the MessageTemplate class at all, why not just get the values directly from the properties file?
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think the Bridge is your best bet. I would create my own MessageTemplate class and change all the client code to only use that one, once and for all.

Since this is done only once at class loading, then the performance hit is taken once. You can read the fully-qualified class name from a system property that you can change for each app instance that you run.

EDIT: Just noticed Liutauras' code - yeah, that's almost what I had in mind for the static initialization block above, except just use System.getProperty() to get the class name.
        
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Cooke wrote:the requirement is to (build in a feature switch that will) enable us to configure whether it uses v1 or v2 of the MessageTemplate.

...The straight forward way to implement it isn't great for performance.

I italicized the implementation detail that got built-in with your requirement statement. If you take out the baked-in solution from that sentence, the requirement is still the same except now you have more freedom to explore options because you're not prematurely locked into one particular solution.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey Brown wrote:... why not just get the values directly from the properties file?
Because the properties don't live in a properties file. These MessageTemplate classes are a lot like a properties file, but just in the form of a class. I don't know why this is so but these are the cards I've been dealt so have to roll with it.

The use of Reflection in Liutauras's proposal is absolutely the missing piece in my thinking, and the refinement proposed by Junilu is exactly the form that intend to use in the final solution. The best feature of Junilu's proposal is that the client usage of the MessageTemplate class remains essentially unchanged, which cuts down the amount of rework required to implement the Bridge. I'm still performing Shotgun Surgery but it's not as invasive as it could be with another solution. Another important advantage to this solution is that the Shotgun Surgery will not need to be repeated when the next new Template version is released because the dependency on that class exists in just one place now. It's tidy, and I like it a lot.

Thank you all for your help.
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for all those cows. Glad that helps you.

It's interesting but I just realized that I've used a similar solution at work to solve a slightly different problem. We used to deploy our apps to an IT-managed environment and they had this scheme where they defined a specific environment variable for apps to refer to in determining which set of configuration files to apply. I used the same technique, a static initialization block that read a system property the IT-managed system environment variable and set the value of a private static final field used by static methods like isProduction(), isDev(), and isStage().

A third instance would make it more conclusive but it seems we may have stumbled upon a Java solution pattern of sorts here, too.

 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm going to call it the Lacar Bridge.
 
Liutauras Vilda
Sheriff
Posts: 4928
334
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the cows too, lots of them.

Indeed Junilu’s idea to have a class named same as a templates of interest makes solution cleaner and clearer.
I am going to keep that idea of approach in my mind for the future.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's my rough draft of what it looks like. The only compromise I had to make was that I could no longer declare the static fields as final because the values are assigned later in the static block.

All of the heavy lifting with the Reflection library and variable assignment is done up front in the static block, which is fine, leaving the performance of the client usage unaffected. This is a big deal for my requirements.

And the only change required on the client side is to change the import statement for MessageTemplate to use the new bridge class.

I think that'll do just nicely.
 
Liutauras Vilda
Sheriff
Posts: 4928
334
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim, do you must use GenConfig.isV2Enabled()? Because now you have to put yourself in a corner about having harcoded version either v1 or v2. Whenever comes version v3 you'll need to recompile MessageTemplate bridge. Why not to keep class configuration in a properties file and not worry about the version decision within a source file? as in example:

properties file you'd have configured to refer to a one template at any given point in time:
or
or

In which way you wouldn't need to make decision in source file which MessageTemplate version to use, you just need to know that you are dealing with messageTemplate.class from properties file.
So you get needed version from properties file without a need to recompile your Java source file in case of changes in templates naming convention or packaging.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's certainly an option for the configuration item and will probably be a point of discussion with the team in the near future. The example I've used in this thread has hidden a great deal of the complexity, which was unnecessary for this discussion, including the fact that there are about 20 of these message templates for different message types. The action of enabling a particular application feature requires the use of a new version of a number of templates, so having to define each class name in the config would be a bit unwieldy and probably not that intuitive for the guys and girls who have to deploy and set up the application. But you never know, it might turn out to be the preferred option. We'll see.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Additionally, in my case a move to a new version of the template almost always comes with other code changes too so the ability to change template versions with no code change is not as valuable as you might think.
 
Liutauras Vilda
Sheriff
Posts: 4928
334
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I see, ech, how i forgot there is always something else to consider
 
Rob Spoor
Sheriff
Posts: 21135
87
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You forgot to make your constants final. Because you initialize them in a static initializer block that would still compile (as long as you throw a runtime exception in the catch block).
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Tim Cooke wrote:The only compromise I had to make was that I could no longer declare the static fields as final because the values are assigned later in the static block.

I'm pretty sure you can still declared those fields as final. The requirement is that they are definitely assigned at the end of the static initialization block.
You might be confusing this with calls to super, which do need to be made before anything else in a constructor.
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The reason you're getting an error with your code is because of the "definitely assigned" part of the requirement regarding static final fields. The code is all wrapped in the try block but the catch part doesn't assign values to the static final fields in the event that an exception occurs. If you assign reasonable defaults to the static final fields in the catch block, you should be able to get rid of the error (Edit: I guess that's not the way to go after all). When you don't declare them as final and you get an exception in the static initialization block, the static fields will end up being defaulted to 0, which I'm not sure is the behavior you want.

A little restructuring of the logic will allow you to keep the fields as static final:

Or you could assign the default values in the catch block. You'd still need to declare the local vars outside the try-catch block though.  
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This is excellent to get back to declaring those static fields as final so the public contract can remain unchanged.

The idea of assigning a default value to those fields should the Reflection bit fail makes me feel really uneasy. To have the application continue running with incorrect values being served up by the MessageTemplate would be a very very bad thing. If for some reason it can't get the correct value through the Reflection call then I need to know about it immediately and loudly because in that case the show must not go on. Therefore, Rob's suggestion of throwing a RuntimeException in the catch block is the desired behaviour. Thanks Rob.
 
Paul Clapham
Sheriff
Posts: 22841
43
Eclipse IDE Firefox Browser MySQL Database
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
One other thing: the classes which use MessageTemplate.NAME_WIDTH have already been compiled with the value assigned to that constant, haven't they? Like for example the one with this line of code



would have been compiled as if it were written



At least that was the case in early versions of Java. So just dropping in your New And Improved™ version of MessageTemplate wouldn't affect those classes. But I'm assuming you're going to recompile them too.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That's a good point Paul, thanks for bringing it up. But yes, everything gets recompiled so all is peachy.
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I don't think it matters if the code that uses the static final field is recompiled every time a new version of a MessageTemplate class is introduced. What Paul is getting at is compiler optimization by inlining of constant literal values. I don't see how a compiler can determine the value at compile time though -- you're using a static initialization block and the Reflection API to assign a value to a static final field. There is no literal involved, so how can the compiler inline it? The value still needs to be resolved at run time.
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This is the relevant section in the JLS, which mentions blank final variable declarations.

https://docs.oracle.com/javase/specs/jls/se9/html/jls-4.html#jls-4.12.4

It also has a link to the section on binary compatibility, which is also relevant to Paul's point.
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
While working this solution into my project, I encountered some occasions where the Bridge class needs to delegate on to an instance method which has proved problematic. In this thread we've pretty well explored how to dynamically get static values with all of the performance cost spent up front, which is idea. However, I cannot appear to use the same approach when dynamically calling on to instance methods, where using the Reflection library java.lang.reflect.Method.invoke() puts the performance cost firmly at the stage of every method invocation.

This leads me back to having to keep a reference to both classes in the Bridge and having an if/else statement to choose. This in itself is sub optimal as I've degraded the performance with the if/else statement. It sounds picky but remember these methods are in the critical path and are called a lot. Performance is everything here.

This now gets me to thinking if I could use Reflection again to generate the method on class construction so that it's body contains the call to the desired class. Something like:

If V2 required, create this method.
If V1 required, create this method.

Or perhaps use reflection to generate the field instead so the method can be just a normal method.

If V2 required, create this field.
If V1 required, create this field.

More experimentation required.
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Just woke up so this is off the top of my head. I believe a lambda expression can serve as a closure. That is, you do the if-then evaluation on construction of the instance and depending on what you find, create a lambda that calls the appropriate version. You save the lambda to a final field and just reuse the lambda in your instance's public method. You could create the lambda with a static factory method that is passed the instance that the lambda references. If this sounds a little circular, that's because it is. I'll give it a try and post an example if I can come up with one.

In the meantime, I found this article by Bruce Eckel: http://bruceeckel.github.io/2015/10/17/are-java-8-lambdas-closures/

I'm assuming, of course, that you're using Java 8 or later.
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Wait, so let me see if I understand this. On top of the static fields that were discussed previously, do you also need to call an instance method of the template class? I.e.,

where template.doStuff() is also version-specific and not necessarily dependent on the static constant values?
 
Tim Cooke
Marshal
Posts: 4051
239
Clojure IntelliJ IDE Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yup. 
 
Junilu Lacar
Sheriff
Posts: 11494
180
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think because you also need to invoke instance methods, your best bet would be to use https://docs.oracle.com/javase/7/docs/api/javax/tools/JavaCompiler.html to create the Bridge class at run time. You're probably going to need to introduce an interface to which you'd program to in your client code, then use dependency injection to provide the implementation class that gets compiled and loaded at run time. You might also think about making implementations Cloneable or provide some kind of factory method for new instances.

Again, this is all just off the top of my head, so you'll have to explore these ideas further with some experimentation. Interesting problem.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!