AlternativeImplementation

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(New page: 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 comp...)
Line 1: Line 1:
Do you want to run [[NetBeans|your application]] on [[JDK]]8, but use [[JDK11]] [[API]]s? That's traditionally done with a reflection. Use [[JDK]]8 [[API]]s directly and let [[javac]] compile your code against them. Whenever using [[JDK11]] [[API]]s, 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 [[module]]s/[[JAR]]s and let them work in smooth orchestration.
Do you want to run [[NetBeans|your application]] on [[JDK]]8, but use [[JDK11]] [[API]]s? That's traditionally done with a reflection. Use [[JDK]]8 [[API]]s directly and let [[javac]] compile your code against them. Whenever using [[JDK11]] [[API]]s, 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 [[module]]s/[[JAR]]s and let them work in smooth orchestration.
-
=== The Integration point [[API]] ===
+
=== Extension Point ===
When you need to access a functionality which requires different code to run on [[JDK]]8 and [[JDK11]], start by defining an [[APISeam]]:
When you need to access a functionality which requires different code to run on [[JDK]]8 and [[JDK11]], start by defining an [[APISeam]]:
Line 23: Line 23:
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 [[JDK]]8 [[API]] for you - e.g. the resulting [[JAR]] is going to run on [[JDK]]8.
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 [[JDK]]8 [[API]] for you - e.g. the resulting [[JAR]] is going to run on [[JDK]]8.
 +
 +
=== Providing the Extension ===
Now let's compile the [[JDK11]] part of the application:
Now let's compile the [[JDK11]] part of the application:
Line 35: Line 37:
</source>
</source>
-
{{NB|org-openide-util-lookup|org/openide/util/lookup|ServiceProvider}} is a comfortable way to generate ''META-INF/services/BetterHandler'' registration without risk of making typos. One can of course, create the {{JDK|java/util|ServiceLoader}} registration manually.
+
{{NB|org-openide-util-lookup|org/openide/util/lookup|ServiceProvider}} is a comfortable way to generate ''META-INF/services/BetterHandler'' registration without risk of making typos. One can of course, create the {{JDK|java/util|ServiceLoader}} registration manually. This time we use '''--release 11''' flag to instruct [[JDK11]]'s [[javac]] to allow us to use all [[JDK11]]'s [[API]]s 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 [[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]] ====
 +
 
 +
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.
 +
 
 +
=== Summary ===
 +
 
 +
It is possible to write [[Java]] application that runs on [[JDK]]8 and safely use [[JDK11]] APIs without any need for reflection.

Revision as of 06:08, 16 February 2021

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 different code to run on JDK8 and 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 [[JDK]8. 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