
Java Train on an Apple M1 – A One Year Review
It has been nearly a 300 and sixty five days since I’ve sold the MacBook Devoted M1 (arm64
processor) for my day to day Java building as a contract advisor. I had my first contact with the Apple M1 when one amongst my route students raised an effort that the originate doesn’t pass on Apple’s fresh flagship notebook computer. I became first shy to stumble upon hardware incompatibilities in 2021. To solve those complications (no longer my primary intent, but I told myself so) and ride if that processor is on the total that snappily, I made a decision to buy the MacBook Devoted.
This text will share my preliminary pitfalls when working with the Apple M1 and a series of friendly methods and workarounds for developing and sorting out Java capabilities.
Cowl: At some level of this article, both arm64
or aarch64
consult with the Apple M1 chip. I will consult with the passe Intel/AMD processors as x64
(it’s most likely you’ll per chance presumably salvage the next synonyms: x86_64
, amd64
).
Installing Java Train Tools
The primary ingredient we install as Java developers on a brand fresh machine is a JDK. While browsing for a arm64
successfully matched JDK abet in 2021, the Azul Zulu JDK originate popped up first.
There’s no incompatibility within the set up route of of a JDK in comparison to an x64
Mac. When we have downloaded and installed a successfully matched JDK originate, we possess now the baseline for our Java building for our Apple M1:
java –version openjdk version “17” 2021–09–14 LTS OpenJDK Runtime Atmosphere Zulu17.28+13–CA (originate 17+35–LTS) OpenJDK 64–Bit Server VM Zulu17.28+13–CA (originate 17+35–LTS, blended mode, sharing) |
In its place of the Azul Zulu JDK, many diversified JDK distributors possess adopted and now provide an arm64 originate:
- Amazon Corretto
- Eclipse Temurin (Adoptium venture, historical AdoptOpenJDK)
- Microsoft’s originate of the OpenJDK
- etc.
The brew system for openjdk is furthermore inserting in a successfully matched Apple M1 JDK originate. For managing diversified Java variations, sdkman or jEnv is a ideal likelihood.
With Apple’s Rosetta 2 emulation, we are able to furthermore strive to work with an x64
JDK originate. However, we would possibly perhaps hobble into points and/or obtained’t salvage the expected performance.
What’s next is Docker. Installing a most fresh Docker for Mac version comes with Apple M1 reinforce. In the early days, there were some minor points. Nonetheless having labored with it for nearly a 300 and sixty five days now, many possess been repeatedly fastened.
All diversified building instruments that we exhaust on a day to day foundation both present an arm64
originate or the emulated x64
version works graceful: IntelliJ IDEA, Visual Studio Code, Slack, Thought, Docker for Mac, Spotify, Firefox, Microsoft Teams, Postman.
Rule one: At any time when there is a native arm64
originate of any building instrument for the M1, dart for it. Completely strive to salvage the x64
originate working if there’s no arm64
originate accessible (yet).
To further examine the compatibility and optimization for diversified diversified instruments for Apple’s M1, checkout out isapplesiliconready.
Integration Tests with Docker and Testcontainers
By system of working our integration tests that exhaust Testcontainers, the principle ingredient we stumble over with our M1 machine are most likely take a look at screw ups on account of missing arm64
platform reinforce.
Docker photos are built for particular processor architectures. We are able to name the accessible platforms by searching on the OS/Arch column on Docker Hub for a specific Docker image:
The instance above is a screenshot of the legitimate Keycloak Docker image. It handiest comes with reinforce for x64
machines.
If we strive to hobble this Docker image on our arm64
Apple M1 machine, we salvage the next warning:
$ docker hobble jboss/keycloak: 16.1.1 WARNING: The requested image’s platform (linux/amd64) does no longer match the detected host platform (linux/arm64/v8) and no particular platform became requested |
While we salvage past this warning and Docker tries to hobble a image for a diversified platform, it internally makes exhaust of emulation to study out to salvage the x64 container up and working. However, this emulation for x64
reinforce for arm64
is an awfully most intelligent effort.
Pictures for a diversified platform would possibly perhaps or would possibly perhaps no longer work on our Apple M1. There’s no guarantee that the container will originate and possess as expected.
The Keycloak image, as an illustration, unfortunately, crashes at some stage within the bootstrap share:
# [thread 378 also had an error] [thread 374 also had an error] # A fatal error has been detected by the Java Runtime Atmosphere: # # [thread 381 also had an error] [thread 331 also had an error] [thread 372 also had an error] [thread 377 also had an error] SIGSEGV (0xb) at notebook computer=0x000000401de90af2, pid=310, tid=334 # # JRE version: OpenJDK Runtime Atmosphere 18.9 (11.0.14+9) (originate 11.0.14+9-LTS) # Java VM: OpenJDK 64-Bit Server VM 18.9 (11.0.14+9-LTS, blended mode, sharing, tiered, compressed oops, g1 gc, linux-amd64) # Problematic frame: # J 546 c2 java.lang.AbstractStringBuilder.append(Ljava/lang/String;)Ljava/lang/AbstractStringBuilder; |
When working our take a look at suite for the principle time on an Apple M1, we possess now to name the Docker containers that fail to birth.
If the image itself doesn’t present arm64 reinforce, we are able to demand for an different. For the most frequently veteran Docker photos, these are most likely conceivable choices:
- PostgreSQL: The everyday photos comprise
arm64
reinforce, no adjustments required - Webdriver photos: Standalone arm64 reinforce comes with seleniarm photos
- Keycloak: No arm64 originate for the jboss variant, nonetheless, there are neighborhood multi-architecture builds (richardjkendall or mihaibob) accessible
- LocalStack: Starting up with version 0.13, LocalStack publishes multi-architecture Docker photos
- MySQL: Starting up with version 8, the oracle version gives an arm64 originate. One other different is the MariaDB image
- MongoDB: The everyday photos comprise
arm64
reinforce, no adjustments required - Kafka: No
arm64
originate for the bitnami image, but the wurstmeister originate helpsarm64
Dynamically Replace Docker Pictures For ARM64 Reinforce
At any time after we uncover ourselves shopping for an different to the default Testcontainers Docker image, we opt to override the default image. If we’re working on a venture on our gather or know that the total team is utilizing an arm64
processor, we are able to hardcode the arm64
successfully matched Docker image. In any other case, we are able to originate the image substitution extra dynamic and handiest change the Docker image when working on the particular architecture.
Let’s exhaust the Selenium Webdriver container as an illustration for a dynamic override:
@Container static BrowserWebDriverContainer?> webDriverContainer = fresh BrowserWebDriverContainer( Scheme.getProperty(“os.arch”).equals(“aarch64”) ? DockerImageName.parse(“seleniarm/standalone-chromium”) .asCompatibleSubstituteFor(“selenium/standalone-chrome”) : DockerImageName.parse(“selenium/standalone-chrome”) ).withCapabilities(fresh ChromeOptions()); |
In accordance to the machine property os.arch
we determined which image to make exhaust of for our BrowserWebDriverContainer
.
As a replacement to this workaround, we are able to exhaust Testcontainers Cloud. With Testcontainers Cloud, we hobble the backing containers for our integration tests within the cloud. There’s no switch required for our take a look at. The containers will excellent hobble on somebody else machine. Since its deepest beta, I had the likelihood to demo this extra special instrument and am convinced that this is able to per chance power the productiveness (e.g., faster builds) for sorting out with Testcontainers even further.
As a last resort, we are able to furthermore disable particular tests within the occasion that they obtained’t hobble at all on our M1 processor. In accordance to the os.arch
Java machine property we are able to detect the processor architecture and exhaust a JUnit Jupiter annotation to disable tests for our M1:
@DisabledIfSystemProperty(named = “os.arch”, suits = “aarch64”, disabledReason = “No ARM64 reinforce”) class SomeIT { } |
We are able to exhaust this annotation on high of a take a look at class or for a specific take a look at. While right here’s no longer an optimal solution, no no longer up to it helps the Apple M1 developers on our team to study a green originate.
These take a look at disabling annotations needs to be short-time duration. We must take a look at if there is a solution accessible (e.g. a successfully matched Docker image) occasionally. The ideal originate on our CI pipeline must make certain that to hobble all tests (frequently a x64
machine).
Docker Shopper JNA Failure with Testcontainers
One other pitfall we would possibly perhaps tumble into sooner than we are able to really hobble our Java integration take a look at with Testcontainers is the next error:
06: 49: 41.241 [main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy – Might well no longer salvage a sound Docker atmosphere. Please take a look at configuration. Attempted configurations were: 06: 49: 41.241 [main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy – UnixSocketClientProviderStrategy: failed with exception RuntimeException (java.lang.UnsatisfiedLinkError: /Users/rieckpil/Library/Caches/JNA/temp/jna11180227188626594160.tmp: dlopen(/Users/rieckpil/Library/Caches/JNA/temp/jna11180227188626594160.tmp, 0x0001): tried: ‘/Users/rieckpil/Library/Caches/JNA/temp/jna11180227188626594160.tmp’ (plump file, but missing successfully matched architecture (possess ‘i386,x86_64’, need ‘arm64e’)), ‘/usr/lib/jna11180227188626594160.tmp’ (no such file)). Root cause UnsatisfiedLinkError (/Users/rieckpil/Library/Caches/JNA/temp/jna11180227188626594160.tmp: dlopen(/Users/rieckpil/Library/Caches/JNA/temp/jna11180227188626594160.tmp, 0x0001): tried: ‘/Users/rieckpil/Library/Caches/JNA/temp/jna11180227188626594160.tmp’ (plump file, but missing successfully matched architecture (possess ‘i386,x86_64’, need ‘arm64e’)), ‘/usr/lib/jna11180227188626594160.tmp’ (no such file)) 06: 49: 41.241 [main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy – UnixSocketClientProviderStrategy: failed with exception RuntimeException (java.lang.NoClassDefFoundError: Might well no longer initialize class org.testcontainers.shadowy.com.github.dockerjava.okhttp.UnixSocketFactory$1). Root cause NoClassDefFoundError (Might well no longer initialize class org.testcontainers.shadowy.com.github.dockerjava.okhttp.UnixSocketFactory$1) |
The underlying effort is a JNA (Java Native Entry) dependency incompatibility with Apple’s M1 chip. Testcontainers transitively depends on this library. Starting up with JNA 5.7.0, this effort is resolved.
To fix this relate for our tasks, we are able to both give a enhance to our Testcontainers version (preferred) or manually override the JNA version. Starting up with Testcontainers 1.15.3, Tesctonainers transitively depends on a JNA version (5.7.0) that’s working for the Apple M1.
Recount we composed hit upon the identical take a look at failure even after bumping the Testcontainers version. If that is the case, the potentialities are high that one more dependency furthermore transitively depends on JNA and overrides the version.
On this topic, we are able to both exclude the JNA dependency from any diversified dependency that capabilities it or exhaust the dependencyManagement
share to force the option of a specific version of it:
|
To hit upon dependencies that transitively encompass JNA, hobble mvn dependency:tree
after which gaze JNA within the consequence.
Native Train Atmosphere with Docker Compose
Many tasks exhaust a docker-compose.yml
for working the wanted infrastructure when locally developing or sorting out a Java utility.
Searching on the amount and diversity of required infrastructure (e.g., messaging queues, databases, caches, etc.), tweaking the docker-compose.yml
to work for both x64
and arm64
would possibly perhaps no longer be an likelihood.
The the same Docker image arm64
availability effort as for sorting out applies right here. Every so incessantly the neighborhood Docker builds handiest reinforce one platform and no longer both.
As a conceivable workaround, we are able to introduce a 2d docker-compose-arm64.yml
file to augment team members working with an M1 (or any diversified arm64
machine).
I’m utilizing this thought for the Making an strive out Spring Boot Application Masterclass, the put the Amazon SQS and Keycloak image needs to salvage replaced with an arm64 successfully matched image.
This adds some minute upkeep effort as the team has to preserve the 2 Docker compose files in sync. However, as soon as the supply and adoption of arm64
photos is bettering, the devoted arm64
Docker compose file would possibly perhaps furthermore furthermore be deleted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
version: ‘3.8’ services and products: # … keycloak: image: mihaibob/keycloak: 15.0.1 atmosphere: – KEYCLOAK_USER=keycloak – KEYCLOAK_PASSWORD=keycloak – DB_VENDOR=h2 – JAVA_OPTS=-Dkeycloak.migration.circulation=import -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=/tmp/keycloak-dump.json volumes: – kind: bind source: ./tmp/keycloak-dump.json target: /tmp/keycloak-dump.json read_only: excellent ports: – “8888: 8080” sqs: image: softwaremill/elasticmq-native volumes: – kind: bind source: ./tmp/sqs-queue-definition.conf target: /decide/elasticmq.conf read_only: excellent ports: – “9324: 9324” – “9325: 9325” |
We are able to then pass the underlying compose file to our docker-compose
originate repeat:
docker-compose –file docker-compose-arm64-reinforce.yml up |
The plan back of this extra compose file is the duplicated upkeep. We must make certain that both our composes files (x64
and arm64
) are in sync. Connected to the workarounds for making our Testcontainers integration tests pass, this must furthermore be a short-time duration solution that we reassess occasionally.
Java Train: Spring Boot Netty Warning on Startup
Upon beginning a Spring Boot utility on an Apple M1, we would possibly perhaps stumble upon an ERROR
log from Netty with a sizable stack mark at some stage within the bootstrap share:
2022–03–21 06: 52: 09.885 ERROR 17220 —– [main] i.n.r.d.DnsServerAddressStreamProviders : Unable to load io.netty.resolver.dns.macos.MacOSDnsServerAddressStreamProvider, fallback to machine defaults. This would possibly perhaps consequence in fallacious DNS resolutions on MacOS. java.lang.possess.InvocationTargetException: null at java.terrifying/jdk.internal.possess.NativeConstructorAccessorImpl.newInstance0(Native Diagram) ~[na:na] at java.terrifying/jdk.internal.possess.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java: 77) ~[na:na] at java.terrifying/jdk.internal.possess.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java: 45) ~[na:na] at java.terrifying/java.lang.possess.Constructor.newInstanceWithCaller(Constructor.java: 499) ~[na:na] at java.terrifying/java.lang.possess.Constructor.newInstance(Constructor.java: 480) ~[na:na] at io.netty.resolver.dns.DnsServerAddressStreamProviders. |
While our utility must composed feature as expected, this log is noisy and can confuse developers. As long as we salvage no longer hobble our Spring Boot utility on an Apple M1 in production, or no longer it’s furthermore less primary if we salvage no longer fix it.
For many who opt to fix this startup error message, be distinct that to make exhaust of Netty> 4.1.68.Ultimate
and add the next dependency to our venture:
|
After making this switch, the Spring Boot utility must originate one more time with a neat bootstrap log.
Building Multiplatform Docker Pictures on an Apple M1
When building Docker photos, the resulted image will handiest work on the platform we’re working on. Any output of docker originate -t myimage .
on an Apple M1 will handiest work on one more arm64
machine.
While right here’s fully glaring, it took me a 2 hour debug session to be responsive to it. I became building a brand fresh feature for a Spring Boot utility (Stratospheric) and wished to push the resulting Docker image on to the ECR (Elastic Container Registry) from my local machine. While we possess now a working CI/CD in remark, I wished to short-circuit this route of and save time. After pushing the image to the container registry, I encountered uncommon error logs, and the ECS (Elastic Container Service) task became unable to birth.
That’s since the underlying EC2 instance is an x64
machine. Even the glaring issues occasionally uncover it the laborious system to attain them.
At any time after we uncover ourselves in a the same topic, the put we opt to originate a Docker image for a diversified processor architecture, we possess now two alternate choices:
- Produce the target Docker image on a CI/CD server working on an
x64
machine (or any architecture) - Use Docker’s buildx, a CLI plugin, to salvage salvage entry to to all aspects of the Moby BuildKit (e.g., multiplatform photos)
We obtained’t dart further into the principle likelihood, as a CI/CD workflow (e.g., GitHub actions) needs to be already in remark for (hopefully) most tasks.
The 2d likelihood is extra intelligent to us as this is able to per chance permit us to originate locally arm64
and x64
successfully matched Docker photos.
As a prerequisite for this to work, we possess now to make certain that our Docker terrifying image (FROM
phase of the Dockerfile) gives a variant for all architectures we opt to originate the last Docker image for. We are able to examine this by visiting Docker Hub and the tags share of our Docker terrifying image.
Taking eclipse-temurin
(OpenJDK originate of the Adoptium venture, historical AdoptOpenJDK) as a Docker terrifying image instance, we are able to hit upon that this image helps diversified architectures:
Once our Dockerfile
exhaust a terrifying image with reinforce for plenty of architectures, we are able to exhaust the Docker buildx
repeat to originate the image for plenty of platforms:
docker buildx originate –platform linux/amd64,linux/arm64 -t todo-app:most fresh . |
In the instance above, we originate both an arm64
and x64
image for our Java Spring Boot utility:
FROM eclipse-temurin: 17-jre ARG JAR_FILE=originate/libs/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [“java”, “-jar”, “-Dspring.profiles.active=aws”, “/app.jar”] |
We are able to furthermore straight away push the consequence correct into a container registry with:
docker buildx originate –platform linux/amd64,linux/arm64 –push -t some.ecr.amazonaws.com/todo-app:most fresh . |
The utilization of this trend, we now can originate fresh Docker photos locally and deploy them to hobble on x64
machines within the cloud. This presents us wide flexibility for the Java building on our Apple M1.
Building ARM64 Pictures on GitHub Actions
If we’re within the lucky remark to originate and publish our gather Docker photos, we would possibly perhaps opt to adopt arm64
builds to broaden the final adoption rate.
We’re utilizing a personalised ActiveMQ Docker image for the Stratospheric venture for sorting out and local building capabilities. We were all utilizing a classic Intel processor at some stage within the inception share of this venture and when developing this Docker image. Things modified with my MacBook Devoted steal, and therefore, we began searching into multiplatform reinforce.
The ActiveMQ message dealer runs on the JVM. Resulting from this truth any ingredient of our Dockerfile must furthermore be accessible for an arm64 architecture. For our instance, we were utilizing fabric8/java-alpine-openjdk11-jre
as the terrifying image. Unfortunately, this image handiest helps linux/amd64
(i.e. x64
) and therefore we had to interchange it with a JRE terrifying image that helps all platforms we opt to originate our image for.
We went for eclipse-temurin: 11-jre-focal
as the terrifying Docker image because it helps plenty of architectures:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
FROM eclipse-temurin: 11-jre-focal # … RUN tar xzf $ACTIVEMQ-bin.tar.gz -C /decide && ln -s /decide/$ACTIVEMQ $ACTIVEMQ_HOME && groupadd activemq && useradd -m -d $ACTIVEMQ_HOME -g activemq activemq && chown -R activemq:activemq /decide/$ACTIVEMQ && chown -h activemq:activemq $ACTIVEMQ_HOME EXPOSE 1883 5672 8161 61613 61614 61616 USER activemq WORKDIR $ACTIVEMQ_HOME CMD [“/bin/sh”, “-c”, “bin/activemq console”] |
Given the multiplatform reinforce from the terrifying image, we are able to now locally originate an arm64
variant of the Docker image (utilizing buildx
of the last share) and push it to a Docker registry.
As a replacement, we are able to automate this route of on GitHub Actions and originate a multiplatform image on every commit. The Docker team gives intriguing-to-exhaust actions to originate and push a multiplatform Docker image.
For our customized ActiveMQ image, we adjusted our GitHub Actions as the next:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
name: Publish Docker image on: [push] jobs: build_push_to_registry: name: Push Docker image to Docker Hub runs-on: ubuntu-most fresh steps: – name: Check out – name: Attach of abode up QEMU with: platforms: arm64, amd64 – name: Attach of abode up Docker Buildx – name: Login to Docker Hub with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} – name: Produce Docker image and push to Docker Hub with: context: . file: ./Dockerfile platforms: linux/amd64, linux/arm64 push: ${{ github.ref == ‘refs/heads/primary’ }} tags: stratospheric/activemq-docker-image:most fresh |
These 5 steps automate every part from sorting out the source code, building a multiplatform image for arm64
and x64
after which within the shatter pushing this image to Docker Hub.
What’s left for us is to configure a username and salvage entry to token for Docker Hub. We retailer them as GitHub secrets for our repository.
Given the GitHub automation, we deploy fresh variations of our Docker image to DockerHub on every push to our primary department:
Summary: Java Train on an Apple M1
Having labored with an Apple M1 essentially for Java building for nearly a 300 and sixty five days now, I salvage no longer remorse the preference. It took me some time to resolve the preliminary hurdles and salvage every part working. The talked about methods listed right here solved all my sorting out and developing issues for Java capabilities on the M1.
The good news is that with every passing day, extra instruments adopt arm64
as Apple continues to commence fresh flagship products working on arm64
processors. It’s handiest a topic of time till the last Java building instrument works perfectly graceful on an Apple M1.
The performance of the notebook computer (Apple MacBook Devoted M1 2021) is terribly marvelous, and a wide productiveness enhance as my Java builds, and tests hobble faster in comparison to an x64
Mac. I composed preserve my outdated Ubuntu desktop PC around as there are occasionally tasks the put I desire a excellent outdated x64
machine.
Let me be taught about your pointers & methods for Java building on an Apple M1 within the feedback.
Gay sorting out,
Philip
!–>>
Read More