Patterns for Dealing with Legacy Code: ServiceLoader

By Gerald Mücke | October 9, 2015

Patterns for Dealing with Legacy Code: ServiceLoader

Legacy code is code that was not developed according to current standards of software development, is no longer maintained, and often is not or insufficiently documented. However, external, proprietary closed-source libraries often meet these criteria too. Often, this code is still operated and needs to be adapted from time to time, e.g., to comply with regulatory requirements. Legacy code often comes in the form of CLOBs. An undefined mass of lines of code, of which no one knows anymore what it does. The first step is to break down the system into its logical components. However, peripheral systems are often integrated via 3rd party libraries, and access to them occurs from the presentation layer through all other logical layers to the peripheral system in a single method. Before such a method can be refactored, the dependency on the peripheral system must be decoupled to write a test for the existing method. A simple tool from the Java toolset for decoupling the peripheral system is the ServiceLoader. The ServiceLoader is a simple dependency injection mechanism and part of the Standard Edition, i.e., available in every Java Runtime Environment since version 6. To use the ServiceLoader, you need three elements:

  1. An abstract class or an interface whose implementation should be interchangeable
  2. A file in the directory META-INF/services, whose name is the fully qualified name of the abstract class or interface and whose content is the fully qualified name of the implementation. This file is required in the jar of the provider, i.e., it must be located where the implementation is
  3. A factory method that uses the ServiceLoader to deliver an instance of the implementation of the abstract class or interface.

To modify the legacy code so that the 3rd party library becomes interchangeable, the following approach should be taken:

  1. Outsource accesses to the 3rd party library to a new class
  2. Abstract the class interface
    1. Outsource the interface to the Service Provider Interface (SPI) module along with the factory method
    2. Change the legacy code dependency to SPI
  3. Outsource the class with the 3rd party library accesses to a provider module, which contains the services file.
  4. For productive use, the provider module must be present in the classpath so that the corresponding implementation is used.

After this conversion has taken place, a test provider can now be developed that mocks the behavior of the 3rd party library. With this, the legacy code can then be tested without the peripheral system using the test provider and thus also be refactored.

comments powered by Disqus