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.
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.
The calendar gadget uses JavaFX's DatePicker control to display a small calendar (month) in one corner of the desktop.
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.
We will make this calendar gadget compatible with Java SE 9, and at that time we will do the following.
The corresponding policy is briefly described below. After that, describe the correspondence details of each item.
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.
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.
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.
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;
Supports Java module system, which is the second Java SE 9 support item. This time, I will mainly describe the correspondence in NetBeans.
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 an empty working directory and create two new projects under it.
The following is the first step on the New Project wizard screen. [Java Modular Project] has been added to the category [Java].
Next is the next step on the New Project wizard screen. Specify the location and project name to create the project.
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.
The next step is to select a project and add a module.
On the New Module screen, enter the module name.
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.
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:
Bring the source file here. When I bring in the source file, the import statement is all in error.
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.
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.
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
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 ...
The "Add Project" screen opens, so select the Gadget Support project. Then, the JAR file generated by the project will be displayed.
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.
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.
The following is the Calendar Gadget repository with Java SE 9 support.
https://github.com/torutk/CalendarGadget/tree/jdk9_module
Recommended Posts