• Post Reply Bookmark Topic Watch Topic
  • New Topic

Avoiding compilation-time dependencies using Reflection  RSS feed

 
Lasse Koskela
author
Sheriff
Posts: 11962
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I once worked with some code that had a utility for pretty-printing DOM documents (i.e. indenting them). The utility had three different strategies for doing that:
1) use the identity transformation
2) use the Apache Xerces proprietary XMLSerializer, or
3) use the WebLogic XMLSerializer
...depending on what's available in the classpath and what's not. The code checked upon startup using Class#forName() whether the Apache/WebLogic implementations were available and selected the correct adapter class based on that knowledge. If nothing better was available, the code defaulted to using the standard identity transform.

The motivation for doing this was to avoid compile-time dependencies to A) xerces.jar and B) weblogic.jar, and still be able to take full advantage of the formatting options available when using the different XMLSerializer implementations (the identity transform with setIndenting(true) is not what I'd call "indentation"...).

What do you think of this use of the Reflection API? Does this sound as weird to you as it did to me? The most interesting thing about all this was that the performance hit from using Reflection was actually nonexistent compared to using the Xerces XMLSerializer class directly. I checked...
 
Nate Forman
author
Ranch Hand
Posts: 32
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey Lasse,

This actually doesn't sound strange at all to me. From a capability perspective, this is definitely some of what reflection is all about--allowing your application to adapt to its environment. I really like the idea of checking around in the classpath for possible strategies.

From a safety perspective, supplying a default is a good idea. I worked on a project that had static dependencies on icon images in the file system. If one got deleted by mistake, the whole application had problems because of the NullPointerExceptions caused. I changed this behavior such that if an icon didn't exist, it replaced the icon with a red X generated by the program in memory. This didn't make the app look pretty, but it definitely solved the NPE problem, alerted us to a missing icon, and guaranteed that the red X would never be missing. It seems like you guys did the same thing with your classes that I did with my icons.

From a performance perspective, I'm not surprised at all. XML serialization is a really performance intensive process. The work done by a reflective method call should be dwarfed by the processing necessary to render all of those objects to a string. Therefore, by Amdahl's law (Chapter 9 ), the slowdown of going to the reflective solution should be very small.

Thanks for posting this usage--I found it really valuable.

Best Regards,

Nate
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ant does something similar regarding the engine used for regular expressions (additionally you can set a property to specify the implementation to use, including specifying your own adapter class for an implementation unknown to Ant). I think it's a very powerfull concept.

Notice that you don't even need to use reflection for all the method calls. After creating the object via reflection, you can simply cast it to a known interface/class, and perhaps wrap it into an appropriate Adapter. After that, none of your code needs to know that the object was created via reflection.
 
Lasse Koskela
author
Sheriff
Posts: 11962
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Ilja Preuss:
Notice that you don't even need to use reflection for all the method calls. After creating the object via reflection, you can simply cast it to a known interface/class, and perhaps wrap it into an appropriate Adapter. After that, none of your code needs to know that the object was created via reflection.

And this was exactly what we had with the pretty-printer stuff. The initiation code checked what's available in the classpath, instantiated an implementation of our XmlFormatter interface (can't remember the actual name but something like that...) and stored it into a static variable to act as a singleton.
 
Ilja Preuss
author
Sheriff
Posts: 14112
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Lasse Koskela:

And this was exactly what we had with the pretty-printer stuff. The initiation code checked what's available in the classpath, instantiated an implementation of our XmlFormatter interface (can't remember the actual name but something like that...) and stored it into a static variable to act as a singleton.


I see - I somehow missed the "selected the correct adapter class" part of your first post...

I'm not sure I like the idea of the "static variable to act as a singleton", though, but that's for a different forum...
 
Joel McNary
Bartender
Posts: 1840
Eclipse IDE Java Ruby
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
None of this sounds strange, except for the part where the goal was to remove "compile-time dependencies" Are things being recompiled in different environments such that this was an issue? Using Reflection to avoid run-time dependencies, well, that's another story all together...and is fairly common (don't we do that all the time with JDBC?)
 
Alexandru Popescu
Ranch Hand
Posts: 995
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The mechanisms Lasse is describing can be used extensivily when one application would be able to handle custom/extended implementations of some default functionality.

--
./pope
[the_mindstorm]
 
Lasse Koskela
author
Sheriff
Posts: 11962
5
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Joel McNary:
None of this sounds strange, except for the part where the goal was to remove "compile-time dependencies" Are things being recompiled in different environments such that this was an issue? Using Reflection to avoid run-time dependencies, well, that's another story all together...
Things weren't being recompiled in different environments (at least as far as I was concerned). The motivation to be independent of the WebLogic API was two-fold: first of all we couldn't put weblogic.jar into our SCM and we didn't want to force developers to install WebLogic just to get the codebase compile. At the time, we didn't know which application server would be used although WebLogic had the advantage of being the preferred product in that particular company. So, why go through the trouble of using such a proprietary API? Well, the identity transform was painfully slow (averaging 200ms per document, if I remember correctly) and we could get it down to 70ms simply by using the Xerces XMLSerializer. Now, because this was a system integrating globally with a number of external systems using XML-over-HTTP, maximum performance regardless of the selected protocol was one of the main priorities. Using the WebLogic XMLSerializer further cut down the average time it took to serialize XML documents (down to 60ms, I think).

I have to say this whole optimization thing before profiling was just taking shots in the dark, but that was the way the customer's architects wanted to go. And to cover our own asses, we chose to continue going down the DOM path as long as it could facilitate good enough performance so that we wouldn't need to write a whole bunch of SAX handlers and home-grown XML serializers.

It would certainly be interesting to go back a couple of years in time and build that system again from scratch, and see how many things we'd do differently -- or not do at all.
 
Don't get me started about those stupid light bulbs.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!