AlternativeImplementation

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(Assembling the Application)
Current revision (06:12, 16 February 2021) (edit) (undo)
(Modularity =)
 
Line 43: Line 43:
Our application has to be composed from both [[JAR]] files: ''app-jdk8.jar'' and ''app-jdk11.jar''. The entrypoint has to be in the ''app-jdk8.jar'' and it has to do something reasonable by its own when executed on [[JDK]]8. The use of '''ServiceLoader().load(BetterHandler.class)''' is going to return no ''JDK11EnhancedHandler'' (possibly yielding some {{JDK|java/lang|LinkageError}}) and as such the fallback code is executed. However when running on [[JDK11]] the ''JDK11EnhancedHandler'' is instantiated and can easily access the [[JDK11]] specific functionality.
Our application has to be composed from both [[JAR]] files: ''app-jdk8.jar'' and ''app-jdk11.jar''. The entrypoint has to be in the ''app-jdk8.jar'' and it has to do something reasonable by its own when executed on [[JDK]]8. The use of '''ServiceLoader().load(BetterHandler.class)''' is going to return no ''JDK11EnhancedHandler'' (possibly yielding some {{JDK|java/lang|LinkageError}}) and as such the fallback code is executed. However when running on [[JDK11]] the ''JDK11EnhancedHandler'' is instantiated and can easily access the [[JDK11]] specific functionality.
-
=== [[Modularity]] ====
+
=== [[Modularity]] ===
Both [[OSGi]] as well as [[NetBeans Runtime Container]] help bring this setup to even a better level. They allow one to specify in the ''app-jdk11.jar'' manifest that the [[module]] requires [[JDK11]]. E.g. when running on [[JDK]]8 the ''JDK11EnhancedHandler'' class isn't going to even be available on the "classpath". That is going to prevent any {{JDK|java/lang|LinkageError}} and just return ''null'' when the implementation of ''ServiceLoader().load(BetterHandler.class)'' is requested.
Both [[OSGi]] as well as [[NetBeans Runtime Container]] help bring this setup to even a better level. They allow one to specify in the ''app-jdk11.jar'' manifest that the [[module]] requires [[JDK11]]. E.g. when running on [[JDK]]8 the ''JDK11EnhancedHandler'' class isn't going to even be available on the "classpath". That is going to prevent any {{JDK|java/lang|LinkageError}} and just return ''null'' when the implementation of ''ServiceLoader().load(BetterHandler.class)'' is requested.

Current revision

Do you want to run your application on JDK8, but use JDK11 APIs? That's traditionally done with a reflection. Use JDK8 APIs directly and let javac compile your code against them. Whenever using JDK11 APIs, resort to reflection and call them in such a verbose, unsafe manner. That's indeed possible, but especially with modularity one has better option. Create few modules/JARs and let them work in smooth orchestration.

Contents

Extension Point

When you need to access a functionality which requires some special code to be executed on JDK11, start by defining an APISeam:

interface BetterHandler {
  void handleInBetterWay();
}

and in your code locate it via ServiceLoader or Lookup library and use it:

BetterHandler h = ServiceLoader().load(BetterHandler.class);
if (h == null) {
  // JDK8 default behavior
} else {
  h.handleInBetterWay();
}

Compile these pieces of code to run on JDK8. Btw. that can easily be done with JDK11 javac - just use the --release 8 - flag. Then the javac is only going to expose JDK8 API for you - e.g. the resulting JAR is going to run on JDK8.

Providing the Extension

Now let's compile the JDK11 part of the application:

@ServiceProvider(service=BetterHandler.class)
public class JDK11EnhancedHandler implements BetterHandler {
  public void handleInBetterWay() {
      // directly use JDK11 APIs  
  }
}

ServiceProvider is a comfortable way to generate META-INF/services/BetterHandler registration without risk of making typos. One can of course, create the ServiceLoader registration manually. This time we use --release 11 flag to instruct JDK11's javac to allow us to use all JDK11's APIs without any need to resolve to reflection.

Assembling the Application

Our application has to be composed from both JAR files: app-jdk8.jar and app-jdk11.jar. The entrypoint has to be in the app-jdk8.jar and it has to do something reasonable by its own when executed on JDK8. The use of ServiceLoader().load(BetterHandler.class) is going to return no JDK11EnhancedHandler (possibly yielding some LinkageError) and as such the fallback code is executed. However when running on JDK11 the JDK11EnhancedHandler is instantiated and can easily access the JDK11 specific functionality.

Modularity

Both OSGi as well as NetBeans Runtime Container help bring this setup to even a better level. They allow one to specify in the app-jdk11.jar manifest that the module requires JDK11. E.g. when running on JDK8 the JDK11EnhancedHandler class isn't going to even be available on the "classpath". That is going to prevent any LinkageError and just return null when the implementation of ServiceLoader().load(BetterHandler.class) is requested.

Summary

It is possible to write Java application that runs on JDK8 and safely use JDK11 APIs without any need for reflection.

Personal tools
buy