Monday Dec 15, 2014

Upgrading major Java versions - technical

Many users have already upgraded from Java 7 to Java 8, to benefit from improvements in speed, brevity of code, and lower memory usage. Other users have asked for more prescriptive guidance of the upgrade: when to make the change, what to expect, and how to do it in a controlled manner.

Relation to a previous post

A previous post, Upgrading Major Java Versions, provides details for certain stakeholders: support timelines, compatibility guides, lists of changes, and different supporting material.

This post is intended to provide more prescriptive guidance of upgrading your Java SE version: how to test components and features designed to control behavior and upgrade part of your environment in stages.

The decision to upgrade is not only for companies that develop software; it also applies to users running software built by others. In many cases, users can see significant speed improvements without recompiling, simply by upgrading the runtime.

Planning upgrade in stages

The previous post explains when to upgrade in relation to platform support. When upgrading infrastructure, it is important to segment the architecture. Rather than upgrading everything at the same time, separate it into different environments so that you can test each one on its own.

Typical environments, in my preferred order of when to upgrade:

  1. Developer workstations, where developers write and test code. This is most likely where you would run IDEs like NetBeans, Eclipse, or IntelliJ.
  2. Central build servers, where code is combined, built, and unit tested through automation. Common software is Hudson or other continuous integration software. Some organizations do not have central build servers.
  3. Test or QA servers. This environment may run your application in order to find any issues before release into production.
  4. Production servers. The final environment that should be upgraded last, these servers run your application for users.

High level upgrade plan: Upgrade the build and test environments but keep things targeted for production. Once you are ready, upgrade the production environment and begin taking advantage of new features.

Backwards Compatibility

The JDK is backwards compatible, meaning that JDK 8 can run binaries produced by JDK 7 and JDK 6. The javac compiler can also produce binaries targeting earlier versions of the JDK. In planning to upgrade, we will use this capability.

Upgrading Developer Workstations and/or Central Build Servers

The important similarity between developer workstations and central build servers is that they are used to compile the application from source code into binary artifacts, such as JAR and WAR.

Upgrading a workstation or build server involves upgrading its JDK installation. The same system may run multiple versions of the JDK side-by-side, making it your choice if you want to uninstall the older one.

Environment Variables for installation

If you choose to run multiple versions, just be mindful of two environment variables:

  • PATH – identifies which actual java executable your system will use when you call java. You can circumvent this by explicitly calling the executable via /opt/jdk8/bin/java or /opt/jdk7/bin/java commands. Just call the one you want to use at the time. If you use scripts that rely on environment variables, consider isolating your terminals if you change the environment.
  • JAVA_HOME – some applications use this variable to identify where Java is.

Test your upgrade the following commands:

  • java -version
    • This should reply back with 1.8 or 1.7 depending on which you want. Just make sure it is right
  • echo $JAVA_HOME
    • On Linux, that will identify the JAVA_HOME variable that some applications may use. Check that it is the installation you intend to use.
    • On Windows, use: echo %JAVA_HOME%
    • You can also check the entire process with:
      • Linux: $JAVA_HOME/bin/java -version
      • Windows: %JAVA_HOME%\bin\java -version

Personal Tip: On my Windows 7 laptop, I regularly switch Java version to test things. To counter forgetfulness, I set my JAVA_HOME variable first, and then my PATH uses that JAVA_HOME. By doing this, I only need to change one thing. My PATH starts with: %JAVA_HOME%\bin;

Cross-compiling to meet your runtime’s compatibility

By using your upgraded installation to cross-compile, you will produce artifacts that run in your not-yet-upgraded test and production environments.

The javac compiler provides three options to control compatibility: -bootclasspath -source and -target. Without the -source flag, the compiler won’t warn you about using language features that may not be available on your earlier target JDK platform.  Without the -target flag, the compiler won’t produce binaries that can run on your earlier target JDK. Finally, without the -bootclasspath flag, the compiler won’t be able to find the correct version of the core class libraries from the earlier JDK. A simple example of using all flags correctly can be found in the javac documentation’s Examples section.

Configure the build to specify the -source and -target of your runtime.

  • Regular javac: -source 1.X -target 1.X
    If you have the older JDK: javac: -source 1.X -target 1.X –bootclasspath JDK1.6/lib/rt.jar
  • Maven: Modern versions of maven have a <maven.compiler.source>1.X</maven.compiler.source> attribute that can be set in properties. Alternately, use the maven-compiler-plugin attributes like <source/> and <target/>
  • Ant: The <javac source="1.X" target="1.X" /> task, or you can use a separate ant.build.javac.source property.
  • Etc. consult your build system’s documentation.

Monitor compiler errors and warnings

Building your application with the latest JDK will identify any potentially problematic areas. Investigate compiler errors, if any.

Although compiler warnings do not cause build failures, they indicate areas of interest. Looking into them provides a safeguard of something that may affect compatibility or legibility of code.

In JDK 8, we added special indicators to point where incompatibilities may occur in the future.

com/example/App.java:[32,24] SOMETHING is internal proprietary API and may be removed in a future release

In this example, my code has made use of a JDK internal API on line 32 of App.java. If you see this message, your code will likely still work for now but you should consider moving towards the known-compatible replacement for that API.

Use jdeps or other compatibility analyzers

OpenJDK 8 features a new dependency analysis tool, jdeps, designed to identify where applications or their dependencies make use of internal JDK APIs. Usage of these APIs does not currently indicate incompatibility, rather they point out where you use non-public and unsupported internal APIs.

If jdeps identifies usage in your code, consider switching to the public replacement APIs. If jdeps identifies usage in third party code, you may still be impacted in the future. Consider upgrading those identified dependencies, patching them, or identifying an alternative.

We have previously limited access to some internal APIs in some situations. The publicly supported APIs are still available, unaffected, and fine to use.

Consider additional tools for analysis as well:

  • The Forbidden APIs project also helps identify cases where certain APIs either should not be used or should be used differently. In addition to identifying internal APIs, this finds potential locale and encoding issues.
  • The Modernizer Maven Plugin also locates legacy APIs. While jdeps is focused solely on internal JDK APIs, this finds them in other projects. That may be useful if you are upgrading those other projects as well.

Running an application in test or production

Upgrading the test and production environments will allow you to evaluate an application and see how it interacts with other systems.

The JDK is designed to be backwards compatible. Upon upgrading the test environment, it is your choice if you want to run the same cross-compiled binary.

When testing your application, it is important not to change too many things at once. For example if you test the upgrade while simultaneously testing a complete rewrite of major components at the same time, it will be hard to tell which issue came from which cause. Given time available for testing, is may not be feasible to test the upgrade alone, without any other changes. Isolate changes as best you can and do not pre-assign a root cause to any issue before investigation.

Once you have successfully upgraded your environment(s), there is no more reason to cross-compile your binaries. Consider going back to your build environment and removing the -bootclasspath, -source, and -target flags.

Environmental changes

The previous post, Upgrading Major Java Versions, provides links to detailed information and compatibility guides about what changed between different versions.

In the interest of brevity, I will list a few noteworthy differences that I have seen:

  • JDK 8 uses TLS 1.2 as its default transport protocol for connections like https. JDK 7 made TLS 1.2 available but did not use it as the default. JDK 6 did not offer this protocol. For details, see JDK 8 Will use TLS 1.2 as its default and Diagnosing TLS, SSL and HTTPS.
  • JDK 8 no longer has a type of memory called PermGen, as it was replaced by Metaspace. This should not affect most people, but older startup scripts may have used -XX:MaxPermSize options. This should not cause problems, as tuning PermGen will no longer do anything. Please consider removing unnecessary startup options as a good measure.
  • Startup switches that begin with -XX: should be validated to see if they still apply. The java command documentation identifies these as advanced options not recommended for casual use. They are subject to change. If you experience unexpected behavior or slower performance, your flags may be working around a problem that no longer exists.
  • Applications using a ScriptEngine like JavaScript will use the newer/faster Nashorn in place of the previous Rhino interpreter. If your application made extensive use of Rhino and you find errors after upgrading, please consult the Rhino Migration Guide.

Uptake of Java SE 8

Many applications are able to leverage a smooth upgrade path from JDK 7 to JDK 8, in order to benefit from improvements like speed and more concise code. Examples of teams that have successfully migrated include:

I will continue monitoring different areas and will try to follow up in the future with different strategies for upgrading.


Wednesday Jun 11, 2014

Nashorn, the rhino in the room

Nashorn is a new runtime within JDK 8 that allows developers to run code written in JavaScript and call back and forth with Java. One advantage to the Nashorn scripting engine is that is allows for quick prototyping of functionality or basic shell scripts that use Java libraries. The previous JavaScript runtime, named Rhino, was introduced in JDK 6 (released 2006, end of public updates Feb 2013). Keeping tradition amongst the global developer community, "Nashorn" is the German word for rhino.

The Java platform and runtime is an intentional home to many languages beyond the Java language itself. OpenJDK’s Da Vinci Machine helps coordinate work amongst language developers and tool designers and has helped different languages by introducing the Invoke Dynamic instruction in Java 7 (2011), which resulted in two major benefits: speeding up execution of dynamic code, and providing the groundwork for Java 8’s lambda executions. Many of these improvements are discussed at the JVM Language Summit, where language and tool designers get together to discuss experiences and issues related to building these complex components.

There are a number of benefits to running JavaScript applications on JDK 8’s Nashorn technology beyond writing scripts quickly:

  1. Interoperability with Java and JavaScript libraries.
  2. Scripts do not need to be compiled.
  3. Fast execution and multi-threading of JavaScript running in Java’s JRE.
  4. The ability to remotely debug applications using an IDE like NetBeans, Eclipse, or IntelliJ (instructions on the Nashorn blog).
  5. Automatic integration with Java monitoring tools, such as performance, health, and SIEM.

In the remainder of this blog post, I will explain how to use Nashorn and the benefit from those features.

Nashorn execution environment

The Nashorn scripting engine is included in all versions of Java SE 8, both the JDK and the JRE. Unlike Java code, scripts written in nashorn are interpreted and do not need to be compiled before execution.

Developers and users can access it in two ways:

  • Users running JavaScript applications can call the binary directly:
    jre8/bin/jjs
    • This mechanism can also be used in shell scripts by specifying a shebang like #!/usr/bin/jjs
  • Developers can use the API and obtain a ScriptEngine through:
    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    • When using a ScriptEngine, please understand that they execute code. Avoid running untrusted scripts or passing in untrusted/unvalidated inputs. During compilation, consider isolating access to the ScriptEngine and using Type Annotations to only allow @Untainted String arguments.

One noteworthy difference between JavaScript executed in or outside of a web browser is that certain objects will not be available. For example when run outside a browser, there is no access to a document object or DOM tree. Other than that, all syntax, semantics, and capabilities are present.

Examples of Java and JavaScript

The Nashorn script engine allows developers of all experience levels the ability to write and run code that takes advantage of both languages. The specific dialect is ECMAScript 5.1 as identified by the User Guide and its standards definition through ECMA international.

In addition to the example below, Benjamin Winterberg has a very well written Java 8 Nashorn Tutorial that provides a large number of code samples in both languages.

Basic Operations

A basic Hello World application written to run on Nashorn would look like this:

#!/usr/bin/jjs
print("Hello World");

The first line is a standard script indication, so that Linux or Unix systems can run the script through Nashorn. On Windows where scripts are not as common, you would run the script like: jjs helloWorld.js.

Receiving Arguments

In order to receive program arguments your jjs invocation needs to use the -scripting flag and a double-dash to separate which arguments are for jjs and which are for the script itself:
jjs -scripting print.js -- "This will print"

#!/usr/bin/jjs
var whatYouSaid = $ARG.length==0 ? "You did not say anything" : $ARG[0]
print(whatYouSaid); 

Interoperability with Java libraries (including 3rd party dependencies)

Another goal of Nashorn was to allow for quick scriptable prototypes, allowing access into Java types and any libraries. Resources operate in the context of the script (either in-line with the script or as separate threads) so if you open network sockets and your script terminates, those sockets will be released and available for your next run.

Your code can access Java types the same as regular Java classes. The “import statements” are written somewhat differently to accommodate for language. There is a choice of two styles:

  1. For standard classes, just name the class: var ServerSocket = java.net.ServerSocket
  2. For arrays or other items, use Java.type: var ByteArray = Java.type("byte[]")
    You could technically do this for all.

The same technique will allow your script to use Java types from any library or 3rd party component and quickly prototype items.

Building a user interface

One major difference between JavaScript inside and outside of a web browser is the availability of a DOM object for rendering views. When run outside of the browser, JavaScript has full control to construct the entire user interface with pre-fabricated UI controls, charts, or components. The example below is a variation from the Nashorn and JavaFX guide to show how items work together.

Nashorn has a -fx flag to make the user interface components available. With the example script below, just specify: jjs -fx -scripting fx.js -- "My title"

#!/usr/bin/jjs -fx
var Button = javafx.scene.control.Button;
var StackPane = javafx.scene.layout.StackPane;
var Scene = javafx.scene.Scene;
var clickCounter=0;
$STAGE.title = $ARG.length>0 ? $ARG[0] : "You didn't provide a title";
var button = new Button();
button.text = "Say 'Hello World'";
button.onAction = myFunctionForButtonClicking;
var root = new StackPane();
root.children.add(button);
$STAGE.scene = new Scene(root, 300, 250);
$STAGE.show();
function myFunctionForButtonClicking(){
  var text = "Click Counter: " + clickCounter;
  button.setText(text);
  clickCounter++;
  print(text);
}

For a more advanced post on using Nashorn to build a high-performing UI, see JavaFX with Nashorn Canvas example.

Interoperable with frameworks like Node, Backbone, or Facebook React

The major benefit of any language is the interoperability gained by people and systems that can read, write, and use it for interactions. Because Nashorn is built for the ECMAScript specification, developers familiar with JavaScript frameworks can write their code and then have system administrators deploy and monitor the applications the same as any other Java application.

A number of projects are also running Node applications on Nashorn through Project Avatar and the supported modules.

In addition to the previously mentioned Nashorn tutorial, Benjamin has also written a post about Using Backbone.js with Nashorn.

To show the multi-language power of the Java Runtime, there is another interesting example that unites Facebook React and Clojure on JDK 8’s Nashorn.

Summary

Nashorn provides a simple and fast way of executing JavaScript applications and bridging between the best of each language. By making the full range of Java libraries to JavaScript applications, and the quick prototyping style of JavaScript to Java applications, developers are free to work as they see fit.

Software Architects and System Administrators can take advantage of one runtime and leverage any work that they have done to tune, monitor, and certify their systems.

Additional information is available within:

Tuesday Apr 01, 2014

Java 8's new Type Annotations

Java 8 introduces two important changes to Annotations designed to help developers produce better code and improve the accuracy of automated code analysis to verify that quality.

Quick Annotations Webinar

There is a great video explaining the new improvements in the Java 8 Launch Webinars called “Enhanced Metadata - Annotations and Access to Parameter Names” by Alex Buckley and Michael Ernst.

Annotation Improvements

Type Annotations allow developers to write annotations in more places than before. The compiler can then verify these annotations, for example identifying uses of null values, accidental value modifications, and cases where data crosses a trust boundary without proper validation. By moving some annotatable information from the Javadoc (understood only by people) and into the code (understood by both people and analyzers), it is easier to understand intent and verify the absence of certain errors.

Repeating Annotations make it easier for authors of these annotations because there is less need for wrapper annotations.

The Checker Framework provides a few Type Annotations that could benefit both library and application developers, such as:

  • @NonNull – The compiler can determine cases where a code path might receive a null value, without ever having to debug a NullPointerException.
  • @ReadOnly – The compiler will flag any attempt to change the object.  This is similar to Collections.unmodifiableList, but more general and verified at compile time.
  • @Regex – Provides compile-time verification that a String intended to be used as a regular expression is a properly formatted regular expression.
  • @Tainted and @Untainted – Identity types of data that should not be used together, such as remote user input being used in system commands, or sensitive information in log streams.
  • @m – Units of measure ensures that numbers used for measuring objects are used and compared correctly, or have undergone the proper unit conversion.

Putting Type Annotations on your code

Java SE 8 allows type annotations anywhere that a type is used. Previously, annotations were only allowed on definitions. Some examples of this are:

Annotation Example Meaning
@NonNull List<String>
A non-null list of Strings.
List<@NonNull String>
A list of non-null Strings.
@Regex String validation = "(Java|JDK) [7,8]"
Check at compile time that this String is a valid regular expression.
private String getInput(String parameterName){
final String retval = @Tainted request.getParameter(parameterName);
  return retval;
}
The object assigned to retval is tainted and not for use in sensitive operations.
private void runCommand(@Untainted String… commands){
ProcessBuilder processBuilder = new ProcessBuilder(command);
  Process process = processBuilder.start();
}
Each command must be untainted. For example, the previously tainted String must be validated before being passed in here.

For reading annotations, the way to look at them is that they annotate the next item after that isn’t also an annotation.

Automating issue detection

When working on software, it helps to uncover potential problems early. A problem caught early is easier to fix than one caught later, and a potential problem caught right away is easier still. Some annotations allow problems to be caught immediately. The @Override annotation allows the compiler (or a static analysis tool) to immediately determine if a developer wrote the wrong method signature.

Other annotations, like @NonNull and @Readonly can be used by analyzers like the Checker Framework, FindBugs, Eclipse, NetBeans, IntelliJ, or a commercial analyzer. Those analyzers can then be run at compile time, through IDE background compilation, Ant/Maven, or continuous integration.

Type Annotations tell those analyzers what to look for. Without the Type Annotations in place, these analyzers would still be able to locate null-usage and write-modifications but would not know that they are wrong. The result would then be false negatives (no issue reported) or false positives (incorrect issues reported).

Teamwork

Type Annotations can greatly benefit teams that are geographically distributed or contain many members. By placing Type Annotations inside the code and running automated checks before commits or during integration builds, team members can identify situations where one change inadvertently affects another.

Optional Type Annotations are not a substitute for runtime validation

Before Type Annotations, the primary location for describing things like nullability or ranges was in the javadoc. With Type annotations, this communication comes into the bytecode in a way for compile-time verification.

Your code should still perform runtime validation.

Annotation validation versus Business Validation

Type Annotations are best used with common forms of validation that relate to computer science. There are certain types of business validation that are not applicable.

Well-suited for Type Annotations
Likely not well-suited for Type Annotations
  • Null value checks.
  • Numeric range checks.
  • Basic type checks, such as regular expressions.
  • Assignments and updates (e.g. read-only)
  • Dataflow validation detection (e.g. have the incoming function arguments gone through the right validation functions)
  • This function cannot be executed outside certain hours or on government holidays.
  • Access to a feature requires a certain account-type.

Appendix

Annotations in core Java

There is no set of default type annotations available out of the box in the Java SE 8 platform. All previous examples in this post used the Checker Framework. The Type Annotations in Java SE 8 focused on the ability to put annotations in the right areas to describe a program. A separate, currently inactive JSR-305 (not part of Java 8) exists for identifying what those annotations should be.

The Checker Framework currently uses Java Annotation Index Files to gain comparable support for the core Java runtime and targeted libraries, or previous Java versions like Java SE 7 or 6.

Removal of APT

JDK 8 also removes a legacy annotation processing tool, named apt. Few users should be affected by this change. This was done as part of JEP 117 because everything required for annotation processing appears in either javax.annotation.processing or javax.lang.model.

Wednesday Mar 19, 2014

Java SE 8 is available for download

Developers and system administrators can now download the first official release of Java SE 8. This is the first major release since Java 7 (July 2011) and features significant improvements in speed, stability, and security. Complete details about Java SE 8 and launch events can be found at The Java Source and Mark’s blog.

Java 8 - Create the future

Please also join the main launch webinar on March 25.

New developers learning Java 8 may also view the Java Tutorials or focus on Java FX 8.

Coordinated Releases

The launch of Java SE 8 was a well-coordinated event across many lines of the Java community. Simultaneous support is available in all IDEs:

Many open source projects have also taken part in the quality outreach campaign. This work involved application compatibility testing as well as using the new JDeps utility to identify reliance on internal JDK APIs.

Auto-Updates and the Security Baseline

System Administrators or end-users looking at compatibility testing must explicitly download Java SE 8 from the Oracle Technology Network. End-users will not be auto-updated to Java SE 8, nor will it be available from Java.com until a future (currently undetermined) date.

The Security Baseline represents the latest critical patch update within its own family and is always documented within the latest release notes -- this baseline is separate for 8, 7, and 6.

Tuesday Jan 28, 2014

JDK 8 will use TLS 1.2 as default

Transport Level Security (TLS) is designed to encrypt conversations between two parties and ensure that others can neither read nor modify the conversation. When combined with Certificate Authorities, a proper level of trust is established: we know who is on the other end of the conversation and that conversation is protected from eavesdropping/modification.

Support for TLS 1.2 first appeared in JDK 7 (2011). For compatibility reasons, it is enabled by default on server sockets but disabled on clients. Since that time, the industry has made considerable improvements to address interoperability and backwards compatibility.

We are setting JDK 8 to use TLS 1.2 as the default for two reasons:

  1. TLS is backwards-compatible. After upgrading the default to 1.2, systems using 1.1 and 1.0 will continue to function*.
    1. * Unless configured to use an algorithm that was removed for security reasons. Few systems are affected by this.
    2. For a complete description of TLS 1.2, please see RFC 5246.
    3. A quick summary of TLS/SSL differences is available from yaSSL.
  2. It strengthens the protection of internet communications against eavesdropping.

For those testing JDK 8 early access, this first occurred in build 122.

TLS is transparent to most users and developers. For those that would like more details, we will cover:

  • Threats and the role of encryption
  • Compatibility with the JDK and other systems
  • Understanding your TLS implementation
  • Other considerations for TLS

Threats and the role of encryption

With a new well-motivated IETF working group for encryption as well as wide industry support for TLS 1.2, the time is right to update system defaults.

Qualys SSL Labs has done great research in depicting a threat model for TLS. Their best practices in dealing with the TLS threat model (specifically "2.2 use secure protocols") support this move.

Compatibility with the JDK and other systems

TLS 1.2 is designed to be backwards-compatible as described in the RFC Appendix E (above). If a 1.2 client connects to a server running a lower version, the client will adjust. If a lower client connects to a server running 1.2, the server will adjust. Because of backwards-compatibility, clients supporting TLS 1.2 will receive improved communications and older clients will continue to function.
  • We added support for TLS 1.2 in JDK 7 (July 2011) although it was not the default. JDK 8 (March 2014) will use TLS 1.2 as the default.
  • OpenSSL added support for TLS 1.2 in version 1.0.1 (March 2012). Most Linux distributions and scripting languages use OpenSSL.
  • Microsoft supported TLS 1.2 in Windows 7. Internet Explorer and .NET follow accordingly. TLS 1.2 was first enabled by default in Internet Explorer 11 (October 2013).
  • Firefox turned TLS 1.2 on by default in version 27 (February 2014).
  • Chrome supported TLS 1.2 in version 29 (August 2013).
  • Etc.

Adoption statistics from the Trustworthy Internet's SSL Pulse show a sufficient number of internet-facing systems using TLS 1.2 and compatible ciphers.

Understanding your TLS implementation

Developers or System Administrators can test servers and clients through the Qualys SSL Labs (server or client) or a different How’s My SSL website.

System Administrators can view their system’s TLS implementation to monitor clients or disable specific TLS versions. For example some system administrators in highly sensitive businesses may want to disable older TLS versions from ever being used.

View your client’s version through a GUI

  1. Open the Java Control Panel
  2. Navigate to the Advanced tab.
  3. At the bottom, there is an “Advanced Security Settings.”
  4. Check or uncheck the "Use TLS X.Y" box.

On a server or without a GUI

  1. To set this for everything:
    1. Open the deployment.properties file, either user-level or system-level.
    2. Set the appropriate property
      deployment.security.TLSvX.Y=false
  2. To set for a specific application or script:
    1. Use the startup flag -Ddeployment.security.TLSvX.Y=false

Other Considerations for TLS

The InfoQ article, Keeping Your Secrets, covers additional information for developers looking to understand more about transport security and encryption. Outside the role of TLS protocol version, that article covers good techniques to safeguard information:

For System Administrators (or some Developers): Perfect Forward Secrecy can be used in Java TLS connections. Using Perfect Forward Secrecy protects past conversations: in the event that if keys are lost in the future, someone cannot decrypt past conversations. As is common with TLS implementations, Perfect Forward Secrecy is not enabled by default. Those that do want to use it can update their https.cipherSuites property. Common values for this property are:

  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA
  • Anything on the Algorithm Standard Name list that start with TLS (Transport Level Security) followed by a type of DHE (Diffie-Hellman Exchange).

About

Science Duke
This blog contains topics related to Java SE, Java Security and Usability. The target audience is developers, sysadmins and architects that build, deploy and manage Java applications. Contributions come from the Java SE Product Management team.

Search

Categories
Archives
« July 2015
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
       
Today