Friday, October 27, 2023

Stay up to date...

 Aloha,

after quite some time I thought it's time again for a little blog post. During one of my travels I've created a little tool in Swift that works on the latest version of Apple's MacOS operating system called Sonoma. 

The main thing that was interesting to me is the fact that you now also can have widgets on MacOS. So I've created a little tool called JDKMonitor which is also available in the MacOS App Store.

Because I always forget when the next update and release of the OpenJDK project is scheduled I thought why not creating a widget that simply tells me the date and the number of days I have to wait.

Because you cannot simply create a standalone widget but it has to come with an application, JDKMonitor also offers some addional functionality. 

It will show you the dates of the next OpenJDK update and release, lets you download one of the last 4 Long Term Support versions (LTS), either as Java Runtime Environment (JRE) or as Java Development Kit (JDK) and if available, it will also give you the opportunity to download a version bundled with JavaFX.

The downloads are based on Azul's build of OpenJDK named Zulu which you can download and use for free also in production. You can also click a link that will open the Azul Zulu download page in your default browser and if you want to read the release notes of the latest LTS builds of OpenJDK it will also offer you a link to that.

The following images show the application and the available widgets that you can place on your desktop. When you click on the widget, it will open JDKMonitor.



As you can see on the image above, it contains an image of Jay, the foojay mascott and if you click on it, it will open the foojay.io page in your default browser to get the latest news from the Java community :)

All in all it's nothing very special but fun...so keep coding...

Wednesday, May 17, 2023

Keep track of your Java inventory...

JavaFinder

Do you ever wondered how many Java distributions you have installed on your machine? I not only mean versions that you use for development but also versions that come bundled with an application. Last weekend I've decided to write a little tool that can help me figuring that out...JavaFinder.

This is a simple command line tool that will search a given path including it's subfolders for any OpenJDK distribution or GraalVM derivate. So it's nothing really fancy but could be useful sometimes.


Usage

Per default it will print all distributions found in json format on the console, when I run it on my Macbook Pro without any parameters like this javafinder, the output will look as follows

{
   "search_path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines",
   "sysinfo":{
      "operating_system":"macos",
      "architecture":"arm64",
      "bit":"64"
   },
   "distributions":[
      {
         "vendor":"Azul",
         "name":"Zulu",
         "version":"17.0.7",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-17.jdk/zulu-17.jdk/Contents/Home/",
         "build_scope":"OpenJDK"
      },
      {
         "vendor":"Azul",
         "name":"Zulu",
         "version":"8.0.372+7",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/jre/",
         "build_scope":"OpenJDK"
      },
      {
         "vendor":"Azul",
         "name":"Zulu",
         "version":"8.0.372+7",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/",
         "build_scope":"OpenJDK"
      },
      {
         "vendor":"Azul",
         "name":"Zulu",
         "version":"11.0.19",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-11.jdk/zulu-11.jdk/Contents/Home/",
         "build_scope":"OpenJDK"
      },
      {
         "vendor":"Azul",
         "name":"Zulu",
         "version":"20.0.1",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-20.jdk/zulu-20.jdk/Contents/Home/",
         "build_scope":"OpenJDK"
      },
      {
         "vendor":"Oracle",
         "name":"Graal VM CE",
         "version":"22.3.1",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.3.1/Contents/Home/",
         "build_scope":"GraalVM"
      },
      {
         "vendor":"Azul",
         "name":"Zulu",
         "version":"21-ea+21",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-21.jdk/zulu-21.jdk/Contents/Home/",
         "build_scope":"OpenJDK"
      },
      {
         "vendor":"Gluon",
         "name":"Gluon GraalVM",
         "version":"22.1.0.1",
         "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/gluon-graalvm/Contents/Home/",
         "build_scope":"GraalVM"
      }
   ]
}


Well that's not completely true because the output is not formatted like this but in a more compact format without linebreaks and intendation. If you prefer something more simple there is also the possibility to output the results in csv format. In this case you need to run the program as follows ```javafinder csv```and the output will look like follows:


Vendor,Distribution,Version,Path,Type
Azul,Zulu,21-ea+21,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-21.jdk/zulu-21.jdk/Contents/Home/,OpenJDK
Oracle,Graal VM CE,22.3.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.3.1/Contents/Home/,GraalVM
Azul,Zulu,20.0.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-20.jdk/zulu-20.jdk/Contents/Home/,OpenJDK
Azul,Zulu,11.0.19,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-11.jdk/zulu-11.jdk/Contents/Home/,OpenJDK
Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/jre/,OpenJDK
Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/,OpenJDK
Azul,Zulu,20.0.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-20.jdk/,OpenJDK
Azul,Zulu,21-ea+21,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-21.jdk/,OpenJDK
Gluon,Gluon GraalVM,22.1.0.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/gluon-graalvm/Contents/Home/,GraalVM
Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/,OpenJDK
Azul,Zulu,17.0.7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-17.jdk/,OpenJDK
Azul,Zulu,11.0.19,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-11.jdk/,OpenJDK
Azul,Zulu,17.0.7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-17.jdk/zulu-17.jdk/Contents/Home/,OpenJDK
Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/jre/,OpenJDK

```

On Linux and Mac you simply can save that into a file using javafinder csv > jdks.csv


In the examples above I did not specify any paths to search for distributions which will make JavaFinder search in

pre-defined folders for each operating system

- Windows: C:\Program Files\Java\

- Linux: /usr/lib/jvm

- MacOS:  /System/Volumes/Data/Library/Java/JavaVirtualMachines/


In case you would like to search in a specific path you can simply add it to the command as follows:

- MACOS
  Search all paths in /Users/hansolo by executing
  >: javafinder /Users/YOUR_USER_NAME -> output will be in json format
  >: javafinder csv /Users/YOUR_USER_NAME -> output will be in csv format

- LINUX
  Search all paths in your home folder
  >: javafinder /home/YOUR_USER_NAME -> output will be in json format
  >: javafinder csv /home/YOUR_USER_NAME -> output will be in csv format

- WINDOWS
  Search all paths in your home folder
  >: javafinder.exe c:\Users\YOUR_USER_NAME -> output will be in json format
  >: javafinder.exe csv c:\Users\YOUR_USER_NAME -> output will be in csv format


ATTENTION:

Please be aware that scanning for distributions can take some time, esp. when you point to a path like your user home folder etc. The less subfolders are in the given path, the faster the scanning will be.


Download

JavaFinder is available for Windows (x64), Linux (x64 and aarch64) and MacOS (x64 and aarch64) and you can find it over at github

That's it for today...keep coding...

Thursday, March 3, 2022

DiscoCLI

 Aloha,

As some of you might know I've created the so called DiscoAPI for foojay.io. This api should help you to get an OpenJDK distribution of your choice. In addition to the api itself I've also created different plugins for IDE's and browsers and also some tools like JDKMon.

That's all good stuff but I was always missing a command line tool which enables me to simply download an OpenJDK package of my choice in the terminal. Thank god there is Picocli which makes it possible to create a command line interface using Java.

If you use Picocli in combination with GraalVM's native image feature you can create a single binary which was exactly what I was looking for.

And even better...you can build the binaries for x64 based platforms easily using github actions.

Long story short, I've created the Disco Command Line Client or short DiscoCLI which makes it easy to download an OpenJDK distribution of your choice.


Here are some examples on how to use it:

Get Zulu with version 17.0.2 for the current operating system including JavaFX:

discocli -d zulu -v 17.0.2 -fx


Get the latest version of JDK 16 for Liberica on Windows:

discocli -d liberica -v 16 -os windows -latest


Get the JDK 17.0.2 of temurin for macos with aarch64 as a tar.gz and store it to a folder

discocli -d temurin -v 17.0.2 -os macos -arc aarch64 -at tar.gz -p /Users/hansolo


In case a JDK pkg cannot be found discocli will try to give you the available pkgs.

discocli -d liberica -v 12 -os linux -arc x64 -fx -latest

Sorry, defined pkg not found in Disco API

Packages available for Liberica for version 12:
discocli -d liberica -v 12.0.2 -os linux -lc glibc -arc amd64 -at tar.gz -pt jdk
discocli -d liberica -v 12.0.1 -os linux -lc glibc -arc amd64 -at tar.gz -pt jdk
discocli -d liberica -v 12 -os linux -lc glibc -arc amd64 -at tar.gz -pt jdk

It's also possible to simply check what packages are available for given set of parameters. If we would like
to know what packages are available of Zulu for Macos on aarch64 that come as tar.gz and offer a jdk for 
version 17.0.2, we could type the following:

discocli -f -d zulu -v 17.0.2 -os macos -arc aarch64 -pt jdk -at tar.gz

Packages found for Zulu for version 17:
discocli -d zulu -v 17.0.2 -os macos -lc libc -arc x64 -at tar.gz -pt jdk -fx
discocli -d zulu -v 17.0.2 -os macos -lc libc -arc x64 -at tar.gz -pt jdk

As always the code is available on github if you would like to build it yourself but if you simply would
like to download the binary, feel free to get it from the github releases.
I provide binaries for the following platforms:
- Macos x64 (but this also works on M1 chips because of Rosetta 2)
- Linux x64
- Linux aarch64
- Windows x64

For those of you that are asking themselves if this is not the same as SDKMan? No, it's not, SDKMan is much more
than this. DiscoCLI really only helps you downloading an OpenJDK distribution and that's it, no installation 
and nothing else than OpenJDK distributions. 
And in the near future DiscoCLI might also be available via SDKMan using JReleaser... :)

That's it for today...so keep coding... :)

Saturday, January 15, 2022

GlucoStatusFX

Aloha,

Two years ago I wrote an iOS app to monitor the diabetes of our son. This app (GlucoTracker) is written in Swift using SwiftUI and I still use it today on my iPhone and my AppleWatch.

After I've created that app I decided it would be nice to also have such an app on my Mac and so I wrote a Macos app using Swift and SwiftUI (GlucoStatus) that I run on all of my Macs.

Last week I thought by myself it might be a nice exercise to port this native Swift Macos app to JavaFX. Well I was really surprised how easy it was to rewrite this app in Java (I'm just more used to Java than to Swift which might be the main reason for this).

So the app gets it's data from a Nightscout server that you have to setup to monitor the blood glucose values. And in addition you somehow need to feed the blood glucose data into the Nightscout server which usually is done by using a specific sensor like the Dexcom G6, the Freestyle Libre, Enlight or others.

Meaning to say without a Nightscout server my app is useless.

But if you have such a server in place you should be able to use the app.

So the main screen looks as follows:


In the upper (colored) part you will see the current value in large letters. Below it you will find the 5 last delta values which can help you to figure out the current trend (this assumes that the values from you sensor will be updated in intervals of 5 minutes.

Then there is the date and time of the last update and the average of the selected range.

On top of the window you can select the range that should be visualized and used for the statistics.

To setup the application you can click on the little button with the gear on the upper right corner and it will show you the following screen:


In the preferences screen you first of all have to set the url of your nightscout server (e.g. https://YOUR-DOMAIN.herokuapp.com).

In addition you can define if you would like to get notifications for different situations (e.g. the value is low, or acceptable low etc.). Except for the "too low" and "too high" situations you can define whether you would like to get a notification. For all situations you can enable/disable an additional sound that will be played with the notification.

Then you can also define intervals for situations like "too low" or "too high". This means that for example if your blood glucose value is too high the app will show you a notification with the given interval (e.g. every 5 minutes or every 20 minutes etc.)

In the lower part of the preferences screen you can then also define the different ranges that you would like to use (e.g. normal values should be within the range of 70-110 mg/dl etc.)

You could also switch to another unit that is more often used in the US which is mmol/l instead of mg/dl.

On the main screen you will also find 3 icons in the colored area. The rounded arrow on the upper right corner will reload the values (this is usually not needed, only in case you would manually update the values). 

The app pings the Nightscout server every minute to check for the latest value.

The icon on the lower right will show you the "time in range" chart which looks as follows:


This chart will simply show you how often your blood glucose values have been in the defined ranges in the given time range (defined by the buttons on top of the main screen e.g. 7 days, 24 hours etc.)

The last icon that you will find on the lower left corner will show you a pattern view of the values from the last week and it looks as follows:


On top you will find the HbAc1 value that is calculated from the last 30 days. Below that you will find a list of patterns that have been identified by the app.

And at the bottom you will see a chart based on the values from the last week that shows the median of all values of a given hour of the day. This makes it possible to identify times where your values are too high or too low. The gray shaded area covers the percentiles between 10 and 90%.

Like I said in the beginning the main reason for doing this was to see how easy it is to port an existing Swift app to JavaFX and give it more or less the same look and feel.

Because I originally created the app for Macos, I did not create a version that looks like a native Windows version yet but it will always look like on the images above.

Because Macos has some special controls (e.g. the Switch), I created a little helper library called AppleFX that you can also find on github and maven central. It does not cover all available controls but only the ones I needed for this app. But if I will find more time I will probably add more Macos controls to the libray.

Be aware that you also need my Toolbox and ToolboxFX libraries to use the AppleFX lib.

As always you can find the source code and also the binaries over at github.

There are no versions for Linux yet because this is again an app that runs in the background and sits in the system tray. Unfortunately this is not really supported on Ubuntu at the moment which is the reason why I did not created installers yet. But I will work on that and will probably add them in the future.

This does not mean that it does not work on Ubuntu, you can run it, create the installers and so on but it does not behave like it should because it should sit in the menu bar and that does not really work yet.

Here are also the links to the latest release:


One last thing...the app is localized and currently I support Germany and English but it would be awesome I could provide more localizations for other languages...so if you are willing to help...you are very welcome...just create a pull request on github or ping me.

And that's it for today...so enjoy your weekend and keep coding...


Wednesday, January 5, 2022

Holiday fun...

 Aloha and a happy new year...

I took the first week of 2022 off and because I love coding I was looking for something that I might add to one of my libraries.

It was not too hard to find something interesting and I decided to give it a try...the Radial Tidy Tree...

For those of you that have no idea what I am talking about...here is a little example from the web...


It is a tree structure that is visualized using a radial layout.

Looks like a fun thing to do but it really gave me some time to get it right. First of all (as nearly always) I had no real use case for it but just wanted to be able to create a chart like that.

So I decided to simply visualize a year. The root node has 4 child nodes, the 4 quarters and each quarter has 3 child nodes, the months of each quarter. Finally each month has it's specific number of days.

That's not real useful data but at least you can use it to create a Radial Tidy Tree. In principle it looks like an easy task but there are some things that are not that easy to solve. 

First of all you have to create the tree structure which is easy using my TreeNode class which I already used for the Sunburst chart. For this one I had to add more properties to it like x, y and angle. Thanks to the java.time package the creation of the tree was easy.

I won't show all the code here but if you like you can simply head over to github and check it out there...

The really tricky part was figuring out the angle step between the items on each level and I tried different approaches before I finally found a way that worked for me. 

Once I was able to place the items in the right place the next thing was to create all the bezier curves between the items to make it look good. And the last step was to put the text in the right position and rotate it correctly.

Well...long story short...here is the result...


And I really like the way the result looks :)

As I already mentioned, I do not have a real use case for it and therefor I cannot guarantee that the tree will work for all use cases. But I did a few other tests and it seems to be ok.

The RadialTidyTree can be found in the latest release of my charts library (17.1.2) which you can either get on github or on maven central.

As with all the charts in my charts library you can find a class that shows how to use it in the test package, for this one just look for RadialTidyTreeTest.java.

And that's it...so keep coding... :)






Friday, December 31, 2021

Harmony...finally

 Aloha,

When you create different libraries and components you find yourself writing the same code in different places over time. In principle that's ok, except you combine those libraries and components. In this case you suddenly have the same classes twice or even more often in your code base. When I started creating Medusa, TilesFX and Charts I did not really think about the possibility to combine those libraries in one project at some point in the future. The main reason for this is that I never plan to create those libraries but they simply grow from components to libraries over time. 

The thing that started me thinking about to re-use more code between those libraries was a project where I needed TilesFX and Charts in the same project. Both of these libraries came with a Country class with different properties and methods. Now in that project I needed both of them and I needed to write some ugly code to convert between them

That was the starting point of the Countries library which you can now find either on github and on maven central.

But then I saw that there are other classes that I more or less use in both libraries and I decided to put those shared classes in a separate project. Because there are projects that use JavaFX and others which don't, I decided to create two projects:

  • eu.hansolo.toolbox
  • eu.hansolo.toolboxfx

Toolbox:

This library contains the code from my Evt project, meaning to say an event system which is similar to the JavaFX events.

Then I also added the code from my Properties project to the Toolbox. The properties are very similar to the JavaFX properties incl. binding. And in the Toolbox they will use the Evt events for property changes.

There are now also tuples in the Toolbox which sometimes can come in handy. They are not that fancy and their getters and setters do look like getA(), get(B) and setA(), setB(). Not so nice but useful.

The last thing I've added is the code from my UnitConverter which contains all kinds of different units and a converter that can convert between them (in the same category e.g. Temperature).

Then there is a Helper method that contains all sorts of methods that I use here and there in my code e.g. clamp() etc.

ToolboxFX:

Then there is the ToolboxFX library which depends on JavaFX but that does not only contain JavaFX related stuff. Here you will find things like my ConicalGradient, FontMetrix, GradientLookup, the Fonts that I do use often and other stuff like Point, Bounds, CornerRadii, Dimension, Location Po, CatmullRom etc.

ToolboxFX depends on Toolbox so you need to add Toolbox too if you use ToolboxFX.

This is stuff that I use a lot in the Charts library but also in TilesFX and Medusa.

But that's not enough, I've also separated the HeatMap from Charts and Countries and put it in a separate project.

So what does that mean for you as a user of one of my libraries?

  • Update your dependencies
    • TilesFX depends on:
      • eu.hansolo:toolbox:17.0.6
      • eu.hansolo:toolboxfx:17.0.15
      • eu.hansolo.fx:heatmap:17.0.3
      • eu.hansolo.fx:countries:17.0.16
    • Medusa depends on:
      • eu.hansolo:toolbox:17.0.6
      • eu.hansolo:toolboxfx:17.0.15
    • Charts depends on:
      • eu.hansolo:toolbox:17.0.6
      • eu.hansolo:toolboxfx:17.0.15
      • eu.hansolo.fx:heatmap:17.0.3
      • eu.hansolo.fx:countries:17.0.16
  • Use the new event system
    • If you make use of things like TileEvent, you should change to TileEvt etc. The best way to see how it works is to take a look at the Demo classes within the library source code.

ATTENTION: The libraries are not backwards compatible due to the new event system !!!


The new versions of TilesFX, Charts and Medusa that will make use of the shared libraries will all start with version 17.1.0. There is still a lot of stuff to streamline (e.g. removing methods from the libraries Helper classes because they are already covered by Helper  in Toolbox and HelperFX in ToolboxFX but for that I need more time.

So here are all libraries that are new or have changed:

I will probably also use the Toolbox and ToolboxFX in future components and libraries.
So that was my holiday project and I'm really happy with it because now I could more easily use combinations of my libraries in projects.

I'm pretty sure there are still some things that do not work correctly, so please, if you stumble upon a problem do not hesitate to file an issue with some example code in the github repo.

I wish all of you a Happy New Year...and hopefully we will get rid of that Covid thing pretty soon...so stay healthy...and keep coding...


Thursday, December 23, 2021

DateRanger...

 Aloha,

Last week I needed some kind of a date picker which I can use to select ranges of dates. So I knew that I once created such a control when I was working for Canoo back in the days. But when I found it I saw that it was realized in JavaFX 2.0 that was based on JDK7 and made use of Skin and Behavior classes which changed in JDK8. 
Because I was not keen on rewriting that stuff I decided to simply create a new control...just for the fun of it :)
And because I used a lot of Canvas recently I made the decision to make use of CSS for this control and not use the Canvas node for it. Using CSS makes the whole thing more usable for standard applications because you can easily style the control to your needs where when using the Canvas node it needs more programming effort to get the same styleability.
So the first step was to figure out a control that I like to have some kind of template.
And I found this one...

It's not really fancy but I really like it's compact look which has all the info that I need. So I've created my version of it which looks as follows:


As you can see I more or less created a copy of the control. So the next step was to add the functionality to select a range of dates.
I've simply added a key listener and if you select a date by clicking somewhere with the mouse you can press the `SHIFT` key with the next click and it will create a range of dates for you.
The range then looks as follows:


Most of the nodes can be styled using CSS and you will find all the available styles in the `date-ranger.css` file.
The plain DateRanger comes without the month and year label and the buttons, so you can also use it for only showing the month. If you would like to use the version above, you can use the DateRangerControl which is also part of the code. This is in principle just a BorderPane that comes with the label and buttons on the top.

It's nothing really fancy but maybe it will be useful for one or the other.
The code is available on github and also on maven central.

Well I guess that's it for 2021...I wish all of you a merry christmas and a happy new year...oh and keep coding... :)