Wednesday, November 9, 2022

The Arrival of Java 19

The arrival of Java 19!


Oracle is proud to announce the general availability of JDK 19. This release is the tenth Feature Release delivered on time through the six-month release cadence. This level of predictability allows developers to easily manage their adoption of innovation thanks to a steady stream of expected changes.

Java 19, Core Java, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Skills, Java Jobs, Java Tutorial and Mateirals

Java’s ability to boost performance, stability, and security continues to make it the world’s most popular programming language. According to an IDC report more than 10 million developers – representing 75% of full-time developers worldwide – use Java.

JDK 19 is now available!


Oracle now offers JDK 19 for developers, end-users, and enterprises. Oracle JDK 19 will receive performance, stability and security updates following the Oracle Critical Patch Update (CPU) schedule as outlined in the Oracle Java SE Support Roadmap.

Oracle JDK 19 is not a long-term support (LTS) release. Oracle JDK 17 (announced on September 14, 2021) is the second LTS  under the release cadence announced in 2018. Oracle has announced plans to shorten the time between future LTS releases, from three years to two years so you should expect the next LTS to be Java 21 in September of 2023.

Another important change announced with Oracle JDK 17 was the introduction of a new and simpler license terms which will allow companies to use Oracle JDK 17 – including the quarterly performance, stability, and security patches – at no cost for at least the next three years, allowing one full year of overlap with the next LTS release. Java SE subscribers get access to Oracle’s Java SE Support and commercial features such as GraalVM Enterprise, Java Management Service and the Advanced Management Console.

Java 19, Together


As with previous releases, with Java 19 we continue to celebrate the contributions from many individuals and organizations in the OpenJDK Community — we all build Java, together!

JDK 19 Fix Ratio

The rate of change over time in the JDK releases has remained largely constant for years, but under the six-month cadence the pace at which production-ready features and improvements are delivered has vastly improved.

Instead of making tens of thousands of fixes and delivering close to one hundred JEPs (JDK Enhancement Proposals) every few years as we did with yesteryear Major Releases, enhancements are delivered in leaner Feature Releases on a more manageable, predictable six-month schedule. The changes range from significant new features to small enhancements to routine maintenance, bug fixes, and documentation improvements. Each change is represented in a single commit for a single issue in the JDK Bug System.

Of the 19,297 JIRA issues marked as fixed in Java 11 through Java 19 at the time of their GA, 13,825 were completed by people working for Oracle while 5,472 were contributed by individual developers and developers working for other organizations. Going through the issues and collating the organization data from assignees results in the following chart of organizations sponsoring the development of contributions in Java:

Java 19, Core Java, Oracle Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Preparation, Oracle Java Skills, Java Jobs, Java Tutorial and Mateirals

In Java 19, of the 1,962 JIRA issues marked as fixed, 1,383 were completed by Oracle, while 579 were contributed by other members of the Java community.

Oracle would like to thank the developers working for organizations including Alibaba, Amazon, ARM, Huawei, IBM, Intel, JetBrains, NTT Data, Red Hat, SAP and Tencent for their notable contributions. We are also thankful to see contributions from smaller organizations such as Bellsoft, DataDog, Loongson, and Skymatic, as well as independent developers who collectively contributed 6% of the fixes in Java 19.

We are equally grateful to the many experienced developers who reviewed proposed changes, the early adopters who tried out early access builds and reported issues, and the dedicated professionals who provided feedback on the OpenJDK mailing lists. 

The following individuals provided invaluable feedback on build quality, logged good quality bugs, or offered frequent updates:

◉ Uwe Schindler (Apache Lucene)
◉ Martin Grigorov (Apache Tomcat, Apache Wicket)
◉ Rafael Winterhalter (Byte Buddy)
◉ Yoann Rodière (Hibernate ORM, Validator, Search, Reactive)
◉ Marc Hoffman (JaCoCo)
◉ Lukas Eder (JOOQ)

Additionally, through the Quality Outreach program we would like to thank the following FOSS projects and individuals who provided excellent feedback on testing Java 19 early access builds to help improve the quality of the release:

◉ Apache Derby (Rick Hillegas)
◉ Apache Zookeeper (Enrico Olivelli)
◉ BNYM Code Katas (Rinat Gatyatullin)
◉ RxJava (David Karnok)
◉ Apache Johnzon
◉ JobRunr
◉ MyBatis
◉ Renaissance

New in Java 19


Along with thousands of performance, stability and security updates, Java 19 delivers dozens of new features and enhancements, seven enhancements/changes rise to the level of being managed through the JDK Enhancement Proposals - JEPs process, including four preview features and two incubator features.

Some new features not requiring a JEP include new (D)TLS Signature Schemes, support for Unicode 14.0, additional DateTime formats and PAC-RET Protection on AArch64 systems.  Full details of these and many other new features can be found at https://jdk.java.net/19/release-notes.

JEP Preview Features are fully specified and fully implemented Language or VM Features of the Java SE Platform; and yet impermanent. They are made available in JDK Feature Releases to allow for developer feedback based on real-world uses, before them becoming permanent in a future release. This also affords tool vendors the opportunity to work towards supporting features before they are finalized into the Java SE Standard.

JEP Incubator modules allow putting non-final APIs and non-final tools in the hands of developers and users to gather feedback that will ultimately improve the quality of the Java platform.

The seven JEPs delivered with Java 19 are grouped into four categories mapping to key long-term Java technology projects and hardware support.

Project Amber



Improves developer productivity by extending pattern matching to express more sophisticated, composable data queries. This is done by enhancing the Java programming language with record patterns to deconstruct record values. Record patterns and type patterns can be nested to enable a powerful, declarative, and composable form of data navigation and processing.

JEP 405 relates to:

- [JDK 19] JEP 427: Pattern Matching for switch (3rd Preview)


Improves developer productivity by improving Java’s code semantics. This is done by enhancing the Java programming language with pattern matching for switch expressions and statements. Extending pattern matching to switch allows an expression to be tested against a number of patterns, each with a specific action, so that complex data-oriented queries can be expressed concisely and safely.

JEP 427 relates to:

- [JDK 17] JEP 406: Pattern Matching for switch (Preview)
- [JDK 18] JEP 420: Pattern Matching for switch (Second Preview)
- [JDK 19] JEP 405: Record Patterns (Preview)
 

Project Panama



The Foreign Function & Memory API offers value in four unique ways:

Ease of use — Replaces the Java Native Interface (JNI) with a superior, pure-Java development model.

Performance — Provides performance that is comparable to, if not better than, existing APIs such as JNI and sun.misc.Unsafe.

Generality — Provides ways to operate on different kinds of foreign memory (e.g., native memory, persistent memory, and managed heap memory) and, over time, to accommodate other platforms (e.g., 32-bit x86) and foreign functions written in languages other than C (e.g., C++, Fortran).

Safety — Allows programs to perform unsafe operations on foreign memory but warn users about such operations by default.

The Foreign Function & Memory API introduces an API by which Java programs can interoperate with code and data outside of the Java runtime. By efficiently invoking foreign functions (i.e., code outside the JVM), and by safely accessing foreign memory (i.e., memory not managed by the JVM), the API enables Java programs to call native libraries and process native data without the brittleness and danger of JNI.

JEP 424 relates to:

- [JDK 18] JEP 419: Foreign Function & Memory API (Second Incubator)
- [JDK 17] JEP 412: Foreign Function & Memory API (Incubator)


Improves performance achieving performance superior to equivalent scalar computations. This is done by Introducing an API to express vector computations that reliably compile at runtime to optimal vector instructions on supported CPU architectures, thus achieving performance superior to equivalent scalar computations. Vector APIs were incubated in JDK 16, 17, and 18. JDK 19 incorporates feedback from users of those releases as well as performance improvements and implementation enhancements. It uses some of the Foreign Function and Memory APIs which are now mature enough to create this dependency.

JEP 426 relates to:

- [JDK 16] JEP 338: Vector API (Incubator)
- [JDK 17] JEP 414: Vector API (Second Incubator)
- [JDK 18] JEP 417: Vector API (Third Incubator)

Project Loom



Virtual Threads are lightweight threads that dramaticaly reduce the effort of writing, maintaining, and observing high-throughput concurrent applications.

Virtual Threads is the first JEP as part of Project Loom. Project Loom upgrades the Java concurrency model to meet the needs of today’s high-scale server applications.

There are a lot of great things about Java’s threads. They offer a natural programming model, with readable, sequential code using control flow operators that users understand – loops, conditionals, exceptions. Users get great debugging and serviceability, and readable stack traces. And threads are natural units of scheduling for OSes. We want to retain these advantages. 

The problem is that the implementation of threads by the OS is too heavyweight. It takes too long to start a thread for each connection, but worse, the number of threads the OS can support at any one time limits the number of concurrent transactions a server can handle — to well below the capacity of the hardware or the network — and so threads become a severe constraining factor on server throughput.

Many people assumed we would embrace the asynchronous programming style offered by so-called “reactive” frameworks. By not representing concurrent operations directly as threads, it does scale beyond the limits posed by the thread-per-request model, but at a huge cost – much more complicated code that is harder to write, harder to read, and much harder to debug or profile because the platform in all its layers and its tools, is built around threads. Reactive may be the best people can do with the current JVM, but our goal is to do better, which we can do by making threads lighter and more scalable, letting developers keep using the model and tooling they’ve been using successfully for years.

Developers today have three bad options: waste hardware through underutilization, waste programmer effort with worse programming models and observability, or switch away from Java. So, Project Loom offers developers a better option.

Project Loom timeline:

- Late 2017: Work on Loom begins
- Jul 2019: EA build of Fiber prototype released for feedback
- Sept 2019: JEP 353 (Reimplement Legacy Socket API) shipped in JDK 13
- Sept 2020: JEP 373 (Reimplement Legacy DatagramSocket API) shipped in JDK 15
- Nov 2021: Early Access builds of structured concurrency support released for feedback
- Nov 2021: Draft JEPs for virtual threads and for structured concurrency published for comment
- Mar 2022: JEP 418 (Internet Address Resolution SPI) shipped in JDK 18
- Sep 2022: Preview in JDK 19
 

Structured Concurrency simplifies multithreaded programming by introducing an API for structured concurrency. Structured Concurrency treats multipe tasks running in different threads as a single unit of work, thereby streamling error handling and cancellation, improving reliability, and enhancing observability.

New Port



Ports the JDK to the Linux/RISC-V architecture. RISC-V is a free and open-source RISC instruction set architecture (ISA) designed originally at the University of California, Berkeley, and now developed collaboratively under the sponsorship of RISC-V International. It is already supported by a wide range of language toolchains. With the increasing availability of RISC-V hardware, a port of the JDK offers value to developers.

Source: oracle.com

Monday, November 7, 2022

Troubleshooting deadlock in an Apache opensource library

Apache PDFBox is a popular open-source library that facilitates java applications to work with PDF documents. Recently we encountered a Deadlock that surfaced in this library. In this post we have shared how we troubleshooted and identified the root cause of the problem.

What is Deadlock?


First let’s try to understand what ‘Deadlock’ means. Several technical definitions aren’t clear. ‘Deadlock’ definition is one among them :-). Deadlock’s definition goes like this: “Deadlock is a situation where a set of processes are blocked because each process is holding a resource and waiting for another resource acquired by some other process.” It’s always easier to learn something new through examples and pictures. Let’s look at the below practical example, which may help you to understand Deadlock better.

Core Java, Java Exam Prep, Java Tutorial and Materials, Java Prep, Java Preparation, Java Certification, Java Career, Java skill

Core Java, Java Exam Prep, Java Tutorial and Materials, Java Prep, Java Preparation, Java Certification, Java Career, Java skill

Let’s say there is only one train track, and this train track has six parts(part-1, part-2, part-3, part-4, part-5, part-6). Train-A starts at part-1 and Train-B starts at Part-6 on the same train track at the same time. Both trains travel at the same speed. Under this circumstance, Train-A and Train-B will reach a Deadlock state when they reach part-3 and part-4 of the train track. Because when Train-A is in part-3 of the train track, it will be stuck waiting for part-4 of the track, which Train-B holds. On the other hand, when Train-B is in part-4, it will be stuck waiting for part-3, which Train-A holds. Thus, both the trains can’t move forward. This is a classic Deadlock situation. Similarly, once a Deadlock happens in the application, it cannot be recovered. The only way to recover from Deadlock is to restart the application.

Troubleshooting Deadlock


Now let’s discuss about the Deadlock problem we faced in the application. From the above explanation you can understand that Deadlock is caused due to threads. Thus, to analyze Deadlock, you need to capture thread dump from the application. Thread dump is basically a snapshot of all threads that are running in your application. It contains information such as: stack trace, thread state, thread priority, … You can capture thread dump using one of the approaches given here.

Note: Most of the time, you will not know whether the actual problem in your application is deadlock or not. What you will notice is unresponsiveness from the application. Thus, it’s safe to capture all the prominent artifacts that are essential for troubleshooting such as: Garbage Collection log, thread dump, heap dump, netstat, iostat,… you may use yCrash open source script, which would capture 360-degree data (GC log, 3 snapshots of thread dump, heap dump, netstat, iostat, vmstat, top, top -H,…) from your application stack within a minute and generate a bundle zip file.

We uploaded the captured thread dump to fastThread – a thread dump analysis tool. Tool immediately pointed out that the two threads caused the deadlock. Below is the excerpt from the fastThread report.

Core Java, Java Exam Prep, Java Tutorial and Materials, Java Prep, Java Preparation, Java Certification, Java Career, Java skill

Tool pointed out the stack trace of the two threads that were in deadlock. Below is the stack trace of those two threads:

"APP_Thread_50_WorkerTask_pool-5-thread-6" #1899 prio=5 os_prio=0 tid=0x00007f894c405555 nid=0x44d1 waiting for monitor entry [0x00007f88e9c44000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at org.apache.fontbox.ttf.TrueTypeFont.getTable(TrueTypeFont.java:147)
    - waiting to lock <0x00000002d216fec8> (a org.apache.fontbox.ttf.TrueTypeFont)
    at org.apache.fontbox.ttf.TrueTypeFont.getHorizontalMetrics(TrueTypeFont.java:229)
    at org.apache.fontbox.ttf.GlyphTable.getGlyphData(GlyphTable.java:210)
    at org.apache.fontbox.ttf.GlyphTable.getGlyph(GlyphTable.java:191)
    - locked <0x00000002d218ca28> (a org.apache.fontbox.ttf.RAFDataStream)
    at org.apache.fontbox.ttf.TrueTypeFont.getPath(TrueTypeFont.java:676)
    at org.apache.pdfbox.pdmodel.font.PDType1Font.getPath(PDType1Font.java:638)
    at org.apache.pdfbox.rendering.Type1Glyph2D.getPathForCharacterCode(Type1Glyph2D.java:83)
    at org.apache.pdfbox.rendering.PageDrawer.drawGlyph2D(PageDrawer.java:495)
    at org.apache.pdfbox.rendering.PageDrawer.showFontGlyph(PageDrawer.java:476)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showGlyph(PDFStreamEngine.java:787)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showGlyph(PDFStreamEngine.java:805)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showText(PDFStreamEngine.java:743)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showTextString(PDFStreamEngine.java:606)
    at org.apache.pdfbox.Contentstream.operator.text.ShowText.process(ShowText.java:56)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:933)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:514)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:492)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:155)
    at org.apache.pdfbox.rendering.PageDrawer.drawPage(PageDrawer.java:277)
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:347)
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:268)
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:228)
    at xxxxxxx.export.service.thumbnail.PDFSlideGeneratorServiceImpl$1.call(PDFSlideGeneratorServiceImpl.java:60)
    at xxxxxxx.ops.executor.util.ParallelExecutorTask.doCall(ParallelExecutorTask.java:43)
    at xxxxxxx.ops.executor.util.ParallelExecutor.executeTaskFromQueue(ParallelExecutor.java:154)
    at xxxxxxx.ops.executor.util.ParallelExecutor.access$100(ParallelExecutor.java:26)
    at xxxxxxx.ops.executor.util.ParallelExecutor$WorkerTask.doCall(ParallelExecutor.java:164)
    at xxxxxxx.ops.executor.util.UserContextAwareCallable.call(UserContextAwareCallable.java:89)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

"APP_Thread_WorkerTask_APP-pool-5-thread-5" #1898 prio=5 os_prio=0 tid=0x00007f894c898900 nid=0x44d0 waiting for monitor entry [0x00007f88e9d45000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at org.apache.fontbox.ttf.TrueTypeFont.readTable(TrueTypeFont.java:356)
    - waiting to lock <0x00000002d218ca28> (a org.apache.fontbox.ttf.RAFDataStream)
    at org.apache.fontbox.ttf.TrueTypeFont.getTable(TrueTypeFont.java:150)
    - locked <0x00000002d216fec8> (a org.apache.fontbox.ttf.TrueTypeFont)
    at org.apache.fontbox.ttf.TrueTypeFont.getCmap(TrueTypeFont.java:262)
    at org.apache.fontbox.ttf.TrueTypeFont.getUnicodeCmapImpl(TrueTypeFont.java:556)
    at org.apache.fontbox.ttf.TrueTypeFont.getUnicodeCmapLookup(TrueTypeFont.java:541)
    at org.apache.fontbox.ttf.TrueTypeFont.nameToGID(TrueTypeFont.java:629)
    at org.apache.fontbox.ttf.TrueTypeFont.hasGlyph(TrueTypeFont.java:698)
    at org.apache.pdfbox.pdmodel.font.PDType1Font.getNameInFont(PDType1Font.java:601)
    at org.apache.pdfbox.pdmodel.font.PDType1Font.hasGlyph(PDType1Font.java:645)
    at org.apache.pdfbox.rendering.Type1Glyph2D.getPathForCharacterCode(Type1Glyph2D.java:59)
    at org.apache.pdfbox.rendering.PageDrawer.drawGlyph2D(PageDrawer.java:495)
    at org.apache.pdfbox.rendering.PageDrawer.showFontGlyph(PageDrawer.java:476)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showGlyph(PDFStreamEngine.java:787)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showGlyph(PDFStreamEngine.java:805)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showText(PDFStreamEngine.java:743)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.showTextString(PDFStreamEngine.java:606)
    at org.apache.pdfbox.Contentstream.operator.text.ShowText.process(ShowText.java:56)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:933)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:514)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:492)
    at org.apache.pdfbox.Contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:155)
    at org.apache.pdfbox.rendering.PageDrawer.drawPage(PageDrawer.java:277)
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:347)
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:268)
    at org.apache.pdfbox.rendering.PDFRenderer.renderImage(PDFRenderer.java:228)
    at xxxxxxx.export.service.thumbnail.PDFSlideGeneratorServiceImpl$1.call(PDFSlideGeneratorServiceImpl.java:60)
    at xxxxxxx.ops.executor.util.ParallelExecutorTask.doCall(ParallelExecutorTask.java:43)
    at xxxxxxx.ops.executor.util.ParallelExecutor.executeTaskFromQueue(ParallelExecutor.java:154)
    at xxxxxxx.ops.executor.util.ParallelExecutor.access$100(ParallelExecutor.java:26)
    at xxxxxxx.ops.executor.util.ParallelExecutor$WorkerTask.doCall(ParallelExecutor.java:164)
    at xxxxxxx.ops.executor.util.UserContextAwareCallable.call(UserContextAwareCallable.java:89)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

You can see the ‘APP_Thread_50_WorkerTask_pool-5-thread-6’ thread is in Deadlock with ‘APP_Thread_50_WorkerTask_pool-5-thread-5’ thread. From the stacktrace you can observe following two things:

1. ‘APP_Thread_50_WorkerTask_pool-5-thread-6’ has acquired the lock ‘0x00000002d218ca28’ of the ‘org.apache.fontbox.ttf.RAFDataStream’ object and waiting to acquire the lock ‘0x00000002d216fec8’ of the ‘org.apache.fontbox.ttf.TrueTypeFont’ object. 

2. On the other hand, ‘APP_Thread_50_WorkerTask_pool-5-thread-5’ thread is trying to do the exact opposite, it acquired the lock ‘0x00000002d216fec8’ of ‘org.apache.fontbox.ttf.TrueTypeFont’ object and waiting to acquire the lock ‘0x00000002d218ca28’ of the ‘org.apache.fontbox.ttf.RAFDataStream’ object. 

Indeed, it’s a classic Deadlock condition. 

Deadlock in Apache PDFBox

If you notice two objects which are causing Deadlock are ‘org.apache.fontbox.ttf.TrueTypeFont’ and ‘org.apache.fontbox.ttf.RAFDataStream’. Both of these objects are originating from the open source Apache PDFBox library.

Once seeing this bug, we searched in the Apache PDFBox bug database to see whether this problem was already reported or not. We couldn’t see this problem reported earlier. Thus, we went ahead and filed a new ticket in the Apache bug database with the details. Here is the ticket that we filed, for your reference. The Apache PDFBox development team was highly responsive. They started acting on it right away and issued a fix within 2 – 3 days. Great job from the PDFBox team. Truly enjoyed the open source community collaboration.

Source: javacodegeeks.com

Wednesday, November 2, 2022

Taking Java to OCI

Java to OCI, Oracle Java Exam, Oracle Java Prep, Oracle Java Career, Java Jobs, Java Skills, Java Tutorial and Materials

I first started working with Java some time in 1997, which is quite a scary thought in some ways.  In other ways though, it’s quite a comforting thought – the language that I learned then is going strong and continues to grow and evolve.

Today Java is the most widely used software development language and platform in the world.

Since the late 90s, both Java and the world have been through some pretty profound changes.  In terms of IT, one of the most obvious is the rise of cloud.

When we think about the cloud and Java, according to a 2021 VDC study:

◉ Java is viewed as the most important language for cloud.
◉ Java is the #1 language developers expect to be using in three years time.
◉ In 2020 there were 56 billion active JVMs worldwide, 60.5% of them in the cloud.  By 2025, there are predicted to be 82 billion, with 74.2% in the cloud.

So, Java’s footprint in the cloud is big and expanding, and developers are betting on Java to keep themselves relevant and employable.

When we look at Oracle’s cloud, OCI, we see that Java is by far the most popular SDK used by developers

When developers choose to use Java in OCI they get all the benefits of a Java SE Subscription at no additional cost.  This means they can unlock additional value and benefits for themselves and their businesses.

One of these is the Java Management Service.  This helps DevOps teams and administrators understand what Java workloads they have deployed, which JDK version and the origin of the JDK.

If they are using the Oracle JDK they can get additional insight into the security state (whether it needs patching) and the applications that are running on top of it.

Oracle is the steward and #1 contributor to Java and the OpenJDK project.

However, many organisations want more than just the Java binaries that they get with OpenJDK.  They are looking for security, patching, support and expert advice on upgrades and application performance.

They may want the flexibility to carry on running existing applications on older versions of Java, so they can upgrade at their own pace or focus development resources on building new applications rather than upgrading old ones.

They may want to enhance the performance of their applications by running them using the latest versions of Java or GraalVM Enterprise.

For on premises customers, these benefits are available through the Java SE subscription.

For OCI customers they are included OOTB.

So you can migrate existing applications to OCI, or develop new ones on OCI and take advantage of these benefits.

As both a cloud provider and the steward and leading contributor to Java, Oracle is uniquely placed to deliver an optimised Java cloud platform and experience.

I’ve witnessed at first hand some of the joint engineering efforts and I think it’s tremendously exciting.

With the new possibilities that Java and GraalVM provide to developers such as the management service, performance benefits, support for older versions of Java and expert advice from the creators of Java, OCI really is the best cloud for your Java workloads.

If you are interested in building and deploying your Java application on OCI, you can start from this introductory hands-on content repository, including architecture best practice for: microservices-based RESTfull applications; modern web and mobile applications; serverless applications; and an introduction to GraalVM.

When you are ready to start testing Java on OCI, you can access the advanced hands-on content repository, including deployment automation code to set up some of the most popular architectures in a Free Trial Oracle Cloud account (e.g. an Apache Tomcat server connected to MySQL Database Service; a CI/CD pipeline for cloud deployments by using GitHub; microservices with a converged database).

If you need any help with the technical resources in the content repository, feel free to reach out to us via our public slack workspace dedicated to developers – and post your questions to the Java channel.

Source: oracle.com

Friday, October 28, 2022

Curly Braces #5: Null is not nothing

Oracle Java, Oracle Java Exam, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Preparation, Oracle Java Tutorial and Materials


What is null? There’s no easy answer. If this essay were an episode of Seinfeld, it would be called “The Null Column,” because it’s a column about nothing—literally. Upon further inspection, however, null is not nothing. That’s what void is for. In fact, void is a much clearer concept than null. void is pure. void is simple. void is loyal and unwavering. But void is not null. If null were on Facebook, its relationship status would be “it’s complicated” (see Figure 1).

Oracle Java, Oracle Java Exam, Oracle Java Career, Oracle Java Skills, Oracle Java Jobs, Oracle Java Preparation, Oracle Java Tutorial and Materials
Figure 1. Null is a complicated concept.

In the excellent book Zero: The Biography of a Dangerous Idea, which I highly recommend, Charles Seife describes zero as a once-evil concept that was missing from ancient number systems. The idea of zero or nothingness (sounds like null to me) is one of the greatest paradoxes of human thought, according to the author. Its invention and acceptance led to other great discoveries such as fractions and even calculus.

On UNIX, null can be a device, as in /dev/null, where it acts like a black hole when written to; nothing ever comes out. This contrasts with a null modem cable, where all the data certainly does come out; it’s just crossed over electrically.

In common vernacular, some things can be “null and void” at the same time, such as a clause in a contract, a discount offer used on the wrong day or for the wrong person. But that concept never applies in a programming language: There, nothing is both null and void.

Maybe null was best explained in my favorite movie, Tron, During a scene with the evil Sark and one of his underlings, in a fit of anger and disappointment, Sark calls the errant program “Null Unit.” By this, Sark implies emptiness, absence of intelligence, disappointment, or just plain frustration.

Looking back, I started programming at a young age with TI-BASIC, then Commodore BASIC, and then 68000 Assembly for my Amiga (I couldn’t afford a C compiler at the time). None of those has the concept of null. But it was there in spirit because null is a notion, a concept, an abstraction. It’s like love: You cannot precisely define it, but you know it when you have it. Heck, like love, null even causes a feeling, such as that sinking feeling you get when your program crashes from a null pointer (I mean a null reference).

The polyglot null


For programming languages, null means different things depending on the language, and there are different sets of rules that govern how it’s used. For instance, in C, null is zero. Even Brian Kernighan and Dennis Ritchie’s The C Programming Language uses 0 in place of null (well, NULL) in some code examples.

char* alloc(int n) {
    if ( allocbuf + ALLOCSIZE - allocp >= n ) {
        allocp += n;
        return allocp - n;
    } else /* no room */
        return 0;
}

Null is simply defined as zero in a macro.

#define NULL 0

In C, null is also confused with false. The following code compiles:

bool b = false;
int y = 0;
if ( !b && !y ) {
    return 1;
}
return 0;

The snippet above outputs the following:

RUN FINISHED; exit value 1

Perhaps it’s a null pointer constant? Even this C code compiles.

int main(int argc, char** argv) {
    int* p = 0;
    int  q = NULL;
    return q;
}

Yes, the above code may generate a warning, but when it runs, the program exits with the value zero.

RUN FINISHED; exit value 0;

That’s confusing!

In JavaScript, null is null. In fact, you can call it NULL, or Null, or Nil, but don’t call it undefined. That’s an entirely different thing unless you’re checking with == (double equals signs) instead of === (triple equals signs). In this context, let’s simply call null “null-ish.”

Python looks promising, as it uses None to denote null, which at first makes sense. Yet when you see how None behaves, you realize it, too, will let you down. For example, setting a function return to None does what you’d expect: It returns nothing. But if you print the value of a function that returns None, you get the following:

>>> eric=None
>>> print(eric)
None

In Python, printing the value None gives you something, in the form of text that spells out the word None. That’s irony at the level of genius.

What about Java?


Surely Java gets null right, right? Of course, in Java, null is a null reference—except when null is a null pointer.

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Object.toString()" because "ref" is null
     com.ericbruno.nullreference.Test.main(Test.java:11)

Nevertheless, null means something in Java. It’s not void. If your method is defined with a void return, you can’t return anything. Yet if your method is declared to return an Object, you can never return void, but you sure can return null. (You can also return an actual Object, if you want to be boring.)

And I recently learned from Bruce Eckel that null can even be a legitimate case in a Java switch statement.

More precisely, null is not a Java keyword, but instead it is a literal in Java.

According to the Java Language Specification, a literal is a representation of a primitive type, a String type, or the null type.

As the specification states, the null type has one value: the null reference, represented by the literal null. The null type, in contrast, is nameless. As you likely know already, the null literal can be assigned only to reference types, not to primitive types. Although you can cast a null reference to any reference type in Java, the return from instanceof is always false, meaning null is null in Java.

public class Test {
    public static void main(String[] args) {
        Integer i = (Integer)null;
        if ( i instanceof Integer ) {
            System.out.println("i is of type Integer");
        }
    }
}

The result of the code above is no output. Take that, Python!

Source: oracle.com

Friday, October 21, 2022

Curly Braces #4: Network data transmission and compression in Java


I find that writing messaging middleware allows me to build robust distributed enterprise software systems. Once I even built my own Java Message Service (JMS) implementation and learned a lot about Java network I/O and related performance.

One important lesson I learned is high-performance messaging software requires more than fast, efficient code. You also need a strong fundamental understanding of networking—and knowledge about the I/O limitations of the systems you run on. For example, when data is transmitted between distributed components, there comes a point where even the fastest code waits on network I/O—what’s called being I/O bound. Figure 1 shows what I mean.

Oracle Java, Java Exam, Java Exam Prep, Java Tutorial and Material, Java Guides, Java Certification, Java Prep, Java Preparation, Java Learning

Figure 1. An I/O-bound thread

Before getting into data compression as one possible solution to addressing I/O delays, I’ll review some basics of Java network programming.

Java network programming


The foundation for Java networking is the combination of the java.net.Socket and java.net.ServerSocket classes. In short, Socket is for client code, while ServerSocket is for servers, which clients connect to.

As with most Java I/O programming, data is exchanged through a combination of the java.io.InputStream and java.io.OutputStream classes. Note that the use of server and client classes doesn’t dictate data direction. A Java server application can sit and listen for a client to send it data after connecting, or the server can serve data to a client that just listens. And of course, data can flow both ways between the two in a request and response paradigm, as with a web browser and server.

Here is a sample implementation of ServerSocket that waits for a client to connect. A client can be any application, regardless of implementation language, that connects to the correct IP address and port.

try {
        ServerSocket clientConnect = new ServerSocket(8080);
        Socket client = clientConnect.accept(); // blocking call!
        InputStream instream = client.getInputStream();
        DataInputStream dis = new DataInputStream( instream );
        while ( true ) {
            String msg = dis.readUTF();
            System.out.println("Message: " + msg);
        }
    }
    catch ( Exception e ) {
        e.printStackTrace();
    }

The code above creates a ServerSocket that listens on port 8080 on the host it runs on. The call to accept() blocks and waits until a network client connects on the listening port, at which point a Socket connection to the client is returned.

In this implementation, the server listens for String messages and outputs them to the command line. To do this, the client’s InputStream is passed to the DataInputStream constructor to instantiate a listener. The subsequent call to readUTF blocks until a String message arrives in its entirety.

Here’s the simplified client code that connects to the server and sends a String message.

try {
        Socket sender = new Socket("localhost", 8080);
        if ( sender.isConnected() ) {
            DataOutputStream outputStream =
                    new DataOutputStream( conn.getOutputStream() );
            
            outputStream.writeUTF("I love Oracle Java Magazine!");
        }
    }
    catch ( Exception e ) {
        e.printStackTrace();
    }

At this point, it’s important to know the expected application-level protocol. In the example above, Java String data is sent over the network. However, other Java Object data can be serialized and sent over the network using ObjectOutputStream and ObjectInputStream classes, as follows:

try {
        Socket sender = new Socket("localhost", 8080);
        if ( sender.isConnected() ) {
            ObjectOutputStream oos = 
              new ObjectOutputStream( 
                new BufferedOutputStream( sender.getOutputStream() ));
                    
            MyObject myObj = new MyObject();
            myObj.message = "I love Java!";
            myObj.messageId = getMessageId();
            // ...
            oos.writeObject( myObj );
            oos.flush();
        }
    }
    catch ( Exception e ) {
        e.printStackTrace();
    }

The listener on the other side connects as shown earlier, but it makes the blocking call wait for a serialized Java object to be returned.

ObjectInputStream ois =
    new ObjectInputStream( 
        new BufferedInputStream( client.getInputStream() ));
                
    MyObject myObject = (MyObject)ois.readObject();

Again, the protocol here is that both client and server agree to send serialized instances of MyObject objects over the network. The use of buffered I/O—using the BufferedOutputStream object—generally improves performance because the JVM efficiently handles the assembly of bytes into an Object internally.

Let’s talk about performance. My experience shows that as an application spends more time sending data over the network, CPU utilization will decrease, which means tuning your network application on a faster server won’t do much good. Instead, you need to improve your network I/O. A server with faster I/O capabilities might help, but that too will become saturated. You need to improve the design, and that means improving the code.

One improvement is to compress the data before it’s sent using a lossless algorithm (so you get back the original bytes). If you have an I/O-bound server, you can afford to spend some CPU processing time compressing the data, which will result in reduced network I/O.

By the way, this is one reason why web servers typically transmit images in compressed formats such as JPEG, because the picture consumes less I/O and bandwidth. When JPEG is used, however, the compression is lossy, so the uncompressed image is not precisely the same as the original. Lossy compression is fine for casual website viewing but is not acceptable for data processing.

Compressing the bytes

The JDK java.util.zip package provides classes for compressing and decompressing data, creating .zip and .gzip files, and much more. For this project, the appropriate classes are Deflater and Inflater, which compress and decompress bytes respectively. I’ll start by choosing the following compression algorithm:

Deflater compressor = new Deflater(Deflater.BEST_SPEED);

This compression algorithm prioritizes speed of execution—which uses minimal CPU resources but also results in less compression, that is, a larger output file. If you want as much compression as possible, which may require more processing time to compress the bytes, use Deflater.BEST_COMPRESSION. These compression options are part of a range you can use to balance the compression-to-speed ratio depending on your application, data type, data size, or other factors; you can see them all here in the “Field Summary” section.

Here is a sample sender that uses data compression.

DataOutputStream dos = 
    new DataOutputStream( conn.getOutputStream() );
byte[] bytes = messageTxt.getBytes("UTF-8");

// Compress the bytes
Deflater compressor = new Deflater(Deflater.BEST_SPEED);
compressor.setInput(bytes);
compressor.finish();
byte[] compressed = new byte[bytes.length];
length = compressor.deflate(compressed);

// Send the compressed data 
dos.write(compressed, 0, length);
dos.flush();

The code begins in a straightforward fashion, with a DataOutputStream and some message text. Assume the message is long, so there are many bytes to transmit.

Then it creates a Deflater set for best processing speed. The example above calls set Input to add bytes, and then it calls the finish() method. The class can also work with data streams. The subsequent call to deflate() compresses the bytes into the provided array, and the new (smaller) length is returned. Finally, the compressed bytes are sent over the network.

In one test application, I created messages of around 100 KB, and they each compressed down to just over 500 bytes. This is a significant savings in terms of network I/O time and bandwidth!

The following code reads and decompresses the bytes on the receiving end:

// Read the bytes
DataInputStream dis = new DataInputStream( instream );
byte[] compressed = new byte[ dis.available() ];
dis.readFully(compressed);

// Decompress the bytes
Inflater decompressor = new Inflater();
decompressor.setInput(compressed);
byte[] msgBytes = new byte[DEFAULT_SIZE];
decompressor.inflate(msgBytes);

String msg = new String(msgBytes);
System.out.println(msg);

First, a byte array is created to store the incoming bytes. Next, the Inflater class is used. The setInput() method is called to provide the compressed bytes, and then inflate() is called to decompress the bytes into the provided array. The resulting bytes are used to re-create the original string.

Adding flexibility and predictability

The process above works fine, but I have ideas for two improvements. The first is to add flexibility to compress the data only when that makes sense and not when it’s unnecessary. The second is to transmit the size of the byte array required for decompressing the string.

In my opinion, using getRemaining() and other Inflater methods to read the data in chunks is inefficient and complicated. I find it’s best to send both the uncompressed and compressed data sizes as int values in the data stream itself. In other words, the bits that arrive look like what’s shown in Table 1.

Table 1. Starting and ending bit sizes

Oracle Java, Java Exam, Java Exam Prep, Java Tutorial and Material, Java Guides, Java Certification, Java Prep, Java Preparation, Java Learning

Determining the sizes allows you to provide runtime flexibility in terms of compressing data only under the right conditions. For example, you can base your compression decisions on message size; if it’s only a few bytes, you don’t need to bother.

The enhanced sender code looks like the following:

DataOutputStream dos = 
    new DataOutputStream( conn.getOutputStream() );
byte[] bytes = messageTxt.getBytes("UTF-8");

// Write the original message length
int length = bytes.length;
dos.writeInt(length);

if ( length > LENGTH_THRESHOLD ) {
    // Compress the bytes
    Deflater compressor = new Deflater(Deflater.BEST_SPEED);
    compressor.setInput(bytes);
    compressor.finish();
    byte[] compressed = new byte[bytes.length];
    length = compressor.deflate(compressed);
}
else {
    compressed = bytes;
}

// Write the length again. If it was compressed, the
// sizes will vary, and this is the indicator that
// the data needs to be decompressed by the receiver
dos.writeInt(length);

// Write the data bytes
dos.write(compressed, 0, length);
dos.flush();

Of course, the receiver needs to change as well. The updated code is shown below.

DataInputStream dis = new DataInputStream( instream );

// Get the length of the next message
int msgSize = dis.readInt();

// Get the compressed size (if it's compressed
// this size will vary from the size above)
int compressedSize = dis.readInt();

// Read the bytes
byte[] compressed = new byte[compressedSize];
dis.readFully(compressed);

byte[] msgBytes = compressed;
if (compressedSize != msgSize) {
    // Decompress the bytes
    Inflater decompressor = new Inflater();
    decompressor.setInput(compressed);
    msgBytes = new byte[DEFAULT_SIZE];
    decompressor.inflate(msgBytes);
}

String msg = new String(msgBytes);
System.out.println(msg);

As you can see, the changes are minimal, but the result is very flexible and efficient code to decidedly and deterministically compress data to reduce I/O and network overhead when optimization criteria are met.

Source: oracle.com

Wednesday, October 19, 2022

Curly Braces #3: Let’s have fun with Java arrays

Elegant array development might encompass reflection, generics, and lambdas.

I was recently chatting with a colleague who develops in C. The conversation came to arrays and how very different they work in Java compared to C—which he found surprising given that Java is considered a C-like language.

Oracle Java arrays, Oracle Java Certification, Java Prep, Java Preparation, Java Tutorial and Materials, Java Guides, Java Cert Exam

This exploration of Java arrays starts simple but quickly gets interesting, especially if you studied or use C.

Declaring an array

If you follow a tutorial on Java, you’ll see there are two ways to declare an array. The first is straightforward.

int[] array; // a Java array declaration

You can see how it differs from C, where the following is the proper syntax:

int array[]; // a C array declaration

Focusing now on Java, after declaring an array you need to allocate it.

array = new int[10]; // Java array allocation

Can you declare and initialize an array in one step? Well, you cannot take a shortcut and do the following:

int[10] array; // NOPE, ERROR!

However, you can declare and initialize an array in one step if you already know the values.

int[] array = { 0, 1, 1, 2, 3, 5, 8 };

What if you don’t know the values? Here’s the code you’ll encounter more often to declare, allocate, and use an int array.

int[] array;

array = new int[10];

array[0] = 0;

array[1] = 1;

array[2] = 1;

array[3] = 2;

array[4] = 3;

array[5] = 5;

array[6] = 8;

...

Notice I specified an int array, which is an array of Java primitive data types. Let’s see what happens if you try the same process with an array of Java objects instead of primitives.

class SomeClass {

    int val;

    // …

}

SomeClass[] array = new SomeClass[10];

array[0].val = 0;

array[1].val = 1;

array[2].val = 1;

array[3].val = 2;

array[4].val = 3;

array[5].val = 5;

array[6].val = 8;

If you run the code above, you’ll get an exception as soon as you try to use the first array element. Why? Although the array is allocated, the array buckets each contain null object references. If you type this code into your IDE, it will even autocomplete the .val for you, so the error can be confusing. To resolve the error, do the following:

SomeClass[] array = new SomeClass[10];

for ( int i = 0; i < array.length; i++ ) {  //new code

    array[i] = new SomeClass();             //new code

}                                           //new code

array[0].val = 0;

array[1].val = 1;

array[2].val = 1;

array[3].val = 2;

array[4].val = 3;

array[5].val = 5;

array[6].val = 8;

That is not elegant. Indeed, it has often frustrated me that I cannot more easily allocate the array, and the objects within the array, by writing less code, maybe even all in one line.

Thus, I did some experimenting.

Finding Java array nirvana

The goal here is coding elegance, not to be a purist. Smells like “clean code” spirit! And in that spirit, I set out to create some reusable code to clean up the array allocation pattern. Here’s a first attempt.

public class MyArray {

    public static Object[] toArray(Class cls, int size) 

      throws Exception {

        Constructor ctor = cls.getConstructors()[0];

        Object[] objects = new Object[size];

        for ( int i = 0; i < size; i++ ) {

            objects[i] = ctor.newInstance();

        }

        return objects;

    }

    public static void main(String[] args) throws Exception {

        SomeClass[] array1 = (SomeClass[])MyArray.toArray(SomeClass.class, 32); // see this

        System.out.println(array1);

    }

}

The one line of code marked as “see this” is elegant and looks just the way I wanted, thanks to the implementation of toArray. This approach uses reflection to find the default constructor for the provided class, and then it calls that constructor to instantiate an object of this class. The process calls the constructor once for each element of the array. Brilliant!

Too bad it doesn’t work.

The code compiles fine but results in a ClassCastException when you run it. To use this code, you need to create an array of Object elements and then cast each array element to class SomeClass, as follows:

Object[] objects = MyArray.toArray(SomeClass.class, 32);

SomeClass scObj = (SomeClass)objects[0];

...

That isn’t elegant! After more experimentation, I evolved several solutions that use reflection, generics, and lambdas.

Solution 1: Use reflection

The answer to the issues above is to use the java.lang.reflect.Array class to instantiate an array of the class you specify, instead of using the base java.lang.Object class. This is essentially a single-line code change that gets closer to the goal.

public static Object[] toArray(Class cls, int size) throws Exception {

    Constructor ctor = cls.getConstructors()[0];

    Object array = Array.newInstance(cls, size);  // new code

    for ( int i = 0; i < size; i++ ) {

        Array.set(array, i, ctor.newInstance());  // new code

    }

    return (Object[])array;

}

You can use this approach to get an array of the class you desire, and then operate on it as follows:

SomeClass[] array1 = (SomeClass[])MyArray.toArray(SomeClass.class, 32);

Although it’s not a necessary change, the second line was modified to use reflection’s Array class to set each array element’s content. This is great! But there’s one more detail that does not feel quite right: That cast to SomeClass[] isn’t elegant. Fortunately, there is a solution with generics.

Solution 2: Use generics

The Collections framework uses generics to be type-specific and eliminate casts in many of their operations. You can use generics here as well. Take java.util.List, for example.

List list = new ArrayList();

list.add( new SomeClass() );

SomeClass sc = list.get(0); // Error, needs a cast unless...

The third line in the above snippet will result in an error, unless you update the first line as follows:

List<SomeClass> = new ArrayList();

You can achieve the same result using generics in the MyArray class. Here’s the new version.

public class MyArray<E> {

    public <E> E[] toArray(Class cls, int size) throws Exception {

        E[] array = (E[])Array.newInstance(cls, size);

        Constructor ctor = cls.getConstructors()[0];

        for ( int element = 0; element < array.length; element++ ) {

            Array.set(array, element, ctor.newInstance());

        }

        return arrayOfGenericType;

    }

}

// ...

MyArray<SomeClass> a1 = new MyArray(SomeClass.class, 32);

SomeClass[] array1 = a1.toArray();

This looks good. By using generics and including the target type in the declaration, the type can be inferred in other operations. In fact, this code can be reduced to a single line, as follows, if you choose:

SomeClass[] array = new MyArray<SomeClass>(SomeClass.class, 32).toArray();

Mission accomplished, right? Well, not quite. This is fine if you don’t care which class constructor you’re calling, but if you want to call a specific constructor, this solution falls short. You can continue to use reflection to solve this problem, but the code may get complex. Fortunately, lambdas offer another solution.

Solution 3: Use lambdas

I’ll admit, I was slow to adopt lambdas, but I’ve grown to appreciate their value. In particular, I’ve come to appreciate the java.util.stream.Stream interface, which processes collections of objects. Stream helped me achieve Java array nirvana.

Here’s my first attempt to use lambdas.

SomeClass[] array = 

    Stream.generate(() -> new SomeClass())

    .toArray(SomeClass[]::new);

I broke this code across three lines for readability. You can see it checks all the boxes: It is simple and elegant, creates a populated array of instantiated objects, and allows you to call a specific constructor.

Notice the parameter to the toArray method: SomeClass[]::new. This is a generator function used to allocate an array of the specified type.

However, as it stands, this code has a minor issue: It creates an array of infinite size. That is suboptimal. Fortunately, this can be resolved by calling the limit method.

SomeClass[] array = 

    Stream.generate(() -> new SomeClass())

    .limit(32)   // calling the limit method

    .toArray(SomeClass[]::new);

The array is now limited to 32 elements. You can even set specific object values for each array element, as in the following:

SomeClass[] array = Stream.generate(() -> {

    SomeClass result = new SomeClass();

    result.val = 16;

    return result;

    })

    .limit(32)

    .toArray(SomeClass[]::new);

This code demonstrates the power of lambdas, but the code is not neat and compact. Calling a different constructor to set the value is much cleaner, in my opinion.

SomeClass[] array6 = Stream.generate( () -> new SomeClass(16) )

    .limit(32)

    .toArray(SomeClass[]::new);

I like the lambda-based solution, which is ideal when you need to call a specific constructor or operate on each array element. When I need something more basic, I tend to use the solution based on generics since it’s simpler. However, you can see that lambdas provide an elegant and flexible solution.

Source: oracle.com