Wednesday, December 29, 2021

A peek into Java 17: Encapsulating the Java runtime internals

The need to encapsulate the runtime is fundamentally caused by Java’s nature as an open programming environment.

Download a PDF of this article

If you’ve ported an application to Java 11, then you’re probably familiar with the following scary-sounding message or something like it:

Java 17, Java Runtime Internals, Oracle Java Exam, Oracle Java Prep, Java Career, Core Java, Oracle Certified

WARNING: An illegal reflective access operation has occurred

WARNING: Illegal reflective access by ReflBytecodeName (file:/Users/ben/projects/books/resources/) to method sun.invoke.util.BytecodeName.parseBytecodeName(java.lang.String)

WARNING: Please consider reporting this to the maintainers of ReflBytecodeName

WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations

WARNING: All illegal access operations will be denied in a future release

While that warning is not as famous as “It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire,” these warnings provide a highly recognizable opening crawl to the logs of most modern Java applications. Many developers have even become desensitized to it—and that hides a problem that will potentially affect large numbers of applications soon.

But what does the message mean?

The error message above exists to warn of the slow, inexorable progress of a long-term project within the Java ecosystem: to strongly encapsulate the internals of the Java runtime.

The encapsulation project was originally intended to form part of Java 8, but it was late for the train, and instead the first steps towards encapsulation were delivered in Java 9.

The need to encapsulate the runtime is fundamentally caused by Java’s nature as an open programming environment. This long-ago design decision has had unintended consequences.

Note that the phrase “open programming environment” can be used in several different ways, so I need to be clear about the specifics of its meaning in this context. Specifically, it means the following:

◉ In Java 8 and before, you can call public methods on any public class you like, both directly and reflectively.

◉ After the Java module system arrived, these calls became subject to additional restrictions.

Those restrictions represented a fundamental change in the way Java access control works, but it might not seem like it. If you’re a Java developer who plays by the rules, you have never called an API in an internal package directly. However, you might well have used a library or a framework that does, so it’s good to understand what has changed behind the scenes.

Encapsulation of direct access

Here is an example of direct access to internal classes, similar to the way that some libraries might use it, using a piece of Java 8 code that extends an internal class to get access to a low-level URL canonicalizer.

A URL canonicalizer is a piece of code that takes a URL in one of the various forms permitted by the URL standard and converts it to a standard (canonical) form. The intent is that canonical URLs can act as a single source of truth for the location of content that can be accessed via multiple, different possible URLs.

By the way, the familiar java.net.URL class does some canonicalization, and for ordinary uses this should be sufficient because your code should never access internal classes directly. However, library authors may need to have better, more-specific control over how some aspects of canonicalization are handled.

The following code is for demonstration purposes only, to provide a concrete example to discuss access control:

import sun.net.URLCanonicalizer;

public class MyURLHandler extends URLCanonicalizer {

    public boolean isSimple(String url) {

        return isSimpleHostName(url);

    }

}

If you try to compile it using Java 8, javac warns that the code is accessing an internal API.

$ javac src/ch02/MyURLHandler.java

src/ch02/MyURLHandler.java:3: warning: URLCanonicalizer is internal proprietary API and may be removed in a future release

import sun.net.URLCanonicalizer;

              ^

src/ch02/MyURLHandler.java:5: warning: URLCanonicalizer is internal proprietary API and may be removed in a future release

public class MyURLHandler extends URLCanonicalizer {

                                  ^

2 warnings

Despite the warnings, the compiler still allows the access. The result is a user class that is tightly coupled to the internal implementation of the JDK.

If enough developers abuse this openness, this leads to a situation in which it is difficult or impossible to make changes to the internals, because to do so would break deployed libraries and applications. This is one of the problems that modules were invented to solve.

Let’s see what happens when you try to compile that code under Java 11.

$ javac src/ch02/MyURLHandler.java

src/ch02/MyURLHandler.java:3: error: package sun.net is not visible

import sun.net.URLCanonicalizer;

          ^

  (package sun.net is declared in module java.base, which does not export it to the unnamed module)

src/ch02/MyURLHandler.java:8: error: cannot find symbol

        return isSimpleHostName(url);

               ^

  symbol:   method isSimpleHostName(String)

  location: class MyURLHandler

2 errors

The messages above aren’t warnings; they are errors. The form of the error message explicitly says that the sun.net package is now invisible.

These changes have the most impact for library developers. Most application developers shouldn’t need to do anything more than upgrade to newer versions of the libraries they depend upon. In fact, this can even be a good thing, because more recent versions are probably more secure and performant and have more features.

Encapsulation for modular JDKs

The new reality is that when Java code is compiled with a modular JDK (all JDKs from version 9 onwards are modular), only methods on exported packages are accessible. It is no longer the case that a public method on a public class is automatically accessible to all code everywhere.

In other words, the platform can finally enforce the long-standing convention that in the JDK, a package that starts java or javax is a public API and everything else is internal-only.

In Java 8 and before, the convention is just that: a convention. Until the arrival of modules, there was no VM or class loading mechanism that enforced that, as I’ve shown.

The basic semantics of the Java module system close off the ability for libraries and applications to link directly to the JDK internals. This means that all applications that have upgraded from Java 8 are already safer—because they are guaranteed only to directly access the JDK via its public API.

However, when it has suited them, programmers have also coupled to the internals using reflection, and this aspect of encapsulation is more complex.

The treatment of reflection is at the heart of what the high-level log message means. To explore what the message means in detail, I’ll explain how modules impact reflection.

Java has supported reflection since almost the very beginning of its existence. Reflection allows you to access and work with types indirectly and, at runtime, in a way that does not require compile-time knowledge of those types. This flexibility is at the heart of many of the most popular Java frameworks and libraries.

As you have already seen, modules add a new concept to Java’s access control model—the idea of exporting a package—which declares a package to be part of the module’s API.

Reflection, however, is not direct linkage. The Java Reflection API has always had a huge encapsulation hole in the form of a method called setAccessible().

The setAccessible() method can be called on an object that represents, for example, a method on an unknown (at compile time) class. The method tells the JVM that it should skip access-control checks when trying to access or call the underlying (unknown at compile time) method. This method is very useful for framework designers, but it represents a major leakage of encapsulation safety. Modules needed to address this case as well.

Reflective access to modularized code

In general, modules can declare their reflective access policy as part of their module-info.class, which is done with the opens keyword. The intent is that, by default, only exported packages can be accessed reflectively, but the designers of the modules system realized that sometimes developers want to give reflective (but not direct) access to certain packages.

Thus, the opens keyword allows a module to declare that a certain set of packages is available for reflective access, even if the packages are not part of the module’s public API. Developers can also specify fine-grained access by using the syntax opens ... to ... allow a named set of packages to be opened reflectively to specific modules but not more generally.

I’ll make this concept more concrete with an example that uses an internal utility method to parse the bytecode name of a method. For the sake of this example, suppose I’m building a library and want it to build on all Java versions from 8 through 15 without the compiler errors shown earlier. To do that, I’ll refer to the class sun.invoke.util.BytecodeName indirectly via reflection.

Here is the reflective code. It compiles without error because it avoids the direct linkage.

// Exception handling elided

Class<?> clz = Class.forName("sun.invoke.util.BytecodeName");

Method method = clz.getDeclaredMethod("parseBytecodeName", String.class);

Object res = method.invoke(null, "java/lang/String");

System.out.println(Arrays.toString((Object[])res));

The output when the code is run under Java 8 is straightforward.

$ java ReflBytecodeName

[java, /, lang, /, String]

However, if the code is run under Java 11, the results are not as happy.

$ java ReflBytecodeName

WARNING: An illegal reflective access operation has occurred

WARNING: Illegal reflective access by ReflBytecodeName (file:/Users/ben/projects/books/resources/) to method sun.invoke.util.BytecodeName.parseBytecodeName(java.lang.String)

WARNING: Please consider reporting this to the maintainers of ReflBytecodeName

WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations

WARNING: All illegal access operations will be denied in a future release

[java, /, lang, /, String]

These are, of course, the same warning messages highlighted at the start of this article.

The change in behavior arrived with Java 9 as part of the ecosystem moving towards proper encapsulation—which includes reflection. This change encodes the message that, in a future version of Java, the currently permissive usage will be disallowed. The helpful message also calls out the command-line switch --illegal-access as the user-level control.

The expressed intent is for that command-line switch to eventually default to deny instead of permit, which has been the default setting in use since Java 9.

What changed in Java 16?

The change to reflective access obviously cannot happen overnight, because if the reflection switch were suddenly set to deny, huge swaths of the Java ecosystem would break and nobody would upgrade.

Java 9 was released in September 2017. Java 11 was released a year later, in September 2018. If the expressed intent was to eventually remove the backdoor reflective access to the JDK’s internals, it is legitimate to ask: “How long will this continue? How much warning is enough?”

Here’s the answer: In Java 16 (released in March 2021), the situation changed. If you rerun the test app under Java 16, you’ll see the following message:

$ java ReflBytecodeName

java.lang.IllegalAccessException: class ReflBytecodeName cannot access class sun.invoke.util.BytecodeName (in module java.base) because module java.base does not export sun.invoke.util to unnamed module @324e4822

  at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:385)

  at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:687)

  at java.base/java.lang.reflect.Method.invoke(Method.java:559)

  at ReflBytecodeName.run(ReflBytecodeName.java:22)

  at ReflBytecodeName.main(ReflBytecodeName.java:12)

The previous warning is now an error. As of Java 16, the default permission for reflective access to the JDK internals has changed to deny (from previously permitting the access and issuing a warning). This is explained in detail by JEP 396: Strongly encapsulate JDK internals by default, which codifies the change.

This means that, unlike other recent Java feature releases, Java 16 has an additional barrier to adoption, whether you are moving from Java 8 or from Java 11.

(By the way, just to be clear, the package sun.invoke.util is not an officially supported or public package like java.*, so this example was focused on effectively demonstrating how the convention that “everything else is internal-only” is now rather more than a convention.)

As always in the Java ecosystem, the ramifications of a change like this are not limited to just what your application itself does explicitly but also to the behavior of the libraries you depend upon and all those libraries’ transitive dependencies.

This change means that, without user intervention (such as upgrading library versions), applications that depend on libraries that still leverage encapsulation-breaking access to the internals will now stop working.

In turn, this change means that before you and your team upgrade to Java 16 (and soon, to Java 17), you will need to ensure that the frameworks you depend upon have been certified to work with the new version of Java.

What’s changing in Java 17?

In Java 16, it is still possible to restore the situation that existed previously by using the --illegal-access command-line switch to allow general reflective access to the JDK internals. However, in Java 17, the situation changes again: This release removes that command-line switch. This topic is covered in detail by JEP 403: Strongly encapsulate JDK internals.

To summarize this somewhat complex situation, see Table 1, which shows the version changes in the permitted access to JDK internals.

Java 17, Java Runtime Internals, Oracle Java Exam, Oracle Java Prep, Java Career, Core Java, Oracle Certified
Table 1. Permitted access to internals by Java version

What about Unsafe?


Unfortunately, for some developers, this closing off of reflective access has been conflated with a discussion of the famous (or possibly notorious) class sun.misc.Unsafe.

In reality, Unsafe has long been recognized as a special case, and it is handled separately from the new access controls discussed in this article, but for the sake of completeness I’ll explain how strong encapsulation relates to Unsafe and its usage.

In Java 9, the core runtime, rt.jar, was split up and, in particular, the actual implementation code of sun.misc.Unsafe (and some related code referred to as critical internal APIs, as defined in JEP 260: Encapsulate most internal APIs), was moved out of the core and into a separate module, jdk.unsupported. The naming is quite deliberate. The use of “jdk” rather than “java” as a prefix warns developers that by using these APIs, they are consciously choosing to delve into the internals, and “unsupported” speaks for itself.

(Note: jdk.unsupported is visible to code on the classpath by default, so sun.misc.Unsafe is, in practice, just as available in Java 9+ as it was in Java 8.)

The jdk.unsupported module is intended to provide a bridge for library developers, allowing them to migrate to fully supported APIs over time, without creating a hard cutoff and subsequent cliff.

Here’s a very brief look at the current implementation of this bridge in jdk.unsupported. While developers cannot rely upon this implementation, it provides an interesting example of a real-world code migration technique that may be a useful reference for anyone thinking of refactoring code to modules.

For sun.misc.Unsafe, the implementation currently works like this.

public final class Unsafe {

    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();
    private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();

    // ...
}

Calls are forwarded to the real implementation of Unsafe, which lives in jdk.internal.misc in the protected part of the java.base module, as in the following:

public int getInt(Object o, long offset) {
        return theInternalUnsafe.getInt(o, offset);
    }

Leaving aside the details of the implementation, ordinary Java applications cannot directly access the Unsafe object (nor should they try). This code

Unsafe unsafe = Unsafe.getUnsafe();

will compile but will throw a SecurityException at runtime—and this is as true in Java 11 as it is in Java 8. Only trusted code inside the JDK can get direct access to the Unsafe object.

This is by design. Unsafe is potentially very dangerous because it provides a way to do certain things that are otherwise impossible in the Java platform. One goal of the JDK developers at Oracle is to mitigate this danger by creating official, supported public APIs to provide capabilities that are currently supplied by Unsafe.

Two great examples of this mitigation project are

◉ Hidden classes (delivered in Java 15), which replaced Unsafe::defineAnonymousClass.
◉ Off-heap memory access, which can now be achieved using the now-incubating JEP 412: Foreign function and memory API

The goal is that, over time, neither application programmers nor library developers should need to use the methods that Unsafe provides. However, until that future arrives, the well-established reflective tricks used by library developers to get hold of a reference to an Unsafe object still work, for example

Field f = Unsafe.class.getDeclaredField("theUnsafe");

f.setAccessible(true);

Unsafe unsafe = (Unsafe) f.get(null);

This code still works in Java 16, and it will continue to work in Java 17, because the jdk.unsupported module is declared as both exports and opens:

module jdk.unsupported {

    exports com.sun.nio.file;

    exports sun.misc;

    exports sun.reflect;

    opens sun.misc;

    opens sun.reflect;

}

The code is, therefore, unaffected by the changes to strong encapsulation that formed part of these releases.

Finally, although there are some capabilities that are still not addressed by fully supported replacements, the amount of code that is present in jdk.unsupported is now fairly small.

To get some indication of how little of that old code is actually left, you can download a copy of the current OpenJDK source code from GitHub. If you navigate to the directory containing the source for jdk.unsupported (jdk/src/jdk.unsupported/share/classes), doing a quick bit of Linux shell scripting can provide an idea of how much code remains.

$ find . -iname "*.java" | xargs -I% wc %

      56     294    1944 ./sun/misc/SignalHandler.java

    1290    6486   50401 ./sun/misc/Unsafe.java

     235    1003    8361 ./sun/misc/Signal.java

     226    1152    9684 ./sun/reflect/ReflectionFactory.java

      34     224    1357 ./module-info.java

      65     301    2228 ./com/sun/nio/file/SensitivityWatchEventModifier.java

      48     263    1753 ./com/sun/nio/file/ExtendedWatchEventModifier.java

      48     265    1761 ./com/sun/nio/file/ExtendedCopyOption.java

      77     461    3117 ./com/sun/nio/file/ExtendedOpenOption.java

For those unfamiliar with the Linux wc command, the first column gives the number of lines of code in each source file. There are about 35 lines of license information at the start of each file. This means that, with the exception of sun.misc.Unsafe, these files are really very small.

Counting lines of code is, of course, an imperfect measure for the complexity of the tasks that remain to fully remove these files, but it does provide a simple metric that can be tracked from release to release.

The use of an unsupported module is obviously not ideal, but it’s clear that, over time, replacements for these remaining pieces should emerge and the jdk.unsupported module will shrink and eventually disappear.

Source: oracle.com

Monday, December 27, 2021

11 great Java tricks from dev.java

The dev.java website is a huge resource for all Java developers. Here are 11 tricks collected from the site.

Download a PDF of this article

Trick #1: Compact record constructors

You know how to use records to model data carriers and how to verify incoming data during construction, but do you know that you don’t have to list the parameters of all the record constructors?

A record’s canonical constructor has one argument per component but in its compact form, you don’t have to list all the arguments. You can’t assign fields—that happens in compiler-generated code after yours—but you can reassign the parameters, which leads to the same result.

public record Range(int start, int end) {

    public Range {

        if (end <= start) {

            throw new IllegalArgumentException();

        }

    }

}

Trick #2: Serializing records

You know that every object can be serialized using black magic, but do you know that no such deviance is needed for records? The guaranteed presence of constructor parameters and accessors makes serialization work with the object model—and makes it easy for you to create reliable serializable records.

Trick #3: jpackage versus modules

You know jpackage, but wait: Do you really know jpackage?

jpackage is a command-line tool that takes an entire Java app as input and produces a fully self-contained application image, meaning it includes your code, dependencies, and a Java runtime. jpackage creates the runtime for your app with jlink, which you can fully configure through jpackage, or you can pass it the path to a runtime image that you already created.

Other configuration options for jpackage include the ability to specify application metadata such as icons and licenses, installation options, and launchers as well as JVM and program options.

jpackage produces output in platform-specific formats such as deb and rpm for Linux or exe and msi for Windows.

Now that you know jpackage, do you know that it can do all of that for modular as well as nonmodular applications?

# generate an application image for a modular application:

jpackage --type app-image -n name -p modulePath -m moduleName/className

# for a nonmodular application:

jpackage --type app-image -i inputDir -n name --main-class className --main-jar myJar.jar

Trick #4: Cross-OS runtime images

Speaking of jlink, do you know that you can use it to create runtime images across operating systems?

Say your build server runs Linux and you need a Windows runtime image. You simply need to download and unpack a Windows JDK of the same version as the Linux one that runs jlink, and then add the Windows jmods folder to the Linux jlink executable’s module path.

# download JDK for Windows and unpack into jdk-win

# create the image with the jlink binary from the system's JDK

# (in this example, Linux)

$ jlink

  --module-path jdk-win/jmods:mods

  --add-modules com.example.main

  --output app-image

Trick #5: Labeled breaks and continues

You know how to use the break statement to get out of an inner loop, but do you know that you can give the break a label to break out of an appropriately labeled outer loop as well? You can do likewise with continue, which skips the rest of the current iteration of the innermost loop, because if you pass it a label, it will skip the iteration of the labeled loop instead.

However, just because you can, doesn’t mean you should. Use this trick with care.

class ContinueWithLabelDemo {

    public static void main(String[] args) {

        String searchMe = "Look for a substring in me";

        String substring = "sub";

        boolean foundIt = false;

        int max = searchMe.length() -

                  substring.length();

    test:

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

            int n = substring.length();

            int j = i;

            int k = 0;

            while (n-- != 0) {

                if (searchMe.charAt(j++) != substring.charAt(k++)) {

                    continue test;

                }

            }

            foundIt = true;

                break test;

        }

        System.out.println(foundIt ? "Found it" : "Didn't find it");

    }

}

Trick #6: Boolean expressions in pattern matching

You know pattern matching, but do you know that you can use the variable that pattern matching introduces in the same boolean expression?

For example, if you check whether the instance object is of type String with object instanceof String s, you can start using s straight away, such as to check whether s is nonempty with && !s.isEmpty(). This works in if statements in Java 16 and in switch as a preview in Java 17.

Object object = // ...

if (object instanceof String s && !s.isEmpty())

    System.out.println("Non-empty string");

else

    System.out.println("No string or empty.");

Trick #7: Generic wildcards and subtyping

You know generics and that a List<Integer> does not extend a List<Number>, as shown in Figure 1.

Java Trick, Oracle Java Certification, Oracle Java Tutorial and Materials, Oracle Java Prep, Java Preparation, Java Career, Oracle Java Learning
Figure 1. Generic inheritance

But do you know that if you add wildcards, you can create a type hierarchy, as shown in Figure 2? A List<? extends Integer> actually does extend a List<? extends Number>. And the other way around, a List<? super Number> extends a List<? super Integer>.

Java Trick, Oracle Java Certification, Oracle Java Tutorial and Materials, Oracle Java Prep, Java Preparation, Java Career, Oracle Java Learning
Figure 2. Generic inheritance with wildcards

Trick #8: Creating and chaining predicates


You know how to write lambdas to create predicates, but do you know that the interface offers many methods to create and combine them? Call the instance methods and, or, or negate for boolean formulas.

You have no predicates yet? No problem! The static factory method not is useful for inverting a method reference. And if you pass some object to isEqual, you create a predicate that checks instances for equality with that object.

Predicate<String> isEqualToDuke = Predicate.isEqual("Duke");
Predicate<Collection<String>> isEmpty = Collection::isEmpty;
Predicate<Collection<String>> isNotEmpty = Predicate.not(isEmpty);

Trick #9: Creating and chaining comparators


If you think that was cool, hold your breath for comparators: You know how to implement them, but do you know there are even more factory and combination methods?

To compare long, double, float, and other values, use a method reference to their static compare method.

Comparator<Integer> comparator = Integer::compare;

If you want to compare objects by one of their attributes, pass a function extracting it to the static method Comparator.comparing. To first sort by one and then another attribute, create both comparators, and then chain them with the instance method thenComparing.

Comparator<User> byFirstName = Comparator.comparing(User::getFirstName);
Comparator<User> byLastName = Comparator.comparing(User::getLastName);
Comparator<User> byName = byFirstName.thenComparing(byLastName);

Need a Comparator instance that uses a Comparable object’s compareTo method? The static factory method naturalOrder is there for you. If that’s the wrong way around, just call reversed on it.

Ah, and what to do about the pesky null? Worry not: Pass a Comparator to nullsFirst or nullsLast to create a Comparator that does what you need.

Comparator<Integer> natural = Comparator.naturalOrder();
Comparator<Integer> naturalNullsLast = Comparator.nullsLast(natural);

Trick #10: Executing source files as scripts


You know that you can use the java command to launch a single source file without having to manually compile the file first. But do you know that you can use this capability to write full-on scripts in Java in three simple steps?

First, add a shebang line to the source file that points at your java executable, followed by --source and the Java version the code was written for.

#!/path/to/your/bin/java --source 16

public class HelloJava {

    public static void main(String[] args) {
        System.out.println("Hello " + args[0]);
    }
}

Second, rename the file so it doesn’t end with .java. This is a great opportunity to give it a good command-line style of name.

Third, make the file executable with chmod +x. There you go: scripts in Java!

Trick #11: Loading jshell with all imports


You know how to launch jshell for some quick experiments but do you know that you don’t have to import everything manually? Simply launch jshell with the option JAVASE and all Java SE packages are imported, so you can get right to work.

jshell JAVASE

Source: oracle.com

Friday, December 24, 2021

Education, JUGs, and hack days: The evolving Java community ecosystem

Oracle Java Exam Prep, Oracle Java Certifications, Oracle Java Exam Prep, Java Career, Java Tutorial and Materials

The chair of the Java Community Process shows you how to engage—and how to make a difference

Download a PDF of this article

Whether you are new to Java programming or have been using the platform for 25 years, it is clear that Java continues to remain one of the most popular and relevant programming languages.

Java moves our world. Think of any industry or technology, and you’ll see Java—banking, healthcare and life sciences, commerce, gaming, insurance, education, quantum computing, artificial intelligence, blockchain, and much more. Java is literally everywhere.

As a trusted ecosystem, Java has adapted to changing developer and business needs, thereby remaining relevant and popular. Java continues to be at or near the top of the rankings of programming languages used by companies and developers. For example, see the TIOBE Index, GitHut survey of languages used on GitHub, and the RedMonk Programming Language rankings.

The Java Community Process

A huge part of Java’s success can be attributed to how the language and runtime have evolved—and how the developer community is collaboratively involved in the evolution.

The Java Community Process (JCP) program is the method by which the international Java community standardizes and ratifies the specifications for Java technologies. The JCP ensures that high-quality specifications are developed using an inclusive, consensus-based approach. Specifications ratified by the JCP program must be accompanied by both a reference implementation, which proves the specification can be implemented, and a technology compatibility kit, which is a suite of tests, tools, and documentation used to test implementations for compliance with the specification.

Years of experience working within the JCP have shown that the best way to produce a technology specification is to use an open and inclusive process to codevelop a specification and implementation, informed by a group of industry experts with a variety of viewpoints. This also includes giving the community and the public opportunities to review and comment—and also a strong technical lead to ensure both the technical goals and integration with other relevant specifications and user expectations.

Within the JCP, the Executive Committee (EC) is responsible for approving the passage of specifications through the JCP’s various stages, as well as for reconciling discrepancies between specifications and their associated test suites. Every year, the members of the JCP program elect the members of the EC, ensuring that the EC represents a broad cross-section of major stakeholders and other members of the Java community.

Who are those members of the JCP? It varies widely, and includes companies, foundations and nonprofit organizations, Java user groups (JUGs), and individuals.

Anyone can apply to join the JCP and then participate in the JCP program as a corporation or nonprofit (full member), JUG (partner member), or individual (associate member). The stability of the JCP program and participation from community members ensures continued success of the Java platform today and into the future.

Standards enable execution of technical strategies, and the JCP enables collaboration of industry and participation from the developer community. Compatibility matters: The specifications, reference implementations, and technology compatibility kits required by the JCP program have led to a large, stable, and vibrant ecosystem to be built up around Java technologies. The JCP program provides the foundation and structure for this because intellectual property rights and obligations are protected, and everyone’s choice of implementations that pass the compatibility tests benefits the whole ecosystem.

Evolution of the JCP and the JCP.Next

After being introduced in 1999, the JCP program has continued to evolve over time using the process itself through an effort called JCP.Next, with the work being carried out in the open by the EC.

JCP.Next is a series of JSRs designed to bring significant benefits to the community and to specifically focus on transparency, streamlining the JCP program, and broadening its membership. These JSRs modify the JCP’s operational processes through modifying the JCP process document. Once the changes are complete, they will apply to all new JSRs and to future Maintenance Releases of existing JSRs for the Java platform.

As director and chairperson of the JCP program, I lead and chair the EC, and in this role, I act as specification lead of the JCP.Next JSRs. The EC itself serves as the expert group developing and revising those JCP.Next JSRs. In the JCP, we use the process to change the process: In other words, we use the same process to evolve the JCP program as we do to evolve technologies and other specifications.

The following four JSRs in JCP.Next have been finalized and approved:

◉ The first effort, JSR 348, Towards a new version of the Java Community Process, was completed and put into effect as JCP process document 2.8. It created important changes to make the JCP more open and transparent, and to enable broader participation.

◉ The second, JSR 355, JCP Executive Committee merge, contains revisions and resulted in JCP process document 2.9.

◉ The third, JSR 364, Broadening JCP membership, was put into effect as JCP process document 2.10. This JSR broadened JCP participation by defining new membership classes, enabling greater participation by the community and helping ensure the appropriate intellectual property commitments from JCP members. Any Java developer may join the JCP program, and depending on the type of membership, JCP members can participate as a JSR specification lead, expert group member, or contributor.

◉ The fourth, JSR 387, Streamline the JCP program, completed a maintenance release in late 2019. It was put into effect as the current version of the JCP program, version 2.11. This JSR streamlines the JSR lifecycle process to bring it in line with the way Java technology is developed today. This JSR also resized the JCP EC to the current size of 18 EC members.

Inspire students, the next generation of Java developers

While the JCP celebrated 25 years of Java technology in 2020, we looked forward to the next generation of Java developers. We began encouraging Java community leaders to participate in inspiring the next generation of developers to code using Java by engaging with their local educational communities. If you are an experienced Java developer or Java User Group member/leader, this is a prime opportunity for you to pay it forward.

Nearly 100 JUGs already participate in the JCP program from all around the world; as mentioned above, JUGs can join the JCP program as partner members.

In 2020, some of our discussions focused on the topic of Java in education. This topic was suggested by Ken Fogel, a new EC member.

Java community leaders are in a unique position to inspire their local community of junior developers and students to learn and use Java technology, and so we formed a working group to address the issue. The purpose and focus of this initiative are to help bridge the gap between the educational environment and industry. Together we can provide opportunities for students, teachers, and educational institutions in the form of networking, mentoring, knowledge and professional internships, open-source assignments, and projects.

Java is a top in-demand skill from employers for technical talent, and Java developers are also the most highly paid tech workers. Once students are working on projects in industry, it is difficult to find a project that does not include or touch some Java code.

We can also educate developers around the myths about the capabilities of modern Java technology.

Get involved with Java in Education

The Java in Education effort is designed to be global, JUG-led, and supported by the JCP program. Join the group.io list to participate in discussions with Java community leaders. This is where we develop materials for the community and share experiences.

Connect your local community to your KIG. Work with your local educational community to leverage Oracle Academy resources which offer secondary schools, technical/vocational schools, two- and four-year colleges, universities, and educators free resources, including

◉ Expert Java Fundamentals curriculum (English, Chinese [Simplified], French, Indonesian, Japanese, Portuguese [Brazil], Spanish, Arabic)

◉ Teaching and learning resources

◉ World-class technology and software

◉ Professional development opportunities

Institutional membership is best suited for institutions and educators who wish to take full advantage of Oracle Academy curriculum and teaching resources for the classroom, including world-class technology and software, professional development, Oracle professional certification resources, member recognition, and a wealth of teaching and learning resources. Oracle Academy membership is free.

Leverage your Java User Group. Host a learning workshop in your JUG to encourage interest in members of your JUG to consider participating in this initiative. You can also invite local teachers to attend or copresent with you in a scheduled session with students.

Don’t make it overwhelming for teachers to attend or copresent. Check to see if your local institutions have joined Oracle Academy. You might use the existing presentation materials on the Java in Education page.

Reach out to universities. You can contact student associations, college professors, or teaching assistants to try to figure out what they are looking for from the industry. You can offer to host sessions by industry professionals (perhaps from your JUG) at their educational institutions, help or organize a Java hack day, or even be a guest speaker at a computer science class.

You can also hold a JUG meeting (virtual or in person) for students and encourage them as well as their professors to join your JUG or subscribe to its mailing list.

By the way, professors are often looking out for internships for their students in good organizations. You and your JUG can help by sharing any open internships.

Encourage students to check out Java certifications. Recognized by the industry the world over, the Oracle Certified Professional: Java SE certifications help students and recent graduates know Java inside out, work with great projects and organizations, and boost their careers.

Tell students about the free Java class. Oracle University offers a free Java Explorer course, a seven-hour class that provides an overview and covers text and numbers; arrays, conditions, and loops; classes and objects; exception handling; inheritance and interfaces; and Java on Oracle Cloud Infrastructure. You and your JUG can offer to mentor students as they complete the coursework.

Navigating the evolution of the Java platform

The Java 16 release in March 2021 and the current Java 17 release are generating enthusiasm in the developer community. Over the last few years there have been many changes in the Java community, but one thing remains the same, and that’s the strength and value of Java technology and the Java developer community. The foundation and advantages for the Java developer community are provided through the oversight of the JCP program.

The innovations in Java 9 allowed for Java SE to deliver a new release every six months. Oracle also contributed many commercial features of Oracle JDK into OpenJDK following the release of Java 9. The Java community adopted the concept of Long-Term Support (LTS) releases, with many community members providing a variety of options for support in distributions.

New Java features and functionality became accessible and digestible to developers in smaller increments with the six-month release cadence. This enables an easier migration to new versions of Java but also helps developers to learn and use all of the features as they become available, without waiting for years between releases. The ability to have these innovations available sooner also motivates community members to have their contributions accepted.

As a community member, how can you navigate the aspects that change and continue to amplify the value provided by the JCP program to ensure continued success of Java technology? How will you participate? While participating as an individual is okay, being part of a team is even better. You can help each other and work together in your JUG or your team at your employer to make Java better. We achieve more by working together.

Why participate in the JCP?

◉ Acquire knowledge. Learn from experts, gain early access to releases. Enable an easier transition between releases. Be faster to market. Put your requirements into process.

◉ Build your resume. Add experience and skill development. Grow as a developer through communication, collaboration, negotiation, and teamwork.

◉ Increase professional visibility through contributing to curriculum, articles, workshops, and presentations.

◉ Become famous! Grow your reputation and network as well as the reputation of your JUG. Gain new customers based on your expertise.

◉ Make Java better: Specifications based on real-world experience are more successful. Create the future Java technology.

Here are some steps to participation in the JCP:

Pick a JCP project. There are many options. You can pick a JSR out of the active JSRs list on JCP.org, such as the Java SE Platform JSR.

Participate in OpenJDK by downloading the Early Access Builds. You can also download early access builds of some OpenJDK projects to learn about upcoming enhancements, such as Loom, Metropolis, Panama, Valhalla, and even JDK 18.

You can participate in OpenJDK by joining the Adoption group. Join the mailing list, and then indicate your interest and feedback.

As part of the Adoption Group, there is also the Quality Outreach group. You can support open-source Java projects and keep up to date with the latest release of Java. More than 100 projects are currently participating; two examples that have found new contributors via this program are Apache Maven and Eclipse Collections.

You can see more about involvement with OpenJDK here.

Communicate! Once you pick a project, either collectively as a work team or as an individual, remember to communicate within your JUG. You also need to communicate with the specification lead, project lead, and the expert group, as appropriate, on public discussion and issue trackers.

Communication is a two-way street; if you don’t communicate, you aren’t participating.

Decide on actions. Once you have communicated your interest to a project, decide and agree on the actions you will take. Here are some things that have been successful, but you should not be limited by this list:

◉ Share ideas and feedback, comment on list and public issue trackers.

◉ Read early versions and share feedback on specifications and Javadocs.

◉ Download and provide feedback on early-access reference implementation.

◉ Try writing sample applications using early builds of reference implementation.

◉ Write or speak about the technology and encourage others to participate.

◉ Translate into your native language.

◉ Evangelize, such as by social media, blogs, or lightning talks.

◉ Help with documentation.

Follow through. It is crucial that you follow through on your agreed actions, including contributing to public discussions/issue trackers and providing your feedback and comments. It is important to keep in mind that the specification lead, project lead, and the expert group have the final decision on incorporating the feedback.

Participate and organize hack days. A hack day can be virtual or in person, and it can be with a small group (three to five developers) or a large one (hundreds of developers). When you organize or participate in a hack day, you might find these ideas on past successful hack day projects useful:

◉ Test the early reference implementation builds. Use them to find pain points, report bugs, and suggest feature enhancements.

◉ Help triage issues: Reproduce issues, erase/merge duplicates, and set priorities/categories.

◉ Give feedback on design, discuss issues, and deliver feedback. Think about how you would use this feature as a developer.

◉ Help build the reference implementation. Start coding with the actual implementation of the specification or build sample applications.

◉ Help build the technology compatibility. Because all implementations must pass the test suite, this is a great way to gain test experience.

When you engage, have fun. As an individual, being part of the Java community should be enjoyable. For companies, it will help you to develop new markets, adapt and inform your technology strategies, and enable you to retain and grow your developers.

Source: oracle.com

Wednesday, December 22, 2021

Making a case for a commercial offering of open source–based software

Oracle Java Exam Prep, Oracle Java Exam, Java Preparation, Oracle Java Certification, Oracle Java Career, Core Java

By subscribing to an enterprise support subscription for platforms based on open source, companies are in essence combining the best aspects of both proprietary and open source software into one solution.

Download a PDF of this article

[The following is an excerpt from “Java and the art of driving digital transformation,” a 2021 paper from Omdia. You can download the full paper here. –Ed.]

Enterprise consumers of open source software are increasingly turning to third-party providers of support services to help them more rapidly innovate while also maintaining secure and sturdy operations.

By subscribing to an enterprise support subscription for platforms based on open source like OpenJDK, companies are in essence combining the best aspects of both proprietary and open source software into one solution.

On the surface, this may sound counterintuitive. For a programming language like Java, what if anything is there to support beyond platform compatibility for the Java JVM?

Of course, this kind of traditional phone/email engineering support and timely access to critical security patches are crucial for any enterprise-class deployment. But there are many more subtle reasons that make commercial support itself a crucial investment, particularly for enterprises with a long-term investment in an open source language like Java.

◉ Direct access to major technology releases

◉ Global, multi-lingual 24/7/365 technical support services

◉ Access to expert practitioner communities

◉ Service ticket response with service level agreements

◉ Accelerated, early access to critical performance, stability, and security updates

◉ Access to regulatory, licensing, and other legal updates

◉ Continued support for software releases past the end of public updates

◉ Software compliance, monitoring, and management software

◉ Upgraded tools, utilities, and scripts

◉ Third-party product compatibility certification

◉ Premier access to consultative services

◉ Performance tuning and license optimization advisory services

◉ Exclusive entitlements such as Java Management Service and GraalVM Enterprise

In evaluating Oracle’s own Java subscription offering, Oracle Java SE Subscription, several key benefits surface.

Inclusive and mature support services. In supporting more than 430,000 global customers, Oracle has built up substantial engineering expertise, which flows to its Java SE subscription customer experience in the form of 24/7/365 access to Java experts with more than 15 years of experience and the ability to converse in more than 27 languages from within the company’s Premier Support team.

Subscribers not only gain access to Java expertise but also access to critical bug fixes well before they reach the broader public. And this support extends beyond basic support issue resolution to include performance tuning and monitoring, security baseline and compliance services, and deployment mode evolution.

Improved application management. Included in the Java SE Subscription and Java SE Desktop Subscription, as well incorporated as a free native service within Oracle Cloud Infrastructure (OCI), Oracle’s new Java Management Service (JMS) provides a single pane of glass through which enterprises can discover all Java assets including Java Development Kit (JDK), Java Runtime Environment (JRE), and GraalVM—whether those assets are running on cloud, on-premises, or within a third-party cloud service.

At a glance, JMS users view the current version, originating vendor (e.g. Oracle), type of runtime, status, and most importantly for Oracle-provided runtimes, if they are above or below the security baseline. For large, complex deployments, in a future release JMS will be able to act as an early warning system, helping customers head off often unforeseen issues such as an imminent expiration of security certificates.

Flexible modernization. Customers can confidently remain with the version of Java used in creating a particular project, migrating to a newer version according to their schedule rather than that of the language. This is crucial for companies that maintain more than one version of Java.

For Java SE Subscription, this is best demonstrated within long term support (LTS) releases, which are now updated every two years (prior to Q3, 2021, this cycle stood at every three years). With an LTS release, customers can stay on the same version longer (Oracle releases new versions of Java every six months) and patch intermittently, at their own schedule before upgrading to the next LTS release. This term-based licensing mechanism lets enterprise practitioners completely control their upgrade path.

Taking this a step further, Oracle’s recently introduced Oracle No-Fee Terms and Conditions (NFTC) license enables developers to freely use Oracle JDK beginning with version 17, even in production and commercially so long as there’s no associated fee for redistribution. This lets developers standardize on Oracle’s enterprise-grade implementation of Java, moving freely between six-month releases and two-year LTS updates.

Long-term risk mitigation. Maintaining code that’s no longer supported with hot fixes and security patches not only creates technical debt, it also opens up companies to potential security risks.

Recognizing that companies often need to continue running on older versions of Java, Oracle Java SE Subscription offers extended support for older, popular versions of Java extending back to version 8 (released in 2014 but available for extended support through 2030) and Java 11 (released in 2016 and available for extended support through 2026). With these options, Oracle can create a consistent support baseline for code written over multiple decades.

This ability to go fast (incorporate new features every six months) or go slow (hold onto a given release for anywhere between two and eight years) is of crucial importance to heavily regulated companies within financial, healthcare, and public sector markets, where it is particularly difficult to balance the pressure to innovate with the need to mitigate risk.

Source: oracle.com

Monday, December 20, 2021

Perform textual sentiment analysis in Java using a deep learning model

Positive? Negative? Neutral? Use the Stanford CoreNLP suite to analyze customer product reviews.

Download a PDF of this article

Sentiment analysis is a text classification task focused on identifying whether a piece of text is positive, negative, or neutral. For example, you might be interested in analyzing the sentiment of customer feedback on a certain product or in detecting the sentiment on a certain topic trending in social media.

Core Java, Oracle Java Certification, Oracle Java Prep, Oracle Java Career, Oracle Java Skills, Java Jobs

This article illustrates how such tasks can be implemented in Java using the sentiment tool integrated into Stanford CoreNLP, an open source library for natural language processing.

The Stanford CoreNLP sentiment classifier

To perform sentiment analysis, you need a sentiment classifier, which is a tool that can identify sentiment information based on predictions learned from the training data set.

In Stanford CoreNLP, the sentiment classifier is built on top of a recursive neural network (RNN) deep learning model that is trained on the Stanford Sentiment Treebank (SST), a well-known data set for sentiment analysis.

In a nutshell, the SST data set represents a corpus with sentiment labels for every syntactically possible phrase derivable from thousands of sentences used, thus allowing for the capture of the compositional effects of sentiment in text. In simple terms, this allows the model to identify the sentiment based on how words compose the meaning of phrases rather than just by evaluating words in isolation.

To better understand the structure of the SST data set, you can examine the data set files downloadable from the Stanford CoreNLP sentiment analysis page.

In Java code, the Stanford CoreNLP sentiment classifier is used as follows.

To start, you build up a text processing pipeline by adding the annotators required to perform sentiment analysis, such as tokenize, ssplit, parse, and sentiment. In terms of Stanford CoreNLP, an annotator is an interface that operates on annotation objects, where the latter represent a span of text in a document. For example, the ssplit annotator is required to split a sequence of tokens into sentences.

The point is that Stanford CoreNLP calculates sentiment on a per-sentence basis. So, the process of dividing text into sentences is always followed by applying the sentiment annotator.

Once the text has been broken into sentences, the parse annotator performs syntactic dependency parsing, generating a dependency representation for each sentence. Then, the sentiment annotator processes these dependency representations, checking them against the underlying model to build a binarized tree with sentiment labels (annotations) for each sentence.

In simple terms, the nodes of the tree are determined by the tokens of the input sentence and contain the annotations indicating the predicted class out of five sentiment classes from very negative to very positive for all the phrases derivable from the sentence. Based on these predictions, the sentiment annotator calculates the sentiment of the entire sentence.

Setting up Stanford CoreNLP

Before you can start using Stanford CoreNLP, you need to do the following setup:

◉ To run Stanford CoreNLP, you need Java 1.8 or later.

◉ Download the Stanford CoreNLP package and unzip the package in a local folder on your machine.

◉ Add the distribution directory to your CLASSPATH as follows:

export CLASSPATH=$CLASSPATH:/path/to/stanford-corenlp-4.3.0/*:

After completing the steps above, you are ready to create a Java program that runs a Stanford CoreNLP pipeline to process text.

In the following example, you implement a simple Java program that runs a Stanford CoreNLP pipeline for sentiment analysis on text containing several sentences.

To start, implement a class that provides a method to initialize the pipeline and a method that will use this pipeline to split a submitted text into sentences and then to classify the sentiment of each sentence. Here is what the implementation of this class might look like.

//nlpPipeline.java

import java.util.Properties;

import edu.stanford.nlp.pipeline.StanfordCoreNLP;

import edu.stanford.nlp.ling.CoreAnnotations;

import edu.stanford.nlp.pipeline.Annotation;

import edu.stanford.nlp.neural.rnn.RNNCoreAnnotations;

import edu.stanford.nlp.sentiment.SentimentCoreAnnotations;

import edu.stanford.nlp.sentiment.SentimentCoreAnnotations.SentimentAnnotatedTree;

import edu.stanford.nlp.trees.Tree;

import edu.stanford.nlp.util.CoreMap;

public class nlpPipeline {

    static StanfordCoreNLP pipeline;

    public static void init() 

    {

        Properties props = new Properties();

        props.setProperty("annotators", "tokenize, ssplit, parse, sentiment");

        pipeline = new StanfordCoreNLP(props);

    }

    public static void estimatingSentiment(String text)

    {

   int sentimentInt;

      String sentimentName; 

      Annotation annotation = pipeline.process(text);

      for(CoreMap sentence : annotation.get(CoreAnnotations.SentencesAnnotation.class))

      {

         Tree tree = sentence.get(SentimentAnnotatedTree.class);

        sentimentInt = RNNCoreAnnotations.getPredictedClass(tree); 

                sentimentName = sentence.get(SentimentCoreAnnotations.SentimentClass.class);

        System.out.println(sentimentName + "\t" + sentimentInt + "\t" + sentence);

      }

     }

}

The init() method initializes the sentiment tool in the Stanford CoreNLP pipeline being created, and it also initializes the tokenizer, dependency parser, and sentence splitter needed to use this sentiment tool. To initialize the pipeline, pass a Properties object with the corresponding list of annotators to the StanfordCoreNLP() constructor. This creates a customized pipeline that is ready to perform sentiment analysis on text.

In the estimatingSentiment() method of the nlpPipeline class, invoke the process() method of the pipeline object created previously, passing in text for processing. The process() method returns an annotation object that stores the analyses of the submitted text.

Next, iterate over the annotation object getting a sentence-level CoreMap object on each iteration. For each of these objects, obtain a Tree object containing the sentiment annotations used to determine the sentiment of the underlying sentence.

Pass the Tree object to the getPredictedClass() method of the RNNCoreAnnotations class to extract the number code of the predicted sentiment for the corresponding sentence. Then, obtain the name of the predicted sentiment and print the results.

To test the functionality above, implement a class with the main() method that invokes the init() method and then invokes the estimatingSentiment() method of the nlpPipeline class, passing sample text to the latter.

In the following implementation, the sample text is hardcoded in the program for simplicity. The sample sentences were designed to cover the entire spectrum of sentiment scores available with Stanford CoreNLP: very positive, positive, neutral, negative, and very negative.

//SentenceSentiment.java

public class SentenceSentiment 

{

  public static void main(String[] args) 

  {

               String text = "This is an excellent book. I enjoy reading it. I can read on Sundays. Today is only Tuesday. Can't wait for next Sunday. The working week is unbearably long. It's awful.";

    nlpPipeline.init();

    nlpPipeline.estimatingSentiment(text);

  }

}

Now, compile the nlpPipeline and SentenceSentiment classes and then run SentenceSentiment:

$ javac nlpPipeline.java

$ javac SentenceSentiment.java 

$ java SentenceSentiment

Here is what the output should look like.

Very positive  4     This is an excellent book.

Positive       3     I enjoy reading it.

Neutral        2     I can read on Sundays.

Neutral        2     Today is only Tuesday.

Neutral        2     Can't wait for next Sunday.

Negative       1     The working week is unbearably long.

Very negative  0     It's awful.

The first column in the output above contains the name of the sentiment class predicted for a sentence. The second column contains the corresponding number code of the predicted class. The third column contains the sentence.

Analyzing online customer reviews

As you learned from the previous example, Stanford CoreNLP can return a sentiment for a sentence. There are many use cases, however, where there is a need to analyze the sentiment of many pieces of text, each of which may contain more than a single sentence. For example, you might want to analyze the sentiment of tweets or customer reviews from an ecommerce website.

To calculate the sentiment of a multisentence text sample with Stanford CoreNLP, you might use several different techniques.

When dealing with a tweet, you might analyze the sentiment of each sentence in the tweet and if there are some sentences that are either positive or negative you could rank the entire tweet respectively, ignoring the sentences with the neutral sentiment. If all (or almost all) the sentences in a tweet are neutral, then the tweet could be ranked neutral.

Sometimes, however, you don’t even have to analyze each sentence to estimate the sentiment of an entire text. For example, when analyzing customer reviews, you can rely on their titles, which often consist of a single sentence.

To work through the following example, you’ll need a set of customer reviews. You can use the reviews found in the NlpBookReviews.csv file accompanying this article. The file contains a set of actual reviews downloaded from an Amazon web page with the help of Amazon Review Export, a Google Chrome browser extension that allows you to download a product’s reviews with their titles and ratings to a comma-separated values (CSV) file. (You can use that tool to explore a different set of reviews for analysis.)

There is another requirement. Because the Java language lacks any native support for the efficient handling of CSV files, you’ll need a third-party library such as Opencsv, an easy-to-use CSV parser library for Java. You can download the Opencsv JAR and its dependencies. In particular, you will need to download the Apache Commons Lang library. Include them in the CLASSPATH as follows:

export CLASSPATH=$CLASSPATH:/path/to/opencsv/*:

Then, add the following method to the nlpPipeline class created in the previous section:

//nlpPipeline.java

    ...

    public static String findSentiment(String text) {

        int sentimentInt = 2;

        String sentimentName = "NULL";

        if (text != null && text.length() > 0) {

          Annotation annotation = pipeline.process(text);

          CoreMap sentence = annotation

                    .get(CoreAnnotations.SentencesAnnotation.class).get(0);

          Tree tree = sentence

                     .get(SentimentAnnotatedTree.class);

          sentimentInt = RNNCoreAnnotations.getPredictedClass(tree);

          sentimentName = sentence.get(SentimentCoreAnnotations.SentimentClass.class);

        }

        return sentimentName;

    }

As you might notice, the code above is similar to the code in the estimatingSentiment() method defined in the previous section. The only significant difference is that this time you don’t iterate over the sentences in input text. Instead, you get only the first sentence, since in most cases a review’s title consists of a single sentence.

Create a ReviewsSentiment.java file with the main method that will read the reviews from a CSV file and pass them to the newly created findSentiment() for processing, as follows:

import com.opencsv.CSVReader;

import com.opencsv.CSVParser;

import com.opencsv.CSVReaderBuilder;

import com.opencsv.exceptions.CsvException; 

import java.io.FileReader;

import java.io.IOException;

public class ReviewsSentiment {

    public static void main(String[] args) throws IOException, CsvException {

        nlpPipeline.init();

        String fileName = "NlpBookReviews.csv";

        try (CSVReader reader = new CSVReaderBuilder(new FileReader(fileName)).withSkipLines(1).build())

        {

            String[] row;

            while ((row = reader.readNext()) != null) {

                System.out.println("Review: " + row[1] + "\t" + " Amazon rating: " + row[4] + "\t" + " Sentiment: " + nlpPipeline.findSentiment(row[1]));

            }

        }

    }

}

You’ll need to recompile the nlpPipeline class and compile the newly created ReviewsSentiment class. After that, you can run ReviewsSentiment as follows:

$ javac nlpPipeline.java

$ javac ReviewsSentiment.java

$ java ReviewsSentiment

The output should look as follows:

Review: Old version of python useless            Amazon rating: 1    Sentiment: Negative

Review: Excellent introduction to NLP and spaCy  Amazon rating: 5    Sentiment: Positive

Review: Could not get spaCy on MacBook           Amazon rating: 1    Sentiment: Negative

Review: Good introduction to SPACY for beginner. Amazon rating: 4    Sentiment: Positive

Source: oracle.com

Friday, December 17, 2021

Dalia’s gentle introduction to Docker

The friendly overview of containers and Docker that many Java developers have been looking for

Download a PDF of this article

You’ve certainly heard of containers and Docker by now. It seems like someone is always talking about them—just like my brother-in-law always talking about cryptocurrency and Bitcoin. What are containers? What is Docker? Why would you use them? Well, I’m going to attempt to answer those questions the way I wish someone explained them to me when I first started learning about this technology.

(Would you prefer watching instead of reading? This article goes over the same concepts covered in the first half of my Intro to Docker video.)

The problem

Imagine you’ve written an awesome application in Java. For simplicity’s sake, let’s say the application is running on a laptop, perhaps on Linux, macOS, or Windows. The application uses a database, such as Oracle Database, MySQL, or MongoDB. It also uses an application server, such as Tomcat or Open Liberty, as seen in Figure 1. You’ve finished writing your application and it runs great on your machine.

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 1. The application stack running on your machine

Now, you need to show the application to your coworker Alex to try out. A simple task, right? Let’s see how it plays out.

You: “Hey Alex, I’d like you to try out my application. I’m sending you the application JAR file so you can run it on your machine.”

Alex: “Got it! I’ve already got Java installed. I’ll go ahead and run it.”

You: “Wait, you have to install MongoDB first.”

Alex: “Oh, I’ve never used MongoDB before. I’ll look up how to do that.”

You: “I’d be happy to help you! Let’s start a screen-sharing session.”

<You start a screen-sharing session.>

You (to yourself): Oh no, it’s a Linux machine. Linux…I have no idea how to do this on Linux.

<Several minutes (or hours) and steps later>

Alex: “Looks like MongoDB is installed! Okay, let’s run the application.”

You: “Wait. You have to install an application server too.”

Alex: “An application server?”

Alex (to himself): This had better be the best application I’ve ever seen.

You: “Yeah. Let me show you.”

<Several minutes, curses, and steps later>

Alex: “Okay, the app server is ready. Can I run the application now?”

You: “Yes!”

<The application server starts and shows an error: “The application failed to start.”>

You: “BUT IT WORKS ON MY MACHINE!”

<You and Alex debug for a while.>

You: “Oh, which version of Java are you using?”

Alex: “Java 8.”

You: “I think that’s the problem. I’m using Java records and that capability is available only since Java 16. Can you install Java 16 and try running the application with that?”

Alex: “Java 16? They’re on Java 16 now?”

<Alex goes off to install Java 16.>

Alex: “Okay. Java 16 is installed. Let me try to run the application again.”

<Another failure: Debug, fix, rinse, and repeat.>

Finally, Alex gets the Java application working (see Figure 2) and is amazed at how awesome it is.

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 2. You and Alex can run the Java application on your machines, because each machine contains the same stack.

A couple of hours later, you receive a message from Jordan.

Jordan: “Hey, Alex told me about the new application. I’d love to try it out.”

You (to yourself): Oh no, not again!

All this was to illustrate that if you have Java applications that use any external resources such as a database or application servers, you’ll find it painful to set up the applications on other machines. (That can be equally true for server-based, cloud-based, or hybrid applications.)

Every time you add a new developer to the project, set up a machine for testing, or configure a continuous integration and continuous deployment (CI/CD) pipeline, you could be spending hours or days doing so (see Figure 3). This is one of the many ways Docker and containers can help.

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 3. More developers can mean more configurations to replicate and synchronize.

What is a container?


A container is a standard component that lets you package your application and its dependencies in an easy-to-share way. Some of the most important characteristics of a container are that they’re portable, isolated, and lightweight (see Figure 4).

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 4. Containers package applications and their dependencies.

The term container comes from shipping containers, which became popular in the 1950s and 1960s. With shipping containers, you could transport a car or a bunch of apples without the ship's captain having to worry about the size or shape of its cargo (see Figure 5). As long as you provide the contents in a uniform container, they will be shipped. (These are also called intermodal containers, since the same container can be carried by ships, trains, and trucks without unloading or repacking its contents.)

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 5. Shipping standardized containers

Similarly, a containerized application can be a web application, a mobile application, or an enterprise application. It can be written in any language, such as Java. The application can use Spring Boot, Jakarta EE, or any other framework. The container can include databases, application servers, and other necessary dependencies. As long as you package your application in a container, Alex, Jordan, and everyone else can run the software with minimal work.

Docker is a popular open source platform that allows you to use containers (see Figure 6).

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 6. Docker’s whale logo looks like a ship filled with standardized containers.

The solution


You (wisely) decide to install Docker and containerize your application. You put the application code, application server, and JVM into a single container since they’re all closely tied together. Then, in a separate container, you install the database. (See Figure 7.)

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 7. The application now lives in two containers: one for the application, application server, and JVM and the other for the database.

How is this any better? Well, if you had this setup earlier, the conversation with Alex might have gone like this.

You: “Hey Alex, I’d like you to try out my application. Since you already have Docker installed, you will need only a couple of simple commands to run it on your machine, and you don’t even need to upgrade from your old Java 8 to the Java 16 JVM that I’m using or figure out how to install MongoDB.”

Alex: “Done! Your application looks awesome!”

That sounds easy, but how do you create a container? First, you need an image.

What is an image?


A helpful analogy for understanding the relationship between images and containers is to consider classes and runtime objects. In Java, if I want to create an Employee object, I need to first create an Employee class. Once I have the Employee class, I can instantiate as many Employee objects as I want. Similarly, in Docker, if I want to create a container, I need to first create an image. Once I have the image, I can use it to instantiate as many containers as I’d like.

In other words, a container is the running instance of an image.

To create an image, you need a Dockerfile, which contains the list of instructions that need to be executed to create an image.

Virtual machines versus containers


A common question when developers are learning about containers is “What is the difference between containers and virtual machines?” Well, they’re both trying to solve the same problem, but containers and VMs approach it differently.

Let’s take a look at the VM scenario. If you wanted to run the applications in a virtual machine, the setup would look something like Figure 8, with a machine running a host operating system. That OS has a hypervisor that creates, executes, and manages virtual machines.

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 8. A machine running a hypervisor, which in turn hosts one or more VMs, each with its own operating system and application stack

When VMs are created, each VM includes its own guest operating system that the application runs on. Unfortunately, having a guest operating system is not cheap resource-wise. Lots of memory and CPU cycles are consumed by each VM’s guest operating system.

Now, let’s compare that to Docker. With Docker containers, you don’t have a guest operating system, making Docker containers much smaller and faster than virtual machines (see Figure 9).

Oracle Java Exam, Oracle Java Exam Prep, Oracle Java Tutorial and Materials, Oracle Java Career
Figure 9. The stacks required by applications managed by Docker containers (left) and virtual machines (right)

Applications running in containers use fewer resources than those consumed by virtual machines, which lets you run more containers on a given physical machine. The startup time on a container is also much faster, especially since containers don’t need to load a guest operating system.

To be clear, there are pros and cons for containers versus virtual machines. I won’t dive into that here. The main takeaway is that they both solve the “but it works on my machine” problem but approach the solution in different ways. In many instances, however, such as for distributing applications, Docker is often the best solution.

Source: oracle.com