With Java 11 long-term support coming to an end in September 2023, in some of our projects in which we had it running already we've decided to upgrade to Java 17. Like version 11, Java 17 is also an LTS version of the platform, meaning that it will have extended support until September 2026 and security patches until 2029. While the upgrade has been pretty straightforward in general terms, in this article we will describe a couple of technical challenges and quirks we've found when doing the upgrade in one particular project.
Impact of changes in Security and Reflection API
Because of our usage of the Salesforce API we are required to use the PATCH HTTP method to update Objects on the SF side, because the PATCH method was standardized as part of RFC 5789 in 2010, it didn’t exist in the implementation of the HttpURLConnection class, which we use for our Salesforce integration.
While it was possible to add it as part of the HttpURLConnection class, the Oracle/OpenJDK team decided to mark the bug as WONTFIX and, instead, suggested to developers that they should use the still-to-be-released HttpClient API.
Since the HTTP protocol and its methods are essentially just text which indicate how an API must behave when contacted with a particular string of data, it was decided that it was easier to just patch the HttpURLConnection class using the Java Virtual Machine internals, namely the Reflection API.
Note that HttpURLConnection class has the following field which specifies valid HTTP methods:
This was implemented in the following way:
In a nutshell perform these actions:
Which essentially just allows us to send ‘PATCH’ calls directly to any API we use.
While this worked for a while, it came with a caveat. As Oracle and the OpenJDK team improved internal security machinery inside the JVM, this particular usage of reflection was eventually deprecated per JDK-8210522 which was released as part of Java 12.
As part of our migration to Java 17, we eventually came upon a compromise solution, using the Invoke API instead.
What JDK-8210522 did was make the “modifiers” field inside Field to not be accessible using reflection, remember, we need this to make “methods” in HttpURLConnection not final. However, we can instead use VarHandle and MethodHandles.privateLookupIn, which are part of the Invoke API to “force” our way in.
While this works, it still requires additional JVM parameters to explicitly allow reflection between java packages, namely, this requires using:
As we need to specify that we will be using the java.lang.reflect package (specifically, the .setAccessible method) to introspect java.base, and again to specify that we need to introspect the class HttpURLConnection, from the java.net package.
While this will work on Java 17, by Java 18 this was patched again, and won’t work anymore, which will require refactoring our entire integration to use other tools, namely, the HttpClient class introduced in Java 11.
Besides that, we didn’t find any issues with switching to JDK 17 on the code side.
We now have our project running locally, but this is a productive API, so we need to deploy it to our infrastructure in AWS. For this, we use CodeBuild to build our jar binary and store it in S3, then we run it in an ECS container with a custom, generic Docker image that fetches and runs it. So, to achieve this, first we need to update our CodeBuild project environment:
Changing the env image to standard 6.0 is important because if we don’t do so, we won’t be able to use coretto17 (Amazon flavor of Java 17) to build our app.
Finally, we updated our buildspec to tell codebuild to use coretto17 and python 3.10 (3.9 is not supported in standard 6.0)
Also, we added the JVM params we talked about earlier in our env vars script to allow reflection:
WIth these final adjustments, we migrated successfully our project to Java 17.
In this post we've described the required steps that we had to perform to upgrade the Java environment from 11 to 17 in a particular project. In this case, most of the challenges have been related to particular aspects like Reflection access al related environments (CI/CD). But we want to emphasize that each project has its own quirks and challenges associated.