Make Calendar gadgets made with JavaFX compatible with Java SE 9

Make Calendar gadgets made with JavaFX compatible with Java SE 9

I started writing this article when I tried to post it on JavaFX Advent Calendar 2017, but I couldn't take the time due to work and influenza, so I later It is open to the public.

Introduction

In the corner of your desktop is a desktop gadget that displays small GUI features such as a clock and calendar. In the old days, it was a gadget of Windows Vista / 7, such as xclock and oclock of X Window System. However, Windows gadgets have been deprecated since Windows 8 due to vulnerabilities.

Therefore, in Windows 8 or later (10 etc.), if you want a desktop gadget, you will have to search for such a program separately, obtain it, and use it. There are many free software that look like desktop gadgets, but I thought it would be good for programmers to try using their own gadgets, so I made a little with JavaFX. This area was introduced in the next session at JJUG CCC 2017 Spring.

As a desktop gadget, I wanted a clock and a calendar, so I made a calendar gadget following the clock gadget.

This time, the content is to make the calendar gadget created with Java SE 8 compatible with Java SE 9.

Calendar gadget

The calendar gadget uses JavaFX's DatePicker control to display a small calendar (month) in one corner of the desktop.

image.png

The repository of programs (source code) is as follows (Github). CalendarGadget

This program is written in Java SE 8. I wrote the way to make a calendar gadget with Java SE 8 in the following blog.

Java SE 9 compatible items for calendar gadgets

We will make this calendar gadget compatible with Java SE 9, and at that time we will do the following.

  1. Support for package changes of DatePickerSkin class
  2. Supports Java SE 9 Module System (JPMS)
  3. Supports the current date when returning from sleep / hibernation

The corresponding policy is briefly described below. After that, describe the correspondence details of each item.

Support for package changes of DatePickerSkin class

The calendar display part of the DatePicker control uses the DatePickerSkin class, which has a package name when changing from Java SE 8 to Java SE 9.

From

It was changed to. We have upgraded from an internal API to a public API. However, you need to change the source code between Java SE 8 and 9.

Supports Java SE 9 Module System (JPMS)

From Java SE 9, in addition to the conventional method of referencing class files and JAR files at runtime with the classpath, a method of referencing module files at runtime with the modulepath has been added.

Here, since it is a big deal, we will correspond to the module.

Correspondence when returning from sleep / hibernation

The calendar gadget marks today's date so that you can see the current date (a rectangle encloses today's date). Therefore, if the date changes while you are running the calendar gadget, you will need to change the markings on today's date to the next day. When creating with Java SE 8, the time until the end of the day is calculated when the calendar gadget is started, and the process to update today's date is executed after that time has passed (implemented using ScheduledExecutorService) ..

However, if the PC running the gadget goes to sleep or hibernation in the middle, this process will not be activated at the desired timing. Therefore, I try to check the date update process every hour as a painstaking measure.

Starting with Java SE 9, an event has been added to let you know that your PC has gone to sleep or hibernate, and that it has been restored from sleep or hibernation.

Therefore, I will take advantage of this event.

Implementation of DatePickerSkin for Java SE 9

Rename the package of DatePickerSkin, which is the first Java SE 9 support item.

CalendarGadgetApp.java


- import com.sun.javafx.scene.control.skin.DatePickerSkin;
+ import javafx.scene.control.skin.DatePickerSkin;

Setting for Java module system

Supports Java module system, which is the second Java SE 9 support item. This time, I will mainly describe the correspondence in NetBeans.

Use NetBeans IDE 9 Development

NetBeans 9 for Java SE 9 (JDK 9) has not yet been officially released at this time. So we'll use the development version of NetBeans 9. http://bits.netbeans.org/download/trunk/nightly/latest/

In NetBeans 9, the project type corresponding to the module system is Java Modular Project. Project types such as JavaFX applications are not compatible with modular systems. Therefore, I will recreate the project once.

Create a new project

Create an empty working directory and create two new projects under it.

  1. Create a project name [GadgetSupport] under an empty working directory in the [File] menu> [New Project]> [Java]> [Java Modular Project].
  2. Select [File] menu> [New Project]> [Java]> [Java Modular Project] and create a project name [Calendar Gadget] under an empty working directory.

The following is the first step on the New Project wizard screen. [Java Modular Project] has been added to the category [Java].

image.png

Next is the next step on the New Project wizard screen. Specify the location and project name to create the project.

image.png

A Java Modular Project can contain multiple modules under one project. Initially the module is empty. This time, we will include only one module in each of the two projects we created.

Define one module in each project.

  1. Right-click on the GadgetSupport project, select New> Module and enter com.torutk.gadget.support for the module name.
  2. Right-click on the CalendarGadget project, select New> Module and enter com.torutk.gadget.calendar for the module name.

The next step is to select a project and add a module.

image.png

On the New Module screen, enter the module name.

image.png

The module name is the name of the most important package to be published (according to the Java language specification 6.1 naming convention).

When you create a module, module-info.java is generated.

image.png

Unlike traditional NetBeans project configurations, a module folder is created under the project, a classes folder is created under it, and the source file packages are placed here. module-info.java is located at the top level (no package name), so it is placed inside what NetBeans describes as the .

The directory / file structure looks like this:

image.png

Bring the source file here. When I bring in the source file, the import statement is all in error.

image.png

Add module definition (GadgetSupport)

We will describe the module definition of the com.torutk.gadget.support module of the GadgetSupport project.

In the Java SE 9 module system, if you use a class other than the one that belongs to the java.base module, you need to explicitly describe the module that contains the class to be used in the module definition file (module-info.java).

Hovering the cursor over the (: exclamation :) mark at the beginning of the error line pops the error message. The following screen pops up an error in java.util.prefs.Preferences.

image.png

Add the java.prefs module to module-info.java according to the error message.

module-info.java


module com.torutk.gadget.support {
    requires java.prefs;
}

Similarly, add the module usage description corresponding to the error of the remaining import statement.

image.png

The error message tells you that you need two modules, javafx.graphics and javafx.controls. You may describe these two modules in module-info.java, but since javafx.graphics and javafx.controls have a transitional dependency, specify only javafx.controls in the module definition on the application side. Will solve it.

module-info.java


module com.torutk.gadget.support {
    requires java.prefs;
    requires javafx.controls;
}

The module definition of javafx.controls has the following description.

    requires transitive javafx.base;
    requires transitive javafx.graphics;

Since transitive is specified, two modules, javafx.base and javafx.graphics, are automatically available for modules that use javafx.controls.

Next, since the GadgetSupport project is a utility library, you need to publish the package com.torutk.gadget.support to your application (such as CalendarGadget).

module-info.java


module com.torutk.gadget.support {
    requires java.prefs;
    requires javafx.controls;
    exports com.torutk.gadget.support;
}

This will build. Compiled class files are generated under build / modules and JAR files are generated under dist.

GadgetSupport
  +-- build
  |     +-- modules
  |           +-- com.torutk.gadget.support
  |                 +-- module-info.class
  |                 +-- com
  |                       +-- torutk
  |                             +-- gadget
  |                                   +-- support
  |                                         +-- TinyGadgetSupport.class
  +-- dist
  :     +-- com.torutk.gadget.support.jar

Addition of module definition (CalendarGadget)

We will describe the module definition of the com.torutk.gadget.calendar module of the Calendar Gadget project.

This time, in addition to the Java SE 9 standard API, I will use the library I created (GadgetSupport), so add the library dependency in the project settings. GadgetSupport was created as a module, so set it by adding a module.

Right-click on the CalendarGadget project and select Properties. Select Libraries in the left pane, click the Compile tab in the right pane, click the + on the far right of Modulepath, and select Add Project ...

image.png

The "Add Project" screen opens, so select the Gadget Support project. Then, the JAR file generated by the project will be displayed.

image.png

Describe the dependent modules in the module definition file.

module-info.java


module com.torutk.gadget.calendar {
    requires java.logging;
    requires java.prefs;
    requires javafx.controls;
    requires com.torutk.gadget.support;
}

I got a compile error, so I'll try it. Then I get the following error:

run:
Exception in Application constructor
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:945)
Caused by: java.lang.RuntimeException: Unable to construct Application instance: class com.torutk.gadget.calendar.CalendarGadgetApp
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:963)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:198)
	at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.IllegalAccessException: class com.sun.javafx.application.LauncherImpl (in module javafx.graphics) cannot access class com.torutk.gadget.calendar.CalendarGadgetApp (in module com.torutk.gadget.calendar) because module com.torutk.gadget.calendar does not export com.torutk.gadget.calendar to module javafx.graphics
	at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
	at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:479)
	at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$8(LauncherImpl.java:875)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417)
	at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
	at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
	at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
	... 1 more

Here are the messages that are the main source of the error:

com.sun.javafx.application.LauncherImpl (in module javafx.graphics) cannot access 
class com.torutk.gadget.calendar.CalendarGadgetApp (in module 
com.torutk.gadget.calendar) because module com.torutk.gadget.calendar does not export 
com.torutk.gadget.calendar to module javafx.graphics

It states that the JavaFX application class CalendarGadgetApp must be accessible from the javafx.graphics module.

You can execute it by adding the following statement to the module definition file.

opens com.torutk.gadget.calendar;

This time, it can be executed with exports instead of opens, but when using FXML or binding in the future, it is necessary to use opens instead of exports because the application side class will need reflection access from the javafx module. .. Therefore, in the module definition of JavaFX application, specify itself with opens.

Supports the current date when returning from sleep / hibernation

This is the last of the three items.

First, it receives the wakeup from sleep / hibernation of the OS as an event.

   private void initResumeProc() {
        Desktop desktop = Desktop.getDesktop();
        desktop.addAppEventListener(new SystemSleepListener() {
            @Override
            public void systemAboutToSleep(SystemSleepEvent e) {
                logger.info("Detect system about to sleep.");
            }

            @Override
            public void systemAwoke(SystemSleepEvent e) {
                logger.info("Detect system awake.");
                if (schedule != null ) {
                    schedule.cancel(true);
                }
                crossoverDate();
            }
        });
    }

Pass an instance of the implementation class of the SystemSleepListener interface to the addAppEventListener method added from Java SE 9 to the java.awt.Desktop class. Then, when the OS goes to sleep or hibernate (suspend), and when it wakes up, the corresponding method of SystemSleepListener is called. This time, set the date update process (crossoverDate method) again at that timing. If the date update process schedule has already been set, cancel the schedule once.

Repository

The following is the Calendar Gadget repository with Java SE 9 support.

https://github.com/torutk/CalendarGadget/tree/jdk9_module

Recommended Posts

Make Calendar gadgets made with JavaFX compatible with Java SE 9
Make SpringBoot1.5 + Gradle4.4 + Java8 + Docker environment compatible with Java11
Refactored GUI tools made with Java8 + JavaFX in 2016
[Rails] Make pagination compatible with Ajax
Make a slideshow tool with JavaFX
Run an application made with Java8 with Java6
How to make a Java calendar Summary
Make something like Java Enum with Typescript
Achieve OpenSSL compatible encryption with Java / PHP
Vending machine made with Java (domain driven)
[Java] Make programs 10x faster with parallelStream
I tried to make Basic authentication with Java
Simple obstacle racing made with processing for Java
Students challenged Java SE 7/8 Bronze with no diligence.
Make Java code coverage more comfortable with Jacoco 0.8.0
Make Java Stream line breaks nice with eclipse
Java SE Development Kit 8u161 --setup with alternatives
Easy to make LINE BOT with Java Servlet