<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Uwe's Blog</title><link href="https://uwesander.de/" rel="alternate"/><link href="https://uwesander.de/feeds/all.atom.xml" rel="self"/><id>https://uwesander.de/</id><updated>2022-03-06T21:23:00+01:00</updated><subtitle>Grumpy old man codes</subtitle><entry><title>Persisting an NFS4 mount point on macOS Monterey</title><link href="https://uwesander.de/nfs4-macos-monterey.html" rel="alternate"/><published>2022-03-06T21:23:00+01:00</published><updated>2022-03-06T21:23:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2022-03-06:/nfs4-macos-monterey.html</id><summary type="html">&lt;p&gt;How to auto-mount an NFS4 share after startup on macOS Monterey?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tisgoud.nl/2020/10/persistent-nfs-mount-points-on-macos/"&gt;This blog post&lt;/a&gt;
explains how to do it with basically one line in &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;192.168.200.200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nfs_share&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kr"&gt;Sys&lt;/span&gt;&lt;span class="n"&gt;tem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kd"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;my_nfs_share&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;resvport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;nfc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;65536&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;wsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;65536 …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;How to auto-mount an NFS4 share after startup on macOS Monterey?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tisgoud.nl/2020/10/persistent-nfs-mount-points-on-macos/"&gt;This blog post&lt;/a&gt;
explains how to do it with basically one line in &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;192.168.200.200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nfs_share&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kr"&gt;Sys&lt;/span&gt;&lt;span class="n"&gt;tem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="kd"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;my_nfs_share&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;resvport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;nfc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;65536&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;wsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;65536&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that the &lt;code&gt;nolockd&lt;/code&gt; option is not included here unlike in the original blog post
because this option resulted in an &lt;code&gt;illegal argument&lt;/code&gt; error when I tried to access
the mount on macOS Monterey. It might work on earlier macOS versions. And the option
is probably unnecessary for an NFS4 mount.&lt;/p&gt;</content><category term="Sysadmin"/><category term="mac"/><category term="nfs"/></entry><entry><title>Setting up an NFS Server with Linux, Mac, and Kodi Clients</title><link href="https://uwesander.de/setting-up-nfs-linux-mac-kodi.html" rel="alternate"/><published>2021-06-22T19:25:00+02:00</published><updated>2021-06-22T19:25:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2021-06-22:/setting-up-nfs-linux-mac-kodi.html</id><summary type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;We'd like to set up an NFS 3 server on a Raspberry Pi with Linux, Mac, and Kodi clients.
Kodi does not support NFS 4 out of the box, so we'll stick with NFS 3 for now.&lt;/p&gt;
&lt;h2&gt;Set up the Server&lt;/h2&gt;
&lt;p&gt;As a starting point, follow &lt;a href="https://www.raspberrypi.org/documentation/configuration/nfs.md"&gt;this tutorial&lt;/a&gt; to …&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;We'd like to set up an NFS 3 server on a Raspberry Pi with Linux, Mac, and Kodi clients.
Kodi does not support NFS 4 out of the box, so we'll stick with NFS 3 for now.&lt;/p&gt;
&lt;h2&gt;Set up the Server&lt;/h2&gt;
&lt;p&gt;As a starting point, follow &lt;a href="https://www.raspberrypi.org/documentation/configuration/nfs.md"&gt;this tutorial&lt;/a&gt; to install and set up an NFS (4) server on the Raspberry Pi.
Then, make sure to change the server configuration as follows for running an NFS 3 server (assuming you want to export the &lt;code&gt;/nfsexport/myshare&lt;/code&gt; directory:&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;/etc/exports&lt;/code&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nfsexport&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;no_subtree_check&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nfsexport&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myshare&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;no_subtree_check&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;&lt;code&gt;/etc/hosts.deny&lt;/code&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;rpcbind mountd nfsd statd lockd rquotad : ALL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;&lt;code&gt;/etc/hosts.allow&lt;/code&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;rpcbind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mountd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nfsd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;statd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lockd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rquotad&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;127.0.0.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;192.168.100.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m m-Double"&gt;192.168.100.3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;more&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, run &lt;code&gt;sudo systemctl restart nfs-kernel-server.service&lt;/code&gt; for the changes to take effect and check if your NFS server is running as expected using &lt;code&gt;sudo rpcinfo -p&lt;/code&gt;.
Your NFS share will them be available at &lt;code&gt;nfs://&amp;lt;ip-address&amp;gt;/nfsexport/myshare&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Linux Client Configuration&lt;/h2&gt;
&lt;p&gt;Add the following line to your &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nfsexport&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myshare&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mnt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Mac (Catalina+) Client Configuration&lt;/h2&gt;
&lt;p&gt;To mount the NFS volume at boot time, we have to add it to the &lt;em&gt;automounter master map&lt;/em&gt; (&lt;a href="https://lowhangingfruit.dev/2019-10-21/automount-nfs-on-macos/"&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;/etc/auto_master&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Add the following &lt;code&gt;auto_nfs&lt;/code&gt; line to the end of the file. This will try to auto-mount your nfs drive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;#&lt;/span&gt;
#
...
/-                 auto_nfs     -nobrowse,nosuid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;&lt;code&gt;/etc/auto_nfs&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Create the file &lt;code&gt;/etc/auto_nfs&lt;/code&gt; and use the template below as an example for your settings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;System&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Shared&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;nfs_volume&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fstype&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;noowners&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;nolockd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;resvport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;hard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;intr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;rw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;nfc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;rsize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;wsize&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//&amp;lt;ip-address&amp;gt;:/nfsexport/myshare&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/System/Volumes/Data/Users/Shared/nfs_volume&lt;/code&gt;, the location where you want to mount the volume. Change &lt;code&gt;nfs_volume&lt;/code&gt; to match the location where you want the volume to be visible in your local file system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-fstype=nfs&lt;/code&gt;, the filetype to mount&lt;/li&gt;
&lt;li&gt;&lt;code&gt;noowners,nolockd,resvport,hard,bg,intr,rw,tcp,nfc,rsize=8192,wsize=8192&lt;/code&gt;, the mounting parameters where the parameter &lt;code&gt;resvport&lt;/code&gt; might be required to connect to the NFS volume.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nfs://&amp;lt;ip-address&amp;gt;:/nfsexport/myshare&lt;/code&gt;, the location of the NFS volume&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Set the proper rights on the file &lt;code&gt;/etc/auto_nfs&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;644&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/auto_nfs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Mount the NFS Volume&lt;/h3&gt;
&lt;p&gt;Use the following command to initiate the automounter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;automount&lt;span class="w"&gt; &lt;/span&gt;-cv
automount:&lt;span class="w"&gt; &lt;/span&gt;/net&lt;span class="w"&gt; &lt;/span&gt;updated
automount:&lt;span class="w"&gt; &lt;/span&gt;/home&lt;span class="w"&gt; &lt;/span&gt;updated
automount:&lt;span class="w"&gt; &lt;/span&gt;/System/Volumes/Data/Users/tisgoud/nfs_volume&lt;span class="w"&gt; &lt;/span&gt;updated
automount:&lt;span class="w"&gt; &lt;/span&gt;no&lt;span class="w"&gt; &lt;/span&gt;unmounts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your NFS volume is now mounted. &lt;/p&gt;
&lt;h2&gt;Kodi Client Configuration&lt;/h2&gt;
&lt;p&gt;Use the following address for your media source definitions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt;&lt;span class="n"&gt;nfsexport&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;myshare&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Sysadmin"/><category term="nfs"/><category term="mac"/><category term="linux"/><category term="kodi"/></entry><entry><title>Fixing the Bootloader of a Ubuntu RAID System</title><link href="https://uwesander.de/fixing-the-bootloader-of-a-ubuntu-raid-system.html" rel="alternate"/><published>2019-08-19T17:18:00+02:00</published><updated>2019-08-19T17:18:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2019-08-19:/fixing-the-bootloader-of-a-ubuntu-raid-system.html</id><summary type="html">&lt;p&gt;If the remote Ubuntu machine does not boot showing the following error &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filesystem&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Entering&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rescue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rescue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;the GRUB bootloader is broken and can be fixed in one of the following ways.&lt;/p&gt;
&lt;h2&gt;Using the GRUB rescue concole&lt;/h2&gt;
&lt;p&gt;Follow the steps described in &lt;a href="https://it-muecke.de/grub-rescue"&gt;this blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Using a …&lt;/h2&gt;</summary><content type="html">&lt;p&gt;If the remote Ubuntu machine does not boot showing the following error &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;unknown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filesystem&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Entering&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rescue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rescue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;the GRUB bootloader is broken and can be fixed in one of the following ways.&lt;/p&gt;
&lt;h2&gt;Using the GRUB rescue concole&lt;/h2&gt;
&lt;p&gt;Follow the steps described in &lt;a href="https://it-muecke.de/grub-rescue"&gt;this blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Using a Live System&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Boot into a rescue system (can be done via your server provider's web interface).&lt;/li&gt;
&lt;li&gt;Connect via ssh using &lt;code&gt;ssh root@ip.address&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once connected, make sure the &lt;em&gt;mdadm&lt;/em&gt; tools are installed and assembled:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apt-get install mdadm
mdadm --assemble --scan&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mount the system partition (e.g. &lt;code&gt;/dev/md2&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo mount /dev/md2 /mnt&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check the mdadm.conf entries by looking at&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mdadm --examine --scan&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mount the boot partition (e.g &lt;code&gt;/dev/md1&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mount /dev/md1 /mnt/boot&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up the other required mounts:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mount -o bind /dev /mnt/dev 
mount -o bind /sys /mnt/sys 
mount -t proc /proc /mnt/proc 
cp /proc/mounts /mnt/etc/mtab&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Switch into the chroot environment:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chroot /mnt /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install GRUB 2 into the MBR of all relevant disks (e.g. &lt;code&gt;/dev/sda&lt;/code&gt; and &lt;code&gt;/dev/sdb2&lt;/code&gt; in case of a RAID system with one mirror disk):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grub-install /dev/sda
grub-install /dev/sdb&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If any error occur during the last operation, try:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grub-install --recheck /dev/sdX&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Recreate the file &lt;code&gt;/boot/grub/grub.cfg&lt;/code&gt; using:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;update-grub&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Leave the chroot environment using &lt;code&gt;CTRL+D&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Reboot.&lt;/li&gt;
&lt;/ol&gt;</content><category term="Sysadmin"/><category term="Ubuntu"/><category term="GRUB"/><category term="RAID"/><category term="bootloader"/><category term="web server"/></entry><entry><title>Mounting a Samba Share on the Raspberry Pi</title><link href="https://uwesander.de/mounting-a-samba-share-on-the-raspberry-pi.html" rel="alternate"/><published>2018-12-07T20:24:00+01:00</published><updated>2018-12-07T20:24:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2018-12-07:/mounting-a-samba-share-on-the-raspberry-pi.html</id><summary type="html">&lt;p&gt;Install the following packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get install  samba-common smbclient samba-common-bin smbclient  cifs-utils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create a local directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir /mnt/abc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mount the Samba share using your username:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mount -t cifs //server/share /mnt/abc -o user=uwe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the Samba share is provided by a FritzBox via its NAS feature and assuming …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Install the following packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apt-get install  samba-common smbclient samba-common-bin smbclient  cifs-utils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create a local directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mkdir /mnt/abc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Mount the Samba share using your username:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mount -t cifs //server/share /mnt/abc -o user=uwe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the Samba share is provided by a FritzBox via its NAS feature and assuming the share is named "MyNAS" on your FritzBox, then the above command would be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;mount -t cifs //&amp;lt;FritzBox IP&amp;gt;/MyNAS /mnt/abc -o user=uwe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Based on &lt;a href="https://raspberrypi.stackexchange.com/a/41067"&gt;this SO answer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: if you want the share to be mounted on every reboot, follow &lt;a href="https://forum-raspberrypi.de/forum/thread/40061-netzwerkfreigabe-mounten-mit-systemd-mount-unit/"&gt;this guide&lt;/a&gt; (in German).&lt;/p&gt;</content><category term="Sysadmin"/><category term="Raspberry Pi"/><category term="Samba"/><category term="Raspbian"/></entry><entry><title>Upgrade Your Apache to a Newer PHP Version</title><link href="https://uwesander.de/upgrade-your-apache-to-a-newer-php-version.html" rel="alternate"/><published>2018-06-08T16:30:00+02:00</published><updated>2018-06-08T16:30:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2018-06-08:/upgrade-your-apache-to-a-newer-php-version.html</id><summary type="html">&lt;p&gt;Upgrading PHP from 7.0 to 7.1 or 7.2 on Ubuntu 16.04 LTS is almost trivial as &lt;a href="https://markus-blog.de/index.php/2018/01/15/nextcloud-12-mit-php-7-1-auf-ubuntu-16-04-lts-beschleunigen/"&gt;this blog post (in German)&lt;/a&gt; explains:&lt;/p&gt;
&lt;p&gt;First, add a PPA (personal package archive):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install -y python-software-properties (falls nicht bereits installiert)
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get …&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Upgrading PHP from 7.0 to 7.1 or 7.2 on Ubuntu 16.04 LTS is almost trivial as &lt;a href="https://markus-blog.de/index.php/2018/01/15/nextcloud-12-mit-php-7-1-auf-ubuntu-16-04-lts-beschleunigen/"&gt;this blog post (in German)&lt;/a&gt; explains:&lt;/p&gt;
&lt;p&gt;First, add a PPA (personal package archive):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install -y python-software-properties (falls nicht bereits installiert)
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get update -y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Second, upgrade all packages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get -y update &amp;amp;&amp;amp; apt-get -y upgrade &amp;amp;&amp;amp; apt-get -y dist-upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Third, if you use Nextcloud, install all required PHP packages (for PHP 7.1 and 7.2 at the same time in this example):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo apt-get install -y php7.1 libapache2-mod-php7.1 php7.1-cli php7.1-common php7.1-mbstring php7.1-gd php7.1-intl php7.1-xml php7.1-mysql php7.1-mcrypt php7.1-zip php7.1-dev php7.1-curl libapache2-mod-php7.2 php7.2-cli php7.2-common php7.2-mbstring php7.2-gd php7.2-intl php7.2-xml php7.2-mysql php7.2-zip php7.2-dev php7.2-curl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, add the following code to your &lt;code&gt;/etc/php/7.1/apache2/php.ini&lt;/code&gt; or &lt;code&gt;/etc/php/7.1/apache2/php.ini&lt;/code&gt;, respectively:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;opcache.enable=1
opcache.enable_cli=1
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.memory_consumption=128
opcache.save_comments=1
opcache.revalidate_freq=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, enable the new PHP modules in your Apache:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo a2dismod php7.0
sudo a2enmod php7.1
service apache2 restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Sysadmin"/><category term="Nextcloud"/><category term="PHP"/><category term="Apache"/></entry><entry><title>Setting up a new Ubuntu Webserver</title><link href="https://uwesander.de/setting-up-a-new-ubuntu-webserver.html" rel="alternate"/><published>2017-11-11T20:15:00+01:00</published><updated>2017-11-11T20:15:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-11-11:/setting-up-a-new-ubuntu-webserver.html</id><summary type="html">&lt;p&gt;Here's a collection of links that provide helpful information when you have a brandnew Ubuntu machine with root access and you want to set it up as a webserver:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"&gt;https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/additional-recommended-steps-for-new-ubuntu-14-04-servers"&gt;https://www.digitalocean.com/community/tutorials/additional-recommended-steps-for-new-ubuntu-14-04-servers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linode.com/docs/security/securing-your-server"&gt;https://www.linode.com/docs/security/securing-your-server …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Here's a collection of links that provide helpful information when you have a brandnew Ubuntu machine with root access and you want to set it up as a webserver:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04"&gt;https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/additional-recommended-steps-for-new-ubuntu-14-04-servers"&gt;https://www.digitalocean.com/community/tutorials/additional-recommended-steps-for-new-ubuntu-14-04-servers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linode.com/docs/security/securing-your-server"&gt;https://www.linode.com/docs/security/securing-your-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu-16-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu-16-04
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-apache-virtual-hosts-on-ubuntu-16-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-set-up-apache-virtual-hosts-on-ubuntu-16-04&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If you want to use Wordpress: &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-lamp-on-ubuntu-16-04"&gt;https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-lamp-on-ubuntu-16-04
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If you're migrating from shared hosting: &lt;a href="https://linode.com/docs/platform/migrate-to-linode/migrate-from-shared-hosting-to-linode/"&gt;https://linode.com/docs/platform/migrate-to-linode/migrate-from-shared-hosting-to-linode/
&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to use PHP 7 with &lt;code&gt;mcrypt&lt;/code&gt;, you might need to add an extra &lt;code&gt;apt&lt;/code&gt; repository like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo add-apt-repository universe
sudo add-apt-repository -y ppa:ondrej/php
sudo apt-get install -y php7.0 libapache2-mod-php7.0 php7.0-mcrypt php7.0-mysql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The following links help you secure your server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://observatory.mozilla.org/analyze.html?host=mydomain.com"&gt;https://observatory.mozilla.org/analyze.html?host=mydomain.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.maketecheasier.com/securing-apache-ubuntu-2/"&gt;https://www.maketecheasier.com/securing-apache-ubuntu-2/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Sysadmin"/><category term="Ubuntu"/><category term="webserver"/></entry><entry><title>Setting up a systemd Service under Ubuntu</title><link href="https://uwesander.de/setting-up-a-systemd-service-under-ubuntu.html" rel="alternate"/><published>2017-11-11T19:15:00+01:00</published><updated>2017-11-11T19:15:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-11-11:/setting-up-a-systemd-service-under-ubuntu.html</id><summary type="html">&lt;p&gt;Starting with 15.04, Ubuntu uses &lt;a href="https://freedesktop.org/wiki/Software/systemd/"&gt;systemd&lt;/a&gt; instead of &lt;a href="http://upstart.ubuntu.com/"&gt;Upstart&lt;/a&gt; as the default system and service manager. &lt;a href="https://wiki.ubuntu.com/SystemdForUpstartUsers"&gt;This page&lt;/a&gt; offers a good comparison between the two systems.&lt;/p&gt;
&lt;p&gt;If you want to add a new systemd service (e.g. for running a Java application everytime your machine boots), create the following …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Starting with 15.04, Ubuntu uses &lt;a href="https://freedesktop.org/wiki/Software/systemd/"&gt;systemd&lt;/a&gt; instead of &lt;a href="http://upstart.ubuntu.com/"&gt;Upstart&lt;/a&gt; as the default system and service manager. &lt;a href="https://wiki.ubuntu.com/SystemdForUpstartUsers"&gt;This page&lt;/a&gt; offers a good comparison between the two systems.&lt;/p&gt;
&lt;p&gt;If you want to add a new systemd service (e.g. for running a Java application everytime your machine boots), create the following file under &lt;code&gt;/etc/systemd/system/myservice.service&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;My Service Description&lt;/span&gt;
&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;syslog.target&lt;/span&gt;

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="na"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/path/to/workdir&lt;/span&gt;
&lt;span class="na"&gt;SyslogIdentifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;MyService&lt;/span&gt;
&lt;span class="na"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/path/to/workdir/myservice.environment&lt;/span&gt;
&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/bash -c &amp;quot;java -jar /path/to/jar/MyService.jar&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;uwe&lt;/span&gt;
&lt;span class="c1"&gt;# see https://stegard.net/2016/08/gracefully-killing-a-java-process-managed-by-systemd/&lt;/span&gt;
&lt;span class="na"&gt;SuccessExitStatus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;143&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;

&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After creating or modifying a unit file, you should reload the systemd process itself to pick up your changes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;systemctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To enable a service to start automatically at boot, type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo systemctl enable unsereernte.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can start the service by typing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;sudo systemctl start unsereernte.service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Aside from &lt;code&gt;start&lt;/code&gt;, you can &lt;code&gt;stop&lt;/code&gt;, &lt;code&gt;restart&lt;/code&gt;, or &lt;code&gt;reload&lt;/code&gt; the service.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;systemd&lt;/code&gt; component called &lt;code&gt;journald&lt;/code&gt; collects and manages journal entries from all parts of the system. This is basically log information from applications and the kernel.&lt;/p&gt;
&lt;p&gt;To see all log entries, starting at the oldest entry, type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;journalctl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A good tutorial on systemd services can be found &lt;a href="https://www.digitalocean.com/community/tutorials/systemd-essentials-working-with-services-units-and-the-journal"&gt;here&lt;/a&gt;.&lt;/p&gt;</content><category term="Sysadmin"/><category term="Ubuntu"/><category term="systemd"/><category term="service"/></entry><entry><title>Redirecting Requests from Apache to Jetty</title><link href="https://uwesander.de/redirecting-from-apache-to-jetty.html" rel="alternate"/><published>2017-11-05T21:55:00+01:00</published><updated>2017-11-05T21:55:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-11-05:/redirecting-from-apache-to-jetty.html</id><summary type="html">&lt;p&gt;Assuming you are running an Apache server with a virtual host for abs.mydomain.com, put this into the virtual host's &lt;code&gt;.conf&lt;/code&gt; file to redirect all traffic to a Jetty web server running locally behind your Apache:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;Order&lt;span class="w"&gt; &lt;/span&gt;deny,allow
&lt;span class="w"&gt;    &lt;/span&gt;Allow&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;all
&lt;span class="nt"&gt;&amp;lt;/Proxy&amp;gt;&lt;/span&gt;

ProxyPass&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;http://localhost:1234/&lt;span class="w"&gt; &lt;/span&gt;retry …&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Assuming you are running an Apache server with a virtual host for abs.mydomain.com, put this into the virtual host's &lt;code&gt;.conf&lt;/code&gt; file to redirect all traffic to a Jetty web server running locally behind your Apache:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;Order&lt;span class="w"&gt; &lt;/span&gt;deny,allow
&lt;span class="w"&gt;    &lt;/span&gt;Allow&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;all
&lt;span class="nt"&gt;&amp;lt;/Proxy&amp;gt;&lt;/span&gt;

ProxyPass&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;http://localhost:1234/&lt;span class="w"&gt; &lt;/span&gt;retry=1
ProxyPassReverse&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;http://localhost:1234/
ProxyVia&lt;span class="w"&gt; &lt;/span&gt;On
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;1234&lt;/code&gt;is the port on which the Jetty server is listening. This port should be blocked from direct access via Apache. Using Ubuntu's default firewall &lt;em&gt;ufw&lt;/em&gt;, this can be done by running a simple &lt;code&gt;sudo ufw deny 1234&lt;/code&gt; on the command line.&lt;/p&gt;
&lt;p&gt;Added benefit: the internal Jetty server does not need to use SSL if the Apache server provides an SSL connection to the client. &lt;/p&gt;
&lt;p&gt;See &lt;a href="https://stackoverflow.com/a/8141490"&gt;this SO answer&lt;/a&gt; for an explanation of the &lt;code&gt;ProxyPassReverse&lt;/code&gt; directive.&lt;/p&gt;</content><category term="Sysadmin"/><category term="Apache"/><category term="Jetty"/><category term="Ubuntu"/><category term="web server"/><category term="SSL"/><category term="proxy"/></entry><entry><title>40 Falsehoods Programmers Believe about Names</title><link href="https://uwesander.de/40-falsehoods-programmers-believe-about-names.html" rel="alternate"/><published>2017-07-27T07:25:00+02:00</published><updated>2017-07-27T07:25:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-07-27:/40-falsehoods-programmers-believe-about-names.html</id><content type="html">&lt;p&gt;&lt;a href="http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/"&gt;Patrick McKenzie has never seen a computer system that handles names
properly&lt;/a&gt;.
Sad!&lt;/p&gt;</content><category term="General Software Development"/><category term="names"/><category term="Patrick McKenzie"/></entry><entry><title>Programming as if the Domain (and Performance) Mattered</title><link href="https://uwesander.de/programming-as-if-the-domain-and-performance-mattered.html" rel="alternate"/><published>2017-07-26T18:55:00+02:00</published><updated>2017-07-26T18:55:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-07-26:/programming-as-if-the-domain-and-performance-mattered.html</id><content type="html">&lt;p&gt;&lt;a href="http://goo.gl/L68zpH"&gt;A long read by my man Carlo
Pescio&lt;/a&gt;.&lt;/p&gt;</content><category term="General Software Development"/><category term="Carlo Pescio"/><category term="performance"/></entry><entry><title>It's not CI, it's just CI Theatre</title><link href="https://uwesander.de/its-not-ci-its-just-ci-theatre.html" rel="alternate"/><published>2017-05-26T18:32:00+02:00</published><updated>2017-05-26T18:32:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-05-26:/its-not-ci-its-just-ci-theatre.html</id><content type="html">&lt;p&gt;Are you practising CI? Find out by reading &lt;a href="https://www.gocd.io/2017/05/16/its-not-CI-its-CI-theatre/"&gt;this post by Suzie
Prince&lt;/a&gt;.&lt;/p&gt;</content><category term="General Software Development"/><category term="continuous integration"/><category term="Suzie Prince"/><category term="ThoughtWorks"/></entry><entry><title>The Initializer Pattern in Java</title><link href="https://uwesander.de/the-initializer-pattern-in-java.html" rel="alternate"/><published>2017-03-18T20:55:00+01:00</published><updated>2017-03-18T20:55:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-03-18:/the-initializer-pattern-in-java.html</id><content type="html">&lt;p&gt;&lt;a href="https://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-java/"&gt;A "type-safe builder pattern in
Java"&lt;/a&gt;.&lt;/p&gt;</content><category term="Design Patterns"/><category term="builder pattern"/><category term="Heinz Kabutz"/><category term="initializer"/><category term="Java"/></entry><entry><title>The Lean Inception</title><link href="https://uwesander.de/the-lean-inception.html" rel="alternate"/><published>2017-03-16T19:43:00+01:00</published><updated>2017-03-16T19:43:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-03-16:/the-lean-inception.html</id><content type="html">&lt;p&gt;I should probably read &lt;a href="https://martinfowler.com/articles/lean-inception/"&gt;this series of articles by Paulo
Caroli&lt;/a&gt; before I try
to convince my boss why implementing an MVP is a good idea.&lt;/p&gt;</content><category term="General Software Development"/><category term="lean inception"/><category term="Martin Fowler"/><category term="MVP"/><category term="Paulo Caroli"/></entry><entry><title>PiBakery</title><link href="https://uwesander.de/pibakery.html" rel="alternate"/><published>2017-03-09T21:33:00+01:00</published><updated>2017-03-09T21:33:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-03-09:/pibakery.html</id><summary type="html">&lt;p&gt;A great way to configure a &lt;a href="https://www.raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt;
from scratch is &lt;a href="http://www.pibakery.org/"&gt;PiBakery&lt;/a&gt;. After struggling a
while with setting up a VNC server and some other things on a fresh Pi
connected only via Ethernet (no display, keyboard, or mouse), finding
PiBakery was very helpful.&lt;/p&gt;
&lt;p&gt;Here's my
configuration:&lt;/p&gt;
&lt;center&gt;
![PiBakery configuration]({static …&lt;/center&gt;</summary><content type="html">&lt;p&gt;A great way to configure a &lt;a href="https://www.raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt;
from scratch is &lt;a href="http://www.pibakery.org/"&gt;PiBakery&lt;/a&gt;. After struggling a
while with setting up a VNC server and some other things on a fresh Pi
connected only via Ethernet (no display, keyboard, or mouse), finding
PiBakery was very helpful.&lt;/p&gt;
&lt;p&gt;Here's my
configuration:&lt;/p&gt;
&lt;center&gt;
![PiBakery configuration]({static}images/pibakery_conf.png)
&lt;/center&gt;

&lt;p&gt;The long string in the second Run Command step is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;DEBIAN_FRONTEND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;noninteractive&lt;/span&gt; &lt;span class="n"&gt;DEBIAN_PRIORITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;critical&lt;/span&gt; &lt;span class="n"&gt;apt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Dpkg::Options::=--force-confdef&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Dpkg::Options::=--force-confold&amp;quot;&lt;/span&gt; &lt;span class="n"&gt;upgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Sysadmin"/><category term="boinc"/><category term="headless"/><category term="PiBakery"/><category term="raspberry pi"/><category term="tor"/><category term="VNC"/></entry><entry><title>Using your SSL certificate for your Spark web application</title><link href="https://uwesander.de/using-your-ssl-certificate-for-your-spark-web-application.html" rel="alternate"/><published>2017-03-09T21:12:00+01:00</published><updated>2017-03-09T21:12:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2017-03-09:/using-your-ssl-certificate-for-your-spark-web-application.html</id><summary type="html">&lt;p&gt;I've fallen in love with &lt;a href="http://sparkjava.com/"&gt;Spark&lt;/a&gt; recently. Being
unexperienced with web development, I stumbled upon Spark when I was
looking for a small framework for setting up a simple web application
based on Java. My learning curve has been steep and my first website
(consisting of nothing but a simple …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've fallen in love with &lt;a href="http://sparkjava.com/"&gt;Spark&lt;/a&gt; recently. Being
unexperienced with web development, I stumbled upon Spark when I was
looking for a small framework for setting up a simple web application
based on Java. My learning curve has been steep and my first website
(consisting of nothing but a simple form) is running smoothly.&lt;/p&gt;
&lt;p&gt;One issue I had to deal with was using my domain's SSL certificate
(proudly sponsored by &lt;a href="http://letsencrypt.org/"&gt;Let's Encrypt&lt;/a&gt;) for the
integrated web server that comes with Spark. As you can read
&lt;a href="http://sparkjava.com/documentation.html#enable-ssl"&gt;here&lt;/a&gt;, a keystore
and a password need to be specified in order to achieve this.&lt;/p&gt;
&lt;p&gt;Alright, nothing easier than coming up with a password, but how do I set
up a Java keystore (.JKS) that uses my certificate? A quick
&lt;a href="https://www.startpage.com"&gt;Startpage&lt;/a&gt; search directed me to &lt;a href="https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm"&gt;Maximilian
Böhm's
tutorial&lt;/a&gt;
on JKS and Let's Encrypt certificates.&lt;br&gt;
I could skip step 1 as I had created and installed my certificates
already. For step 2, I needed to locate the directory in which my Plesk
installation stored the &lt;code&gt;fullchain.pem&lt;/code&gt; and &lt;code&gt;privkey.pem&lt;/code&gt; files. I found
them in:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;psa&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;letsencrypt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;live&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mydomain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I copied them to a temp directory to make things easier, and then ran
the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;openssl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkcs12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fullchain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;inkey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;privkey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkcs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mydomain_letsencrypt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After typing in and remembering the password, the resulting &lt;code&gt;pkcs.p12&lt;/code&gt;
file could be used to create the JKS file as described in step 3:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;keytool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;importkeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deststorepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DEST_STORE_PASS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;destkeypass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;KEY_PASS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;destkeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keystore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srckeystore&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pkcs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srcstoretype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PKCS12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;srcstorepass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SRC_STORE_PASS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mydomain_letsencrypt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;SRC_STORE_PASS&lt;/code&gt; is the password chosen in step 2. Make sure to
remember and distinguish those three passwords! Or use the same phrase
for all of them.&lt;/p&gt;
&lt;p&gt;The result was saved as &lt;code&gt;keystore.pks&lt;/code&gt;. I changed my Spark code
accordingly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyStoreLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;keystore.jks&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyStorePassword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;DEST_STORE_PASS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyStoreLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyStorePassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and put the file next my JAR. Done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 2017-09-02:&lt;/strong&gt; Here's a script that updates the keystore entry
(e.g in case the existing SSL certificate has been invalided and a new
one has been created).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="ch"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;CERT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/psa/var/modules/letsencrypt/etc/live/mydomain/
&lt;span class="nv"&gt;CERT_ALIAS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mydomain_letsencrypt
&lt;span class="nv"&gt;PKCS_FILENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pkcs.p12
&lt;span class="nv"&gt;PKCS_PW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_pkcs_pw&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;KEYSTORE_FILENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;keystore.jks
&lt;span class="nv"&gt;KEYSTORE_PW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my_keystore_pw&amp;#39;&lt;/span&gt;

&lt;span class="c1"&gt;# generate PKCS12 file&lt;/span&gt;
openssl&lt;span class="w"&gt; &lt;/span&gt;pkcs12&lt;span class="w"&gt; &lt;/span&gt;-export&lt;span class="w"&gt; &lt;/span&gt;-in&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CERT_PATH&lt;/span&gt;/fullchain.pem&lt;span class="w"&gt; &lt;/span&gt;-inkey&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CERT_PATH&lt;/span&gt;/privkey.pem&lt;span class="w"&gt; &lt;/span&gt;-out&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PKCS_FILENAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CERT_ALIAS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-passout&lt;span class="w"&gt; &lt;/span&gt;pass:&lt;span class="nv"&gt;$PKCS_PW&lt;/span&gt;

&lt;span class="c1"&gt;# delete existing entry in Java keystore&lt;/span&gt;
keytool&lt;span class="w"&gt; &lt;/span&gt;-delete&lt;span class="w"&gt; &lt;/span&gt;-keystore&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$KEYSTORE_FILENAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-alias&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CERT_ALIAS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-storepass&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$KEYSTORE_PW&lt;/span&gt;

&lt;span class="c1"&gt;# add new Java keystore entry from PKCS12 file&lt;/span&gt;
keytool&lt;span class="w"&gt; &lt;/span&gt;-importkeystore&lt;span class="w"&gt; &lt;/span&gt;-deststorepass&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$KEYSTORE_PW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-destkeypass&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$KEYSTORE_PW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-destkeystore&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$KEYSTORE_FILENAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-srckeystore&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PKCS_FILENAME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-srcstoretype&lt;span class="w"&gt; &lt;/span&gt;PKCS12&lt;span class="w"&gt; &lt;/span&gt;-srcstorepass&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PKCS_PW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-alias&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$CERT_ALIAS&lt;/span&gt;

rm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PKCS_FILENAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="Web Development, Sysadmin"/><category term="encryption"/><category term="Java"/><category term="Java keystore"/><category term="let's encrypt"/><category term="Spark"/><category term="SSL"/></entry><entry><title>Referencing a Bean in JasperReports</title><link href="https://uwesander.de/referencing-a-bean-in-jasperreports.html" rel="alternate"/><published>2016-09-10T18:53:00+02:00</published><updated>2016-09-10T18:53:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-09-10:/referencing-a-bean-in-jasperreports.html</id><summary type="html">&lt;p&gt;Thanks to &lt;a href="http://stackoverflow.com/a/6889704/6163371"&gt;this answer on
StackOverflow&lt;/a&gt;, I know how
easy it is to reference the bean itself instead of one of its properties
in a text field expression in JasperReports. The trick is to declare a
field whose description is set to the keyword &lt;code&gt;_THIS&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;myBean&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;de …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Thanks to &lt;a href="http://stackoverflow.com/a/6889704/6163371"&gt;this answer on
StackOverflow&lt;/a&gt;, I know how
easy it is to reference the bean itself instead of one of its properties
in a text field expression in JasperReports. The trick is to declare a
field whose description is set to the keyword &lt;code&gt;_THIS&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;myBean&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;de.uwesander.MyBean&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;fieldDescription&amp;gt;&lt;/span&gt;_THIS&lt;span class="nt"&gt;&amp;lt;/fieldDescription&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/field&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then it can be referenced in the text field expression as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;textFieldExpression&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;![CDATA[$F{myBean} instanceof ...]]&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/textFieldExpression&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="General Software Development"/><category term="Bean"/><category term="jasperreports"/><category term="stack overflow"/></entry><entry><title>Installing a Tor Relay from Source on a Raspberry Pi</title><link href="https://uwesander.de/installing-a-tor-relay-from-source-on-a-raspberry-pi.html" rel="alternate"/><published>2016-09-03T21:33:00+02:00</published><updated>2016-09-03T21:33:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-09-03:/installing-a-tor-relay-from-source-on-a-raspberry-pi.html</id><summary type="html">&lt;p&gt;The other day I learned on Twitter that I should update the Tor relay
software on my Raspberry Pi as the Tor project had introduced a new
Bridge Authority. So I lazily ran &lt;code&gt;sudo apt-get update&lt;/code&gt; 
followed by &lt;code&gt;sudo apt-get upgrade&lt;/code&gt; only to notice that no new version of the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The other day I learned on Twitter that I should update the Tor relay
software on my Raspberry Pi as the Tor project had introduced a new
Bridge Authority. So I lazily ran &lt;code&gt;sudo apt-get update&lt;/code&gt; 
followed by &lt;code&gt;sudo apt-get upgrade&lt;/code&gt; only to notice that no new version of the tor package was
available via the package manager. Bummer.&lt;/p&gt;
&lt;p&gt;Waiting for the package manager to provide a new version seemed futile
so I decided to build Tor from scratch. I found &lt;a href="https://tor.stackexchange.com/questions/75/how-can-i-install-tor-from-the-source-code-in-the-git-repository"&gt;a very helpful answer
on
StackOverflow&lt;/a&gt;
that listed all the steps required  for my setup.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Make sure to have all dependencies needed for compiling the sources:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo apt-get install git build-essential automake libevent-dev libssl-dev&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone Tor from &lt;code&gt;git.torproject.org&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://git.torproject.org/tor.git&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go into the tor directory: &lt;/p&gt;
&lt;p&gt;&lt;code&gt;cd tor&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Switch to the latest release branch:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git checkout release-x.y.z&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;./autogen.sh&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;./configure&lt;/code&gt; 
    or &lt;code&gt;./configure
    --disable-asciidoc&lt;/code&gt;  if you don't want to build the manpages.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;(Optiona): run &lt;code&gt;make install&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This last step installed the new tor binary in &lt;code&gt;/usr/local/bin/&lt;/code&gt; whereas
the existing tor binary was still located in &lt;code&gt;/usr/bin/&lt;/code&gt; from where it
was picked up by &lt;code&gt;/etc/init.d/tor&lt;/code&gt; (I want to run my tor relay as a
service whenever the Raspberry reboots). Luckily, this problem had been
solved by &lt;a href="https://lists.torproject.org/pipermail/tor-relays/2013-August/002500.html"&gt;someone else who shared his findings on a mailing
list&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If you're using `service tor {start, stop, reload, etc.}:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ /usr/bin/tor --version; /usr/local/bin/tor --version
$ grep DAEMON=/etc/init.d/tor&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The latter will likely point to &lt;code&gt;/usr/bin/tor&lt;/code&gt;, which might be
    outdated.&lt;br&gt;
    If that's the case, change that line in &lt;code&gt;/etc/init.d/tor&lt;/code&gt; to point
    to the new Tor executable &lt;code&gt;/usr/local/bin/tor&lt;/code&gt; - that's where it
    &lt;strong&gt;should&lt;/strong&gt; be; if you don't like that, change &lt;code&gt;BINDIR =
    /usr/local/bin&lt;/code&gt; in Tor's Makefile and &lt;code&gt;make install&lt;/code&gt; again.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Debian Tor packages seem to like to assume &lt;code&gt;torrc&lt;/code&gt; will be
    placed in &lt;code&gt;/etc/tor/torrc&lt;/code&gt; . If that's where your &lt;code&gt;torrc&lt;/code&gt; resides,
    make a symlink to it from &lt;code&gt;/usr/local/etc/tor&lt;/code&gt; , which is where the
    new Tor executable will look for it:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ln -s /etc/tor/torrc /usr/local/etc/tor/torrc&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Stop the running tor service:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo /etc/init.d/tor stop&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reload the tor daemon:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart the tor service:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo /etc/init.d/tor start&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content><category term="Uncategorized"/><category term="git"/><category term="raspberry pi"/><category term="stack overflow"/><category term="tor"/></entry><entry><title>Regex for German Zip Codes</title><link href="https://uwesander.de/regex-for-german-zip-codes.html" rel="alternate"/><published>2016-07-05T19:56:00+02:00</published><updated>2016-07-05T19:56:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-07-05:/regex-for-german-zip-codes.html</id><summary type="html">&lt;p&gt;I found the following regular expression
(&lt;a href="https://stackoverflow.com/questions/7926687/regular-expression-german-zip-codes"&gt;source&lt;/a&gt;)
working fine for me:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;?&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="mi"&gt;01000&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;99999&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;\\&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;\\&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;})$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I tried two alternatives that did not work because they were too
general:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})[&lt;/span&gt;&lt;span class="mi"&gt;0 …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;I found the following regular expression
(&lt;a href="https://stackoverflow.com/questions/7926687/regular-expression-german-zip-codes"&gt;source&lt;/a&gt;)
working fine for me:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;?&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="mi"&gt;01000&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;99999&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;\\&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;\\&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;})$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I tried two alternatives that did not work because they were too
general:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first regex matches the zip code "00000" which is invalid in the
German system. The second regex matches the zip code "99999" which is
invalid, too.&lt;/p&gt;</content><category term="General Software Development"/><category term="regex"/><category term="zip code"/></entry><entry><title>Renewal of Let's Encrypt Certificates Using Plesk</title><link href="https://uwesander.de/renewal-of-lets-encrypt-certificates-using-plesk.html" rel="alternate"/><published>2016-06-14T18:18:00+02:00</published><updated>2016-06-14T18:18:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-06-14:/renewal-of-lets-encrypt-certificates-using-plesk.html</id><summary type="html">&lt;p&gt;Note to self: If the Let's Encrypt extension for Plesk fails to renew a
certificate (when triggered manually), disable the automatic forwarding
to an SSL connection in the Apache settings. This forced secure
connection seems to disturb the renewal script.&lt;/p&gt;
&lt;p&gt;The error I got was something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;Note to self: If the Let's Encrypt extension for Plesk fails to renew a
certificate (when triggered manually), disable the automatic forwarding
to an SSL connection in the Apache settings. This forced secure
connection seems to disturb the renewal script.&lt;/p&gt;
&lt;p&gt;The error I got was something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Let&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Encrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SSL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;installation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letsencrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;execution&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;2016&lt;/span&gt;&lt;span class="mo"&gt;-01&lt;/span&gt;&lt;span class="mi"&gt;-19&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;885&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;WARNING&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;letsencrypt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;letsencrypt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;functionality&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Failed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;authorization&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;procedure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="mo"&gt;-01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;urn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;acme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;unauthorized&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lacks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sufficient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;authorization&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;
&lt;span class="n"&gt;Invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//removed.domain.com/.well-known/acme-challenge/&lt;/span&gt;
&lt;span class="n"&gt;REMOVED&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REMOVED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;
&lt;span class="n"&gt;IMPORTANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;NOTES&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;following&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;were&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reported&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="nl"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;urn&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;acme&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;unauthorized&lt;/span&gt;
&lt;span class="nl"&gt;Detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//removed.domain.com /.well-known/acme-challenge/&lt;/span&gt;
&lt;span class="n"&gt;REMOVED&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REMOVED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;See also &lt;a href="https://github.com/plesk/letsencrypt-plesk/issues/63"&gt;this bug report on
Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let's hope the extension is going to renew all certificates
automatically every month from now on, as it should.&lt;/p&gt;</content><category term="Sysadmin"/><category term="Apache"/><category term="certificate"/><category term="let's encrypt"/><category term="Plesk"/><category term="SSL"/><category term="web server"/></entry><entry><title>Create Custom Application Icons for MacOS X</title><link href="https://uwesander.de/create-custom-application-icons-for-macos-x.html" rel="alternate"/><published>2016-05-01T20:58:00+02:00</published><updated>2016-05-01T20:58:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-05-01:/create-custom-application-icons-for-macos-x.html</id><content type="html">&lt;p&gt;&lt;a href="http://blog.macsales.com/28492-create-your-own-custom-icons-in-10-7-5-or-later"&gt;This blog
post&lt;/a&gt;
by Chris Stevens shows you how.&lt;/p&gt;</content><category term="General Software Development"/><category term="icons"/><category term="Mac"/></entry><entry><title>Testing Delegate Methods</title><link href="https://uwesander.de/testing-delegate-methods.html" rel="alternate"/><published>2016-04-15T18:33:00+02:00</published><updated>2016-04-15T18:33:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-04-15:/testing-delegate-methods.html</id><summary type="html">&lt;p&gt;We had an interesting discussion the other day: should we write unit
tests for delegate methods, that is, methods that only delegate the call
to a delegate object? Here's an example of such a method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I said I did …&lt;/p&gt;</summary><content type="html">&lt;p&gt;We had an interesting discussion the other day: should we write unit
tests for delegate methods, that is, methods that only delegate the call
to a delegate object? Here's an example of such a method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I said I did not see much value in a unit test for this method as all
the test could do is verify whether the &lt;code&gt;do()&lt;/code&gt;
method of the mocked delegate object was called. Instead of testing
behaviour, we would test the wiring which is close to trivial in this
case and should be tested implicitly as part of an integration test
involving the delegate object and the main object.&lt;/p&gt;
&lt;p&gt;Some of my colleagues disagreed with me. Their argument was that a unit
test would detect changes in the delegate call and indicate the intended
usage of the delegate object. They were concerned an incorrect wiring
could be introduced without a unit test. And this unit test would be
cheap to write.&lt;/p&gt;
&lt;p&gt;I did not find any of these arguments very convincing - and I'm in good
company: the JUnit team's FAQ webpage has a section on Best Practices
which comments on testing delegate methods. You can read it here. Thank
you, &lt;a href="http://blog.thecodewhisperer.com/"&gt;J. B. Rainsberger&lt;/a&gt;!&lt;/p&gt;</content><category term="General Software Development, Testing"/><category term="delegate method"/><category term="J. B. Rainsberger"/><category term="JUnit"/><category term="unit testing"/></entry><entry><title>Headless Testing with TestFX</title><link href="https://uwesander.de/headless-testing-with-testfx.html" rel="alternate"/><published>2016-04-11T18:35:00+02:00</published><updated>2016-04-11T18:35:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-04-11:/headless-testing-with-testfx.html</id><summary type="html">&lt;p&gt;&lt;a href="https://jeromecambon.wordpress.com/2016/03/15/testing-javafx-in-headless-mode/"&gt;Jérome Cambon wrote a nice introduction to TestFX and how to enable the
headless mode using
Monocle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I tried to reproduce the steps he described for running my TestFX GUI
tests in headless mode from Eclipse so I can keep using my mouse while
they are running. The crux is …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://jeromecambon.wordpress.com/2016/03/15/testing-javafx-in-headless-mode/"&gt;Jérome Cambon wrote a nice introduction to TestFX and how to enable the
headless mode using
Monocle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I tried to reproduce the steps he described for running my TestFX GUI
tests in headless mode from Eclipse so I can keep using my mouse while
they are running. The crux is setting the VM arguments in Eclipse.
First, I tried to set&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;-Dtestfx.robot=glass -Dglass.platform=Monocle -Dmonocle.platform=Headless -Dprism.order=sw
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;in the VM arguments section of the JRE tab in my Maven run
configuration:&lt;/p&gt;
&lt;center&gt;
![Maven run configuration in
Eclipse]({static}/images/run_configuration.png)
&lt;/center&gt;

&lt;p&gt;This did not work. I could still see the TextFX robot move the mouse
cursor on my screen.&lt;/p&gt;
&lt;p&gt;I had to specify the VM arguments using Maven's 'argLine' parameter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;clean install -DargLine=&amp;quot;-Dtestfx.robot=glass -Dglass.platform=Monocle -Dmonocle.platform=Headless -Dprism.order=sw&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;center&gt;
![Maven argLine
parameter]({static}/images/arg_line.png)
&lt;/center&gt;

&lt;p&gt;If anyone knows why setting the VM arguments in the run configuration
did not work, please let me know.&lt;/p&gt;</content><category term="JavaFX, Testing"/><category term="Eclipse"/><category term="headless"/><category term="JavaFX"/><category term="Maven"/><category term="Monocle"/><category term="TestFX"/></entry><entry><title>A Potential Pitfall with the TextFormatter Class in JavaFX</title><link href="https://uwesander.de/a-potential-pitfall-with-the-textformatter-class-in-javafx.html" rel="alternate"/><published>2016-04-07T18:52:00+02:00</published><updated>2016-04-07T18:52:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-04-07:/a-potential-pitfall-with-the-textformatter-class-in-javafx.html</id><summary type="html">&lt;p&gt;A common use case for the
&lt;a href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TextFormatter.html"&gt;TextFormatter&lt;/a&gt;
class in JavaFX is a text field that should convert any lower-case
character input into an upper-case character. This can be achieved
easily by defining a filter for the text formatter as described
&lt;a href="https://uwesander.de/the-textformatter-class-in-javafx-how-to-restrict-user-input-in-a-text-field.html"&gt;here&lt;/a&gt;. A straightforward implementation
could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UnaryOperator …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;A common use case for the
&lt;a href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TextFormatter.html"&gt;TextFormatter&lt;/a&gt;
class in JavaFX is a text field that should convert any lower-case
character input into an upper-case character. This can be achieved
easily by defining a filter for the text formatter as described
&lt;a href="https://uwesander.de/the-textformatter-class-in-javafx-how-to-restrict-user-input-in-a-text-field.html"&gt;here&lt;/a&gt;. A straightforward implementation
could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UnaryOperator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getFilter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUppercase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This implementation comes with one pitfall, though. When the (German)
user types the character "ß" into the text field, it will be converted
into "SS" because that's how Germans roll. &lt;strong&gt;So the resulting string is
one character longer than the input string&lt;/strong&gt;. Why is this a problem?
Well, the cursor position will be between the two "S" characters after
the conversion:&lt;/p&gt;
&lt;center&gt;
![cursor in text field]({static}/images/cursor_ss.png)
&lt;/center&gt;

&lt;p&gt;When the user types in the next character, it will be inserted &lt;em&gt;between&lt;/em&gt;
the two "S" characters, not &lt;em&gt;behind&lt;/em&gt; them. Bummer.&lt;/p&gt;
&lt;p&gt;Luckily, the masterminds behind JavaFX give us the tools to correct this
unfortunate behaviour since the change object that the filter works on
allows for setting the anchor and caret position in the text field. We
need to correct them by the difference between the string lengths before
and after the conversion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UnaryOperator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getFilter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originalText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upperCaseText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originalText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upperCaseText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;correctAnchorAndCaretPositions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originalText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;correctAnchorAndCaretPositions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originalText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;originalText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAnchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAnchor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCaretPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCaretPosition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This puts the cursor at the end of the string no matter how many "ß"
characters the user types in or pastes into the text field.&lt;/p&gt;</content><category term="JavaFX"/><category term="anchor"/><category term="caret"/><category term="cursor"/><category term="eszett"/><category term="Java"/><category term="JavaFX"/><category term="string conversion"/><category term="text field"/><category term="text formatter"/></entry><entry><title>The TextFormatter Class in JavaFX: How to Restrict User Input in a Text Field</title><link href="https://uwesander.de/the-textformatter-class-in-javafx-how-to-restrict-user-input-in-a-text-field.html" rel="alternate"/><published>2016-04-05T20:01:00+02:00</published><updated>2016-04-05T20:01:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-04-05:/the-textformatter-class-in-javafx-how-to-restrict-user-input-in-a-text-field.html</id><summary type="html">&lt;p&gt;There are a lot of code examples for restricting or modifying user input
into a JavaFX text field. Most examples I have seen suggest adding a
change listener to the text field's text property. Here's how you would
allow only lower-case characters in your text field using the change
listener …&lt;/p&gt;</summary><content type="html">&lt;p&gt;There are a lot of code examples for restricting or modifying user input
into a JavaFX text field. Most examples I have seen suggest adding a
change listener to the text field's text property. Here's how you would
allow only lower-case characters in your text field using the change
listener approach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;de&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uwesander&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;javafx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textformatter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.application.Application&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.Scene&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.control.TextField&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.layout.StackPane&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.stage.Stage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;TextFieldListenerExample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setMaxWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;obs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oldText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;newText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[a-z]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;StackPane&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pane&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StackPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pane&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;TextField Listener Example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This approach comes with one drawback: you'll have two events being
triggered by two changes of the text property. The first change is
caused by the direct user input, the second change is caused by the
manipulation of the user input by the change listener. The user won't
notice these two events, but somewhere in your code you have another
listener for the text property, that listener will receive two events,
one with the "invalid" change and a second one with the "valid" change.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://fxexperience.com/2012/02/restricting-input-on-a-textfield/"&gt;Another approach was suggested by Richard Bair a long time
ago&lt;/a&gt;.
His suggestion resulted in the &lt;code&gt;TextFormatter&lt;/code&gt; 
class being &lt;a href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TextFormatter.html"&gt;added to JavaFX with version
8u40&lt;/a&gt;.
It's a clean way to format, filter, or restrict user input. Here's how
it works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;de.uwesander.javafx.textformatter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;java.util.function.UnaryOperator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.application.Application&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.Scene&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.control.TextField&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.control.TextFormatter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.control.TextFormatter.Change&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.scene.layout.StackPane&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;javafx.stage.Stage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TextFormatterExample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMaxWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TextFormatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;textFormatter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getTextFormatter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTextFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textFormatter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;StackPane&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pane&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;StackPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textField&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pane&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;TextFormatter Example&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TextFormatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getTextFormatter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;UnaryOperator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getFilter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;TextFormatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;textFormatter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TextFormatter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;textFormatter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UnaryOperator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getFilter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isContentChange&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;[a-z]*&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What's still missing is support for the backspace key to delete a
character in the text field, but that's just my silly example
implementation.&lt;/p&gt;
&lt;p&gt;[&lt;strong&gt;Update 2016-12-28:&lt;/strong&gt; The implementation was
improved to support the deletion and selection of characters.]&lt;/p&gt;
&lt;p&gt;This approach intercepts the user input before it's written into the
text property and thus fires only one event. Before that happens, you
can examine and modify the &lt;code&gt;Change&lt;/code&gt; object in the &lt;code&gt;UnaryOperator&lt;/code&gt; defined in &lt;code&gt;getFilter()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;In addition to filtering, a &lt;code&gt;TextFormatter&lt;/code&gt; object can convert a value to a string representation and vice versa.
From the Javadoc:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A value converter and value  can be used to provide a special format
that represents a value of type V. If the control is editable and the
text is changed by the user, the value is then updated to correspond
to the text.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sounds pretty handy.&lt;/p&gt;</content><category term="JavaFX"/><category term="change listener"/><category term="event"/><category term="JavaFX"/><category term="Richard Bair"/><category term="text field"/><category term="text formatter"/></entry><entry><title>Owncloud with Apache 2.4, PHP FPM, and Plesk</title><link href="https://uwesander.de/owncloud-with-apache-2-4-php-fpm-and-plesk.html" rel="alternate"/><published>2016-04-04T19:46:00+02:00</published><updated>2016-04-04T19:46:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-04-04:/owncloud-with-apache-2-4-php-fpm-and-plesk.html</id><summary type="html">&lt;p&gt;After about two weeks of fiddling around, I finally managed to get my
own instance of owncloud up and running with Apache behind Plesk. Here's
how I did it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up a sub-domain in Plesk, e.g. &lt;code&gt;owcloud.mydomain.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set up a database for owncloud using Plesk.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure …&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;After about two weeks of fiddling around, I finally managed to get my
own instance of owncloud up and running with Apache behind Plesk. Here's
how I did it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set up a sub-domain in Plesk, e.g. &lt;code&gt;owcloud.mydomain.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set up a database for owncloud using Plesk.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure to use HTTP strict transport security by adding the
    following Apache HTTPS directive:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;IfModule mod_headers.c&amp;gt;
    Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"
&amp;lt;/IfModule&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a directory for your owncloud data, e.g. &lt;code&gt;/var/oc_data&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://doc.owncloud.org/server/8.0/admin_manual/installation/installation_wizard.html#strong-perms-label"&gt;Give sufficient
    permissions&lt;/a&gt;
    to your HTTP user on the data directory:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chown -R $user1:psacln /var/oc_data/
chmod 750 /var/oc_data&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;In my case, &lt;code&gt;user1&lt;/code&gt; is the the name of the Plesk user with which I
had created the sub-domain. &lt;code&gt;psacln&lt;/code&gt; is the group assigned to this
user by Plesk.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update PHP to version 5.6.x. There is &lt;a href="https://bbs.archlinux.org/viewtopic.php?id=178551"&gt;this
    bug&lt;/a&gt; in version
    5.5.x which makes owncloud unusable if you don't want to use
    &lt;em&gt;mod_php&lt;/em&gt; (which is discouraged).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure PHP in Plesk and Apache as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run PHP as an FPM application&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;open_basedir&lt;/code&gt; to
    &lt;code&gt;{WEBSPACEROOT}{/}{:}{TMP}{/}{:}/dev/urandom{:}/var/oc_data{/}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;memory_limit&lt;/code&gt; to 256M (or something like that)&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;max_execution_time&lt;/code&gt; to 120 (or something like that)&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;max_input_time&lt;/code&gt; to 120 (or something like that)&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;post_max_size&lt;/code&gt; to 128M (or something like that)&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;upload_max_filesize&lt;/code&gt; to 64M (or something like that)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check &lt;a href="https://doc.owncloud.org/server/9.0/admin_manual/installation/source_installation.html#php-fpm-configuration-notes"&gt;this owncloud documentation
    page&lt;/a&gt;
    for potential additional PHP settings when running in FPM mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Download the &lt;code&gt;setup-owncloud.php&lt;/code&gt; web installer as described
    &lt;a href="https://owncloud.org/install/#instructions-server"&gt;here&lt;/a&gt; and run it
    from your browser.&lt;/li&gt;
&lt;li&gt;Have fun.&lt;/li&gt;
&lt;/ol&gt;</content><category term="Sysadmin"/><category term="Apache, HTTP"/><category term="SSL, Owncloud, PHP, Plesk"/></entry><entry><title>How to Make Unit Tests more Likeable</title><link href="https://uwesander.de/how-to-make-unit-tests-more-likeable.html" rel="alternate"/><published>2016-04-04T19:13:00+02:00</published><updated>2016-04-04T19:13:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-04-04:/how-to-make-unit-tests-more-likeable.html</id><summary type="html">&lt;blockquote&gt;
&lt;p&gt;What are these unit tests good for? Are we testing Mockito?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is what a colleague asked another colleague a couple of days ago.
He was referring to some rather complicated and long unit tests that
used a lot of mocks and &lt;em&gt;verify&lt;/em&gt; statements. I knew what he was talking …&lt;/p&gt;</summary><content type="html">&lt;blockquote&gt;
&lt;p&gt;What are these unit tests good for? Are we testing Mockito?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is what a colleague asked another colleague a couple of days ago.
He was referring to some rather complicated and long unit tests that
used a lot of mocks and &lt;em&gt;verify&lt;/em&gt; statements. I knew what he was talking
about since I had seen quite a few tests like that myself.&lt;/p&gt;
&lt;p&gt;Whenever a &lt;em&gt;JUnit&lt;/em&gt; test method has more than ten lines or so, I get
suspicious (and sometimes start crying). Add a few &lt;em&gt;ArgumentCaptors&lt;/em&gt; and
I'm done with that test. If I'm supposed to fix a test like that, I tend
to give up quickly and rather delete the test method than try to
understand what is being tested and why it fails.&lt;/p&gt;
&lt;p&gt;A good test, however, makes me smile. It's short, concise and readable
because it follows the &lt;em&gt;arrange, act, assert&lt;/em&gt; principle. It uses
Mockito's fluent API if it uses Mockito at all. That's &lt;code&gt;anyString()&lt;/code&gt; 
instead of &lt;code&gt;Mockito.anyString()&lt;/code&gt; and &lt;code&gt;times(2)&lt;/code&gt; instead of &lt;code&gt;VerificationModeFactory.times(2)&lt;/code&gt;
(which is Mockito's internal API btw). And mocks, stubs, spies, and
dummy objects are set up in the &lt;code&gt;@Before&lt;/code&gt; method instead of every test method. This saves a lot of boilerplate code and makes me want to fix the test if it breaks.&lt;/p&gt;</content><category term="General Software Development, Testing"/><category term="argument captor"/><category term="dummy"/><category term="JUnit"/><category term="mock"/><category term="Mockito"/><category term="spy"/><category term="stub"/><category term="unit testing"/></entry><entry><title>Adding JavaFX Properties to a DTO</title><link href="https://uwesander.de/adding-javafx-properties-to-a-dto.html" rel="alternate"/><published>2016-03-21T20:23:00+01:00</published><updated>2016-03-21T20:23:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-03-21:/adding-javafx-properties-to-a-dto.html</id><summary type="html">&lt;p&gt;Now that I have understood &lt;a href="https://uwesander.de/whats-up-with-dtos.html"&gt;what a DTO is good
for&lt;/a&gt;, I'm thinking about ways to improve it.
As they are right now, our DTOs are POJOs extended by &lt;a href="https://en.wikipedia.org/wiki/Bean_Validation"&gt;JSR
303&lt;/a&gt; annotations for
defining constraints on their fields/methods. The annotations are
duplicates of the annotations defined on the corresponding …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Now that I have understood &lt;a href="https://uwesander.de/whats-up-with-dtos.html"&gt;what a DTO is good
for&lt;/a&gt;, I'm thinking about ways to improve it.
As they are right now, our DTOs are POJOs extended by &lt;a href="https://en.wikipedia.org/wiki/Bean_Validation"&gt;JSR
303&lt;/a&gt; annotations for
defining constraints on their fields/methods. The annotations are
duplicates of the annotations defined on the corresponding entities.&lt;/p&gt;
&lt;p&gt;Since I have worked with the UI code most of my time so far, I have felt
the drawback of not having JavaFX properties in our DTOs: they cannot be
observed easily by the GUI controls. There is &lt;a href="https://uwesander.de/can-there-be-too-much-of-a-good-thing.html"&gt;a wrapping
mechanism&lt;/a&gt; to add this observability, but it is rather complicated to use and requires a lot of extra code.&lt;/p&gt;
&lt;p&gt;So why not make the DTOs full JavaFX beans? After all, &lt;a href="http://www.adam-bien.com/roller/abien/entry/dto_the_exceptions_from_the"&gt;we would not be
the first project to do
this&lt;/a&gt;.
Two common objections were raised when I brought this up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DTOs are created on the server which should not have dependencies to
    a GUI/client technology.&lt;/li&gt;
&lt;li&gt;We would need to mix JavaFX beans with JSR 303 annotations.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's address the first point. Yes, having a GUI dependency in your
server code is not a good idea. But are JavaFX properties a GUI
technology? Or even a client technology? It's a bit unfortunate that
they were introduced as part of JavaFX in a &lt;code&gt;javafx.*&lt;/code&gt; package such that
everyone identifies them as a GUI technology at first sight. It's an
image problem. I would argue they are a self-contained technology which
is independent of any GUI parts of JavaFX and could therefore be used
for any data model, no matter if it's on the client, on the server, or
shared between both. Even if one does not use JavaFX for the frontend,
depending on JavaFX properties would do almost no harm in practice since
JavaFX is part of the JDK and your server code certainly depends on the
JDK. So the &lt;em&gt;no client dependencies on the server argument&lt;/em&gt; is merely a
formal one in my opinion.&lt;/p&gt;
&lt;p&gt;What about the second point, mixing JavaFX properties with bean
validation annotations? I have never tried it myself, but I have the
feeling it cannot be that big of a problem. If it works in the &lt;a href="https://github.com/dooApp/FXForm2/blob/master/demo/src/main/java/com/dooapp/fxform/MyBean.java"&gt;FXForm2
demo&lt;/a&gt;,
why should there be a fundamental incompatibility between these two
technologies? But I admit that a good prototype is needed to make a
final judgment.&lt;/p&gt;
&lt;p&gt;We will have a meeting on this tomorrow. Let's see what the others
think.&lt;/p&gt;</content><category term="JavaFX"/><category term="annotations"/><category term="DTO"/><category term="FXForm2"/><category term="JavaFX"/><category term="JSR 303"/><category term="UI"/><category term="Wrapper"/></entry><entry><title>What's up with DTOs?</title><link href="https://uwesander.de/whats-up-with-dtos.html" rel="alternate"/><published>2016-03-19T21:30:00+01:00</published><updated>2016-03-19T21:30:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-03-19:/whats-up-with-dtos.html</id><summary type="html">&lt;p&gt;Having worked a lot with &lt;a href="https://eclipse.org/modeling/emf/"&gt;EMF&lt;/a&gt; and
Eclipse RCP applications based on EMF as the framework of choice for
transferring domain models into code, I never got in touch with
real-life examples of &lt;a href="http://martinfowler.com/eaaCatalog/dataTransferObject.html"&gt;Data Transfer
Objects&lt;/a&gt;
(DTOs). I was familiar with the pattern in theory, but I never saw a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Having worked a lot with &lt;a href="https://eclipse.org/modeling/emf/"&gt;EMF&lt;/a&gt; and
Eclipse RCP applications based on EMF as the framework of choice for
transferring domain models into code, I never got in touch with
real-life examples of &lt;a href="http://martinfowler.com/eaaCatalog/dataTransferObject.html"&gt;Data Transfer
Objects&lt;/a&gt;
(DTOs). I was familiar with the pattern in theory, but I never saw a DTO
in practice since EMF works fine without them, even in a client-server
architecture.&lt;/p&gt;
&lt;p&gt;This changed recently. My new project is a client-server system which
does not use EMF for implementing its domain objects. DTOs are used
heavily to transfer data from the server to the client and vice versa.
During my first journeys through the unknown code base, I stumbled upon
a lot of DTOs and wondered what they were needed for at all since they
seemed to introduce a lot of redundancy and required a mapping to and
from the entities managed by the server. Wouldn't it be more efficient
and practical to simply use the entities as the shared domain objects on
the server and the client? What is right in the EMF world cannot be
wrong in the JavaEE world, I thought.&lt;/p&gt;
&lt;p&gt;So I did some research and found &lt;a href="http://www.adam-bien.com/roller/abien/entry/javaee_7_retired_the_dto"&gt;this blog post by Adam
Bien&lt;/a&gt;
who seemed to confirm my doubts. "JavaEE 7 retired the DTO" was all I
needed to hear in order to start a discussion with one of my new
colleagues about the whole purpose of DTOs in our project. He was nice
enough to take a long break from his current task and give me an
extended explanation of why and how DTOs are necessary in our project.
He answered all of my questions patiently, even if some of them were
perhaps a bit naive and showed my lack of experience with some aspects
of JavaEE. Needless to say it was a fruitful discussion.&lt;/p&gt;
&lt;p&gt;I did some more research the next day based on his explanation. When I
was finished, I had changed my mind. I am now convinced DTOs add value
to our project despite the code duplication and the extra efforts for
the domain mapping because they embody more than a simple transfer
mechanism . Here are the pros and cons of DTOs based on my current
understanding:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Duplicate code (duplicate data models, duplicate constraints,...)&lt;/li&gt;
&lt;li&gt;A mapping to and from the domain objects is required&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DTO exposes the domain model in a form that is tailored for the
    client's needs&lt;/li&gt;
&lt;li&gt;Entity should be managed by the server all the time, never
    deattached from it&lt;/li&gt;
&lt;li&gt;Can save bandwidth by carrying only a selected subset of the
    entity's attributes/dependencies&lt;/li&gt;
&lt;li&gt;No client dependency on JPA if entity uses JPA annotations (e.g.
    for constraints)&lt;/li&gt;
&lt;li&gt;More pros &lt;a href="http://www.adam-bien.com/roller/abien/entry/dto_the_exceptions_from_the"&gt;here on Adam Bien's
    blog&lt;/a&gt; (2014)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;**Resources I found helpful **&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://martinfowler.com/eaaCatalog/dataTransferObject.html"&gt;Martin Fowler's Definition of a
    DTO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.adam-bien.com/roller/abien/entry/how_evil_are_actually_data"&gt;Adam Bien: How evil are actually
    data (2009)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.adam-bien.com/roller/abien/entry/how_to_deal_with_j2ee"&gt;Adam Bien: Design Patterns in Java
    EE (2014)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.adam-bien.com/roller/abien/entry/javaee_7_retired_the_dto"&gt;Adam Bien: JavaEE 7 retired the
    DTO (2014)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://programmers.stackexchange.com/questions/171457/what-is-the-point-of-using-dto-data-transfer-objects"&gt;StackExchange: What is the point of using a
    DTO?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Design Patterns"/><category term="Adam Bien"/><category term="DTO"/><category term="Eclipse RCP"/><category term="EMF"/><category term="JavaEE"/><category term="Martin Fowler"/></entry><entry><title>Any Complaints?</title><link href="https://uwesander.de/any-complaints.html" rel="alternate"/><published>2016-03-16T19:52:00+01:00</published><updated>2016-03-16T19:52:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-03-16:/any-complaints.html</id><content type="html">&lt;p&gt;&lt;a href="https://twitter.com/phillip_webb/status/705909774001377280"&gt;https://twitter.com/phillip_webb/status/705909774001377280&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I usually go for "too much boilerplate".&lt;/p&gt;</content><category term="General Software Development"/><category term="complaints"/><category term="Twitter"/></entry><entry><title>Can There Be Too Much of a Good Thing?</title><link href="https://uwesander.de/can-there-be-too-much-of-a-good-thing.html" rel="alternate"/><published>2016-03-16T19:49:00+01:00</published><updated>2016-03-16T19:49:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2016-03-16:/can-there-be-too-much-of-a-good-thing.html</id><summary type="html">&lt;p&gt;I recently worked on JavaFX UI code that was new to me. In order to
extend the existing UI by a simple label, I needed access to a bean of
the underlying data model. Much to my surprise, I had to dig deep to
find it hidden under multiple layers …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently worked on JavaFX UI code that was new to me. In order to
extend the existing UI by a simple label, I needed access to a bean of
the underlying data model. Much to my surprise, I had to dig deep to
find it hidden under multiple layers of wrappers. This was the data
structure:&lt;/p&gt;
&lt;center&gt;
![A bean wrapped in a BeanWrapper wrapped in an ObjectProperty wrapped
in a WrapperPropertyProxy. Quite
impressive.]({static}/images/wrapper_galore.png)
&lt;/center&gt;

&lt;p&gt;Wow. Clearly, someone tried to satisfy their fetish for wrappers and
indirection. I'm going show this image to the next computer science
student I come across and ask them how many design (anti-)patterns they
can see.&lt;/p&gt;</content><category term="Design Patterns"/><category term="Java"/><category term="JavaFX"/><category term="UI"/></entry><entry><title>Zooming in the Fancy Chart Control</title><link href="https://uwesander.de/zooming-in-the-fancy-chart-control.html" rel="alternate"/><published>2014-04-24T16:03:00+02:00</published><updated>2014-04-24T16:03:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-04-24:/zooming-in-the-fancy-chart-control.html</id><summary type="html">&lt;p&gt;A new version of the fancy chart control is available at
&lt;a href="https://github.com/tesis-dynaware/fancy-chart"&gt;https://github.com/tesis-dynaware/fancy-chart&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This version supports zooming for the XY chart: by dragging the mouse,
you can select an area in the chart to scale the selected area up to
full size of the chart. This allows …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A new version of the fancy chart control is available at
&lt;a href="https://github.com/tesis-dynaware/fancy-chart"&gt;https://github.com/tesis-dynaware/fancy-chart&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This version supports zooming for the XY chart: by dragging the mouse,
you can select an area in the chart to scale the selected area up to
full size of the chart. This allows you to have a close look at the
selected data points.&lt;/p&gt;</content><category term="JavaFX"/><category term="fancy chart"/><category term="Github"/><category term="JavaFX"/><category term="zoom"/></entry><entry><title>Headless UI Testing with TestFX and JavaFX 8</title><link href="https://uwesander.de/headless-ui-testing-with-testfx-and-javafx-8-2.html" rel="alternate"/><published>2014-04-23T14:41:00+02:00</published><updated>2014-04-23T14:41:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-04-23:/headless-ui-testing-with-testfx-and-javafx-8-2.html</id><summary type="html">&lt;p&gt;&lt;a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html"&gt;JavaFX&lt;/a&gt;
is a great UI toolkit. &lt;a href="https://github.com/TestFX/TestFX"&gt;TestFX&lt;/a&gt; is a
great library for testing the user interfaces written in JavaFX. Writing
graphical tests with TestFX is simple and fast, but one challenge
remains when you build your software using a headless build machine: how
can you perform your UI tests in …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://www.oracle.com/technetwork/java/javase/overview/javafx-overview-2158620.html"&gt;JavaFX&lt;/a&gt;
is a great UI toolkit. &lt;a href="https://github.com/TestFX/TestFX"&gt;TestFX&lt;/a&gt; is a
great library for testing the user interfaces written in JavaFX. Writing
graphical tests with TestFX is simple and fast, but one challenge
remains when you build your software using a headless build machine: how
can you perform your UI tests in headless mode during your build?&lt;/p&gt;
&lt;p&gt;Luckily, JavaFX 8 Update 20 will contain
&lt;a href="https://wiki.openjdk.java.net/display/OpenJFX/Monocle"&gt;Monocle&lt;/a&gt;, which
should allow us to run these headless tests.&lt;/p&gt;
&lt;p&gt;First analysis using the latest beta of JDK 8u20 indicates that these
tests are feasible, but require some changes in the TestFX library. We
used a simple standalone JavaFX application for which we wrote a basic
TestFX test class. We ran this test using the following command line
options:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;-Dglass.platform=Monocle
-Dmonocle.platform=Headless
-Dprism.order=sw
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, this was not enough to make the test work. So we took a
deeper look at the inner workings of TestFX and found that a working
solution required the following changes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;refactor a part of the TestFX code such that different
    implementations of the
    &lt;code&gt;org.loadui.testfx.framework.robot.ScreenRobot&lt;/code&gt; interface can
    be used. This allowed us to make test use an other robot
    implementation, in this case an adapter for &lt;code&gt;MonocleRobot&lt;/code&gt;. &lt;a href="https://github.com/TestFX/TestFX/blob/master/subprojects/testfx-core/src/main/java/org/loadui/testfx/framework/robot/FxRobot.java#l211"&gt;The
    current TestFX
    implementation&lt;/a&gt;
    relies on &lt;code&gt;java.awt.Robot&lt;/code&gt;, which uses the system event queue to
    move the system cursor. This, of course, does not integrate with
    Monocle in headless mode.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;implement a &lt;code&gt;ScreenRobot&lt;/code&gt; adapter for &lt;code&gt;MonocleRobot&lt;/code&gt;. &lt;code&gt;MonocleRobot&lt;/code&gt;
    is in internal package and thus not available via public API. In
    order to obtain an instance of this class, we had to call:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;java
Platform.runLater(() -&amp;gt; {
   robot = com.sun.glass.ui.Application.GetApplication().createRobot();
   ...
});&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This setup gave us a green test eventually. :-)&lt;/p&gt;</content><category term="JavaFX"/><category term="build"/><category term="headless"/><category term="JavaFX"/><category term="JDK"/><category term="Monocle"/><category term="TestFX"/></entry><entry><title>Enhanced Fancy Chart</title><link href="https://uwesander.de/enhanced-fancy-chart.html" rel="alternate"/><published>2014-03-21T12:28:00+01:00</published><updated>2014-03-21T12:28:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-03-21:/enhanced-fancy-chart.html</id><content type="html">&lt;p&gt;A new version of the fancy chart control is available at
&lt;a href="https://github.com/tesis-dynaware/fancy-chart"&gt;https://github.com/tesis-dynaware/fancy-chart&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Data can now be imported and exported to CSV, XLS, and HDF5 files. A lot
of code was refactored to make it easier to support other file formats
in the future.&lt;/p&gt;</content><category term="JavaFX"/><category term="CSV"/><category term="fancy chart"/><category term="Github"/><category term="HDF5"/><category term="JavaFX"/><category term="XLS"/></entry><entry><title>Source Code for the Fancy Chart Control</title><link href="https://uwesander.de/source-code-for-the-fancy-chart-control.html" rel="alternate"/><published>2014-03-14T15:38:00+01:00</published><updated>2014-03-14T15:38:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-03-14:/source-code-for-the-fancy-chart-control.html</id><content type="html">&lt;p&gt;Due to popular demand, I published the source code for the combined
LineChart and TableView control mentioned &lt;a href="https://uwesander.de/a-combined-linechart-and-tableview-widget-in-javafx.html"&gt;in this
post&lt;/a&gt;
to Github. You can find the sources here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/tesis-dynaware/fancy-chart"&gt;https://github.com/tesis-dynaware/fancy-chart&lt;/a&gt;&lt;/p&gt;</content><category term="JavaFX"/><category term="animation"/><category term="fancy"/><category term="Github"/><category term="JavaFX"/><category term="TableView"/></entry><entry><title>Visit Zombieland</title><link href="https://uwesander.de/visit-zombieland.html" rel="alternate"/><published>2014-03-13T16:43:00+01:00</published><updated>2014-03-13T16:43:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-03-13:/visit-zombieland.html</id><content type="html">&lt;p&gt;We finally released our FBX importer code for JavaFX 8 on Github:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/tesis-dynaware/fbx-importer"&gt;https://github.com/tesis-dynaware/fbx-importer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can use it to load (binary) FBX files and show the result as a
JavaFX 3D mesh. Have fun playing around and please don't hesitate to
give us feedback.&lt;/p&gt;</content><category term="3D, JavaFX"/><category term="3D"/><category term="ASCII"/><category term="binary"/><category term="FBX"/><category term="Github"/><category term="JavaFX"/><category term="JNI"/><category term="zombie"/></entry><entry><title>A Combined LineChart and TableView Widget in JavaFX</title><link href="https://uwesander.de/a-combined-linechart-and-tableview-widget-in-javafx.html" rel="alternate"/><published>2014-02-19T12:10:00+01:00</published><updated>2014-02-19T12:10:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-02-19:/a-combined-linechart-and-tableview-widget-in-javafx.html</id><summary type="html">&lt;center&gt;
[![Video of a combined LineChart and Table widget in JavaFX](http://img.youtube.com/vi/w0No8gBxDi8/0.jpg)](http://www.youtube.com/watch?v=w0No8gBxDi8)

(click to watch the video)
&lt;/center&gt;

&lt;p&gt;This video shows a widget for displaying three characteristics (velocity
vs. distance) in a LineChart node and a tabbed TableView …&lt;/p&gt;</summary><content type="html">&lt;center&gt;
[![Video of a combined LineChart and Table widget in JavaFX](http://img.youtube.com/vi/w0No8gBxDi8/0.jpg)](http://www.youtube.com/watch?v=w0No8gBxDi8)

(click to watch the video)
&lt;/center&gt;

&lt;p&gt;This video shows a widget for displaying three characteristics (velocity
vs. distance) in a LineChart node and a tabbed TableView node. The two
nodes provide different views on the same data.&lt;/p&gt;
&lt;p&gt;The widget comes with the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hovering over a data point in the chart will show a popup with the x
    and y values.&lt;/li&gt;
&lt;li&gt;selecting a data point in the chart will select the corresponding
    data item in the table view and vice versa.&lt;/li&gt;
&lt;li&gt;when a data item was edited in the table view, the chart will
    update immediately.&lt;/li&gt;
&lt;li&gt;three colour pickers allow for changing the line colours in
    the chart.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: #999999;"&gt;import and export data from sources
    like CSV, Excel, HDF5 (to be done).&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These features were implemented using JavaFX bindings, events,
animations, and CSS styling.&lt;/p&gt;</content><category term="JavaFX"/><category term="animation"/><category term="characteristic"/><category term="fancy chart"/><category term="JavaFX"/><category term="popup"/><category term="TableView"/></entry><entry><title>A Widget Picker Control in JavaFX</title><link href="https://uwesander.de/a-widget-picker-control-in-javafx.html" rel="alternate"/><published>2014-02-07T12:38:00+01:00</published><updated>2014-02-07T12:38:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-02-07:/a-widget-picker-control-in-javafx.html</id><summary type="html">&lt;p&gt;The following video shows a custom control in JavaFX that lets you
select items from a list and position them in a grid. The items
represent UI widgets, that is, JavaFX controls, with a defined size on
the grid. The grid represents a page on which these widgets will be …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The following video shows a custom control in JavaFX that lets you
select items from a list and position them in a grid. The items
represent UI widgets, that is, JavaFX controls, with a defined size on
the grid. The grid represents a page on which these widgets will be
shown.&lt;/p&gt;
&lt;center&gt;
[![Video of a widget picker in JavaFX](http://img.youtube.com/vi/CvVRlZOfE6c/0.jpg)](http://www.youtube.com/watch?v=CvVRlZOfE6c)

(click to watch the video)
&lt;/center&gt;</content><category term="JavaFX"/><category term="editor"/><category term="grid"/><category term="JavaFX"/><category term="user interface"/><category term="widget"/></entry><entry><title>A Graph Editor in JavaFX</title><link href="https://uwesander.de/a-graph-editor-in-javafx.html" rel="alternate"/><published>2014-02-06T16:29:00+01:00</published><updated>2014-02-06T16:29:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-02-06:/a-graph-editor-in-javafx.html</id><summary type="html">&lt;p&gt;Another thing we've been working on is going to see the light of day
soon: a graph editor for JavaFX. The current beta version provides the
following main features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create draggable, resizable nodes and connections between them&lt;/li&gt;
&lt;li&gt;add multiple input or output connectors to a node&lt;/li&gt;
&lt;li&gt;create joints inside a …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;Another thing we've been working on is going to see the light of day
soon: a graph editor for JavaFX. The current beta version provides the
following main features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create draggable, resizable nodes and connections between them&lt;/li&gt;
&lt;li&gt;add multiple input or output connectors to a node&lt;/li&gt;
&lt;li&gt;create joints inside a connection&lt;/li&gt;
&lt;li&gt;add custom skins for nodes, connections, connectors, and joints&lt;/li&gt;
&lt;li&gt;enhanced user experience through comprehensive styling during
    drag-and-drop actions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Internally, we use an EMF model to represent the UI objects and their
state. The model is in sync with the UI at all times.&lt;/p&gt;
&lt;p&gt;Here's a short video of our current development version:&lt;/p&gt;
&lt;center&gt;
[![Video of a graph editor in JavaFX](http://img.youtube.com/vi/LI\_aPjz1O00/0.jpg)](http://www.youtube.com/watch?v=LI\_aPjz1O00)

(click to watch the video)
&lt;/center&gt;</content><category term="JavaFX"/><category term="EMF"/><category term="graph editor"/><category term="JavaFX"/><category term="user interface"/></entry><entry><title>A Custom Breadcrumb Control in JavaFX</title><link href="https://uwesander.de/a-custom-breadcrumb-control-in-javafx.html" rel="alternate"/><published>2014-02-06T14:49:00+01:00</published><updated>2014-02-06T14:49:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2014-02-06:/a-custom-breadcrumb-control-in-javafx.html</id><summary type="html">&lt;p&gt;After investing some serious brain power in the dynamic sizing of the
breadcrumbs in a navigation bar (described in &lt;a href="https://uwesander.de/implementing-breadcrumbs-in-javafx.html"&gt;this
post&lt;/a&gt;),
I forgot to provide a closer look at the outcome.&lt;/p&gt;
&lt;p&gt;Here's a short video of this custom control written in JavaFX:&lt;/p&gt;
&lt;center&gt;
[![Video of a graph editor in JavaFX](http …&lt;/center&gt;</summary><content type="html">&lt;p&gt;After investing some serious brain power in the dynamic sizing of the
breadcrumbs in a navigation bar (described in &lt;a href="https://uwesander.de/implementing-breadcrumbs-in-javafx.html"&gt;this
post&lt;/a&gt;),
I forgot to provide a closer look at the outcome.&lt;/p&gt;
&lt;p&gt;Here's a short video of this custom control written in JavaFX:&lt;/p&gt;
&lt;center&gt;
[![Video of a graph editor in JavaFX](http://img.youtube.com/vi/59PPwihHiAw/0.jpg)](http://www.youtube.com/watch?v=59PPwihHiAw)

(click to watch the video)
&lt;/center&gt;</content><category term="JavaFX"/></entry><entry><title>Using FXForm2 with EMF Models</title><link href="https://uwesander.de/using-fxform2-with-emf-models.html" rel="alternate"/><published>2013-11-08T13:46:00+01:00</published><updated>2013-11-08T13:46:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2013-11-08:/using-fxform2-with-emf-models.html</id><summary type="html">&lt;p&gt;Based on &lt;a href="https://uwesander.de/gui-generation-with-javafx.html"&gt;these considerations from
July&lt;/a&gt;,
we gave &lt;a href="http://dooapp.github.io/FXForm2/"&gt;FXForm2&lt;/a&gt; a shot for
generating forms from EMF models. It worked quite smoothly after we
wrote the factories that defined how the custom data types used in our
models should be displayed.&lt;/p&gt;
&lt;p&gt;We found a few things that are worth noting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Using …&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;</summary><content type="html">&lt;p&gt;Based on &lt;a href="https://uwesander.de/gui-generation-with-javafx.html"&gt;these considerations from
July&lt;/a&gt;,
we gave &lt;a href="http://dooapp.github.io/FXForm2/"&gt;FXForm2&lt;/a&gt; a shot for
generating forms from EMF models. It worked quite smoothly after we
wrote the factories that defined how the custom data types used in our
models should be displayed.&lt;/p&gt;
&lt;p&gt;We found a few things that are worth noting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Using Java Bean Validation&lt;/strong&gt;&lt;br&gt;
   In order to validate our EMF models using the Java Bean Validation
    API (JSR-303) that comes with FXForm2, we wrote wrapper classes for
    those model elements that we wanted to add constraints to. We
    decided to not touch the generated code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using another validation implementation&lt;/strong&gt;FXForm2 can use any
    implementation of JSR-303. &lt;a href="https://github.com/dooApp/FXForm2/wiki/Bean-validation-%28JSR-303%29"&gt;Using the Hibernate implementation is
    suggested on the FXForm2
    website&lt;/a&gt;,
    but we found it easy to supply our own implementation based on EMF's
    validation features such as the &lt;a href="http://download.eclipse.org/modeling/emf/emf/javadoc/2.4.2/org/eclipse/emf/ecore/util/Diagnostician.html"&gt;Diagnostician
    class&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validation philosophies&lt;/strong&gt;&lt;br&gt;
   When you enter an invalid value into a field, FXForm2 won't update
    the corresponding model attribute. The value you see in the form
    differs from the value in the model. The model thus always has a
    valid state.EMF validation typically works in a different way: you
    start the validation operation only after all model values were set.
    This allows for invalid states of the model.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic beans&lt;/strong&gt;&lt;a href="https://github.com/dooApp/FXForm2/issues/30"&gt;&lt;br&gt;
   An open issue&lt;/a&gt; seems to
    be (as least for us) the handling of a String-to-String Map
    attribute inside an EMF bean class that stores user-supplied
    key-value pairs which should be displayed as regular attributes of
    the bean. &lt;a href="https://github.com/dooApp/FXForm2/issues/30#issuecomment-26268441"&gt;Antoine suggested a
    solution&lt;/a&gt; based
    on a custom control (with a factory and a skin), but it comes with a
    different visual appearance for the user-defined attributes. This is
    work in progress.&lt;/li&gt;
&lt;/ol&gt;</content><category term="EMF, JavaFX"/><category term="data model"/><category term="forms"/><category term="FXForm2"/><category term="GUI generation"/><category term="Java"/><category term="Java Bean"/><category term="JavaFX"/></entry><entry><title>Implementing Breadcrumbs in JavaFX</title><link href="https://uwesander.de/implementing-breadcrumbs-in-javafx.html" rel="alternate"/><published>2013-11-04T15:08:00+01:00</published><updated>2013-11-04T15:08:00+01:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2013-11-04:/implementing-breadcrumbs-in-javafx.html</id><summary type="html">&lt;p&gt;I've been struggling to implement a JavaFX breadcrumbs bar using a
ToolBar with a few Button items in it. The breadcrumbs should have the
shape of an arrow as in the example shown in &lt;a href="http://www.loop81.com/2011/11/javafx-2-breadcrumbs-and-styling.html"&gt;Loop81's blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The breadcrumbs bar in the &lt;a href="http://www.oracle.com/technetwork/java/javafx/samples/index.html"&gt;Ensemble
application&lt;/a&gt;
uses the same approach. What I …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I've been struggling to implement a JavaFX breadcrumbs bar using a
ToolBar with a few Button items in it. The breadcrumbs should have the
shape of an arrow as in the example shown in &lt;a href="http://www.loop81.com/2011/11/javafx-2-breadcrumbs-and-styling.html"&gt;Loop81's blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The breadcrumbs bar in the &lt;a href="http://www.oracle.com/technetwork/java/javafx/samples/index.html"&gt;Ensemble
application&lt;/a&gt;
uses the same approach. What I don't like about this implementation is
the use of rather sophisticated CSS rules in combination with static
images.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;breadcrumbs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-background-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-background-insets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-insets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;crumb.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-slice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;stretch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;breadcrumbs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;hover&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;crumb-hover.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;breadcrumbs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;alone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;stretch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-image-source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;crumb-selected.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;#6e737d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-border-insets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As another approach, I tried to define an arrow-shaped SVG path (using
the -fx-shape CSS rule) for the breadcrumb buttons, but I encountered
scaling issues. The width of the shape must depend on the width of the
text displayed in the button, which could not be achieved using CSS.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;breadcrumbs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;-fx-shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M 0 0 L 100 0 L 130 15 L 100 30 L 0 30 z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So I defined the shape in Java code and used it to set the button's
clip. The width of the shape was computed dynamically based on the
button's text plus two magic numbers for the correct scaling of the
shape. This worked nicely, but came with the burden of those two magic
numbers that worked well only for the particular font I used.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;int textLength = button.getText.length();
// scaling and padding are magic numbers
int mainWidth = TEXT_SCALING &lt;span class="gs"&gt;* textLength + TEXT_PADDING;&lt;/span&gt;

&lt;span class="gs"&gt;// build the following shape&lt;/span&gt;
&lt;span class="gs"&gt;// --------&lt;/span&gt;
&lt;span class="gs"&gt;//&lt;/span&gt;
&lt;span class="gs"&gt;// / /&lt;/span&gt;
&lt;span class="gs"&gt;// --------&lt;/span&gt;
&lt;span class="gs"&gt;Path path = new Path();&lt;/span&gt;

&lt;span class="gs"&gt;// start in upper left corner&lt;/span&gt;
&lt;span class="gs"&gt;MoveTo e1 = new MoveTo(0, 0);&lt;/span&gt;

&lt;span class="gs"&gt;// draw upper horizontal line&lt;/span&gt;
&lt;span class="gs"&gt;HLineTo e2 = new HLineTo(ARROW_WIDTH + mainWidth);&lt;/span&gt;

&lt;span class="gs"&gt;// draw upper part of right arrow&lt;/span&gt;
&lt;span class="gs"&gt;LineTo e3 = new LineTo(ARROW_WIDTH + mainWidth + ARROW_WIDTH, ARROW_HEIGHT / 2.0);&lt;/span&gt;

&lt;span class="gs"&gt;// draw lower part of right arrow&lt;/span&gt;
&lt;span class="gs"&gt;LineTo e4 = new LineTo(ARROW_WIDTH + mainWidth, ARROW_HEIGHT);&lt;/span&gt;

&lt;span class="gs"&gt;// draw lower horizontal line&lt;/span&gt;
&lt;span class="gs"&gt;HLineTo e5 = new HLineTo(0);&lt;/span&gt;

&lt;span class="gs"&gt;// draw lower part of left arrow&lt;/span&gt;
&lt;span class="gs"&gt;LineTo e6 = new LineTo(ARROW_WIDTH, ARROW_HEIGHT / 2.0);&lt;/span&gt;

&lt;span class="gs"&gt;// close path&lt;/span&gt;
&lt;span class="gs"&gt;ClosePath e7 = new ClosePath();&lt;/span&gt;
&lt;span class="gs"&gt;path.getElements().addAll(e1, e2, e3, e4, e5, e6, e7);&lt;/span&gt;

&lt;span class="gs"&gt;// this is a dummy color to fill the shape, it won&amp;#39;t be visible&lt;/span&gt;
&lt;span class="gs"&gt;path.setFill(Color.BLACK);&lt;/span&gt;

&lt;span class="gs"&gt;// set path as shape for button&lt;/span&gt;
&lt;span class="gs"&gt;button.setClip(path);&lt;/span&gt;

&lt;span class="gs"&gt;// make button width equal to width of shape&lt;/span&gt;
&lt;span class="gs"&gt;double buttonWidth = 2 *&lt;/span&gt; ARROW_WIDTH + mainWidth;
button.setMinWidth(buttonWidth);
button.setMaxWidth(buttonWidth);
button.setPrefWidth(buttonWidth);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In yet another attempt, I implemented a breadcrumbs bar in which every
breadcrumb is represented by three buttons: one for the left-hand part
of the arrow, one for the central part (showing the text), and one for
the right-hand part. This allowed me to work with fixed-width shapes on
the left and on the right plus a regular-shaped (rectangular) button in
the middle. The obvious drawback was the management of all the extra
buttons including the correct update of hover effects for the outer
buttons when the mouse was over the inner button, and vice versa.&lt;/p&gt;
&lt;p&gt;I don't think there a JavaFX library out there that provides the
functionality I need, so is there anyone with experience in implementing
a breadcrumbs bar? Any thoughts or recommendations? Are there solutions
that do not come with any of the drawbacks mentioned above?&lt;/p&gt;
&lt;p&gt;Any help will be much appreciated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (2013-11-06):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://twitter.com/andy_till"&gt;Andy Till&lt;/a&gt; suggested an implementation
that won my heart: bind the &lt;code&gt;x&lt;/code&gt; property of the horizontal lines in the
arrow shape to the button's width. Since the button's width gets updated
to fit the text, the shape will always have the correct width. No need
for magic numbers. Thanks, Andy!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// build the following shape&lt;/span&gt;
&lt;span class="c1"&gt;// --------&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// / /&lt;/span&gt;
&lt;span class="c1"&gt;// --------&lt;/span&gt;
&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// begin in the upper left corner&lt;/span&gt;
&lt;span class="n"&gt;MoveTo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MoveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// draw a horizontal line that defines the width of the shape&lt;/span&gt;
&lt;span class="n"&gt;HLineTo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HLineTo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// bind the width of the shape to the width of the button&lt;/span&gt;
&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xProperty&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;widthProperty&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARROW_WIDTH&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// draw upper part of right arrow&lt;/span&gt;
&lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// the x endpoint of this line depends on the x property of line e2&lt;/span&gt;
&lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xProperty&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xProperty&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARROW_WIDTH&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARROW_HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// draw lower part of right arrow&lt;/span&gt;
&lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// the x endpoint of this line depends on the x property of line e2&lt;/span&gt;
&lt;span class="n"&gt;e4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xProperty&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xProperty&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;e4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARROW_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// draw lower horizontal line&lt;/span&gt;
&lt;span class="n"&gt;HLineTo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HLineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// draw lower part of left arrow&lt;/span&gt;
&lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARROW_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ARROW_HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// close path&lt;/span&gt;
&lt;span class="n"&gt;ClosePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ClosePath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getElements&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// this is a dummy color to fill the shape, it won&amp;#39;t be visible&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BLACK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// set path as button shape&lt;/span&gt;
&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setClip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And this is what my breadcrumbs bar looks like now:&lt;/p&gt;
&lt;center&gt;
![breadcrumbs]({static}/images/breadcrumbs.png)
&lt;/center&gt;

&lt;/div&gt;</content><category term="JavaFX"/><category term="breadcrumbs"/><category term="CSS"/><category term="Ensemble"/><category term="JavaFX"/><category term="SVGPath"/><category term="toolbar"/></entry><entry><title>Zombieland</title><link href="https://uwesander.de/zombieland.html" rel="alternate"/><published>2013-08-23T13:29:00+02:00</published><updated>2013-08-23T13:29:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2013-08-23:/zombieland.html</id><summary type="html">&lt;p&gt;Based on &lt;a href="https://twitter.com/jasperpotts/status/330516243914305537"&gt;Jasper Potts' 3DViewer
app&lt;/a&gt; (with
support for importing OBJ, MAX, and MAYA files), we are developing an
importer for FBX files into JavaFX. Below is a picture of our
developer trying to understand the FBX format a screenshot of the
extended 3DViewer app after loading an FBX file …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Based on &lt;a href="https://twitter.com/jasperpotts/status/330516243914305537"&gt;Jasper Potts' 3DViewer
app&lt;/a&gt; (with
support for importing OBJ, MAX, and MAYA files), we are developing an
importer for FBX files into JavaFX. Below is a picture of our
developer trying to understand the FBX format a screenshot of the
extended 3DViewer app after loading an FBX file containing a zombie.&lt;/p&gt;
&lt;center&gt;
![A JavaFX viewer application showing a Zombie object imported from an FBX
file.]({static}/images/zombie.png)
&lt;/center&gt;

&lt;p&gt;For implementing this importer, we took&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/jasperpotts/status/330516243914305537"&gt;Jasper's 3DViewer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&amp;amp;id=10775847"&gt;The FBX SDK&lt;/a&gt;
    (comes with a zombie)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jni/"&gt;JNI&lt;/a&gt;
    (as a bridge between the FBX SDK and our Java code)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The importer currently loads all mesh, material, and texture information
from all nodes in the FBX graph. We support ASCII and binary file
formats.&lt;/p&gt;
&lt;p&gt;We do not yet load light, camera, and animation data (the former two
should be quite simple, while animation will probably take longer).&lt;/p&gt;
&lt;p&gt;Our work is still ongoing, and I hope we can have an improved version
soon. Stay tuned!&lt;/p&gt;</content><category term="3D, JavaFX"/><category term="3D"/><category term="ASCII"/><category term="binary"/><category term="FBX"/><category term="Jasper Potts"/><category term="JavaFX"/><category term="JNI"/><category term="zombie"/></entry><entry><title>GUI Generation with JavaFX</title><link href="https://uwesander.de/gui-generation-with-javafx.html" rel="alternate"/><published>2013-07-12T07:48:00+02:00</published><updated>2013-07-12T07:48:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2013-07-12:/gui-generation-with-javafx.html</id><summary type="html">&lt;p&gt;Recently, we have had a look at JavaFX libraries for generating UIs in a
dynamic way. The idea is that we have a data model (a JavaBean class, an
EMF model class, a model class with JavaFX properties,...) as input for
some GUI builder that takes the model and generates …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently, we have had a look at JavaFX libraries for generating UIs in a
dynamic way. The idea is that we have a data model (a JavaBean class, an
EMF model class, a model class with JavaFX properties,...) as input for
some GUI builder that takes the model and generates a matching user
interface with bindings between the model and the JavaFX controls. In a
perfect world, this library would allow us to use a variety of data
models and to customise the generated user interface.&lt;/p&gt;
&lt;p&gt;We have had three candidates (all open source). As we use EMF a lot, two
of them are based on EMF models.&lt;/p&gt;
&lt;h2&gt;1. FXForm2&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://dooapp.github.io/FXForm2/"&gt;http://dooapp.github.io/FXForm2/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This pops up early when you are looking for UI generation libraries for
JavaFX. As the name implies, it is meant for generating forms from model
beans.&lt;/p&gt;
&lt;center&gt;
![A sample form generated by FXForm2](https://camo.githubusercontent.com/178463387c4056f6bad98224fb117de90ffd462e/687474703a2f2f646f6f6170702e6769746875622e636f6d2f4658466f726d322f636f6d706f6e656e74732e706e67)

Source: [https://github.com/dooApp/FXForm2/wiki/Style-your-form-with-css](https://github.com/dooApp/FXForm2/wiki/Style-your-form-with-css\[/caption\])
&lt;/center&gt;

&lt;p&gt;The main features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic form generation and binding to bean properties&lt;/li&gt;
&lt;li&gt;CSS support&lt;/li&gt;
&lt;li&gt;Bean Validation handling (JSR 303)&lt;/li&gt;
&lt;li&gt;Fields reordering and filtering&lt;/li&gt;
&lt;li&gt;Tooltips&lt;/li&gt;
&lt;li&gt;Localization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It takes three lines to add a generated form to your JavaFX scene graph:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;MyBean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myBean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MyBean&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// get an instance of the bean to be edited&lt;/span&gt;
&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fxForm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FXForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myBean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// create the FXForm node for your bean&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getChildren&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fxForm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// add it to your scene graph&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;2. EMF Edit for JavaFX&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://tomsondev.bestsolution.at/2012/12/13/emf-edit-support-is-coming-to-javafx-via-efxclipse/"&gt;http://tomsondev.bestsolution.at/2012/12/13/emf-edit-support-is-coming-to-javafx-via-efxclipse&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This library has a broader scope than FXForm2 as it can generate an
entire RCP application with a sophisticated visual model editor rather
than just a couple of form fields. Or you can define your own controls
and easily bind your EMF model instances to these controls, including
support for drag &amp;amp; drop and undo/redo commands. An example of a user
interface based on a "Contacts" model can be seen in Tom's video from
the link above.&lt;/p&gt;
&lt;center&gt;
![A sample application using EMF Edit for JavaFX](http://videos.videopress.com/R4NEN2Ic/screenflow1_dvd.original.jpg)

Source:
[http://tomsondev.bestsolution.at/2012/12/13/emf-edit-support-is-coming-to-javafx-via-efxclipse/](http://tomsondev.bestsolution.at/2012/12/13/emf-edit-support-is-coming-to-javafx-via-efxclipse/)

&lt;/center&gt;

&lt;h2&gt;3. EMF Client Platform&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://eclipse.org/emfclient/index.html"&gt;http://eclipse.org/emfclient/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This takes the idea of EMF Edit one step further. As the ECP website
says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The goal is to provide a one-click application based on a given EMF
model. Besides the EMF model, no additional components have to be
developed or generated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;center&gt;
![A sample application as generated by the EMF Client
Platform](http://eclipse.org/emfclient/public/images/application.png)

Source:
[http://eclipse.org/emfclient/](http://eclipse.org/emfclient/)

&lt;/center&gt;

&lt;p&gt;So when you have your EMF model, you are only one click away from a
complete client application with a model explorer, an editor, a
persistence mechanism, and a couple of other nice things. The editor for
the model attributes is similar to the data form generated by FXForm2,
but it is embedded in an entire application whose pieces are
customisable and extensible.&lt;/p&gt;
&lt;p&gt;The bad news is that there is currently no JavaFX support for ECP.  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;[Update]&lt;/strong&gt;&lt;br&gt;
As Tom Schindl has pointed out in the comments section, there is a first
version of JavaFX support for ECP in one of the development branches of
the e(fx)clipse project. See
&lt;a href="http://git.eclipse.org/c/efxclipse/org.eclipse.efxclipse.git/log/?h=ecp"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;4. Summary&lt;/h2&gt;
&lt;p&gt;We have not found the perfect solution yet. FXForm2 has a promising
approach for easily generating forms, of which we are going to have a
lot in our final product. It requires some tweaks to make it work with
EMF models but that is a small obstacle. What worries me a bit is the
maturity of this library: the latest version is 0.2.2. So is it ready
for production use? Well, there's at least &lt;a href="http://infiltrea.com/index.php/screenshots"&gt;one real-world
application&lt;/a&gt; that uses it.&lt;/p&gt;
&lt;p&gt;EMF Edit is a very powerful framework but does not generate controls on
the granularity we require for most parts of our application. It will
certainly be of use for fancy features like drag &amp;amp; drop and the command
stack. Maybe we can combine its strengths with the power of FXForm2 for
generating really rich forms.&lt;/p&gt;
&lt;p&gt;The EMF Client platform has a very appealing one-click approach but it
lacks support for JavaFX. Once this limitation is gone, we may give it
another look. A major question will then be how far we can customise the
basic layout of the generated application which by default is very
different from what we have in mind for our user interface.&lt;/p&gt;</content><category term="3D, EMF"/><category term="data model"/><category term="drag &amp; drop"/><category term="Eclipse"/><category term="EMF Client Platform"/><category term="EMF Edit"/><category term="forms"/><category term="FXForm2"/><category term="GUI generation"/><category term="Java"/><category term="JavaBean"/><category term="JavaFX"/><category term="open source"/><category term="RCP"/><category term="real world"/><category term="scene graph"/></entry><entry><title>Navigation</title><link href="https://uwesander.de/navigation.html" rel="alternate"/><published>2013-05-23T16:43:00+02:00</published><updated>2013-05-23T16:43:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2013-05-23:/navigation.html</id><summary type="html">&lt;p&gt;Dear tree huggers,&lt;/p&gt;
&lt;p&gt;I'm one of you. I like trees - preferably on the left of my application.
They give me the impression that I exactly know where I am and where I
can go next. Just recently I learnt how much I appreciate a tree as a
navigation element when …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Dear tree huggers,&lt;/p&gt;
&lt;p&gt;I'm one of you. I like trees - preferably on the left of my application.
They give me the impression that I exactly know where I am and where I
can go next. Just recently I learnt how much I appreciate a tree as a
navigation element when I worked with &lt;a href="http://www.atlassian.com/software/confluence/overview/team-collaboration-software"&gt;Confluence
5&lt;/a&gt;
for the first time and tried to replace the navigation tree on the left
by the &lt;a href="http://blogs.atlassian.com/2013/03/search-less-find-more-with-the-space-sidebar-in-confluence-5/"&gt;new space sidebar&lt;/a&gt;.
It felt strange to me because I always looked for things that were not
there. I tried to familiarise myself with the dynamic contents of the
sidebar, but I gave up after a couple of days. The mental overload was
too high.&lt;/p&gt;
&lt;p&gt;Why was it so difficult to get rid of the tree?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As a developer I'm used to thinking about tree-structured data so
    trees are in my head all the time..&lt;/li&gt;
&lt;li&gt;I encounter trees in many of my tools: my IDE, my file manager, my
    email client.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I live in a forest. As this is my natural habitat, I'm pretty good
with trees. I know how they work and how to find what I want as they are
an accurate representation of my mental model in most cases. In other
words: my brain was not ready for that sidebar thing.&lt;/p&gt;
&lt;p&gt;But there are other people in the world. People who tend to think in
different ways than a software developer. Scary, I know.&lt;br&gt;
Some of these people use the software I build and at the same time do
not like trees, in particular as navigational elements. Their mental
model is different from mine, maybe workflow-centric rather than
data-centric. They might not think of the data they're working with in
the same way I do. It's not unlikely that some of them do not think
about their data at all but simply execute a given (no-brainer) task.&lt;/p&gt;
&lt;p&gt;In these use cases, a tree is of limited value at best and can even be
harmful when the user is faced with too many options and too many paths
that do not get him where he wants to go. It is better to restrict user
choices and thereby make the user focus on the current task. A
context-sensitive UI helps guide the user through the given workflow
whereas a tree might encourage the user to meander through the
application and explore things are are not to be explored.&lt;/p&gt;
&lt;p&gt;No wonder a full chapter of &lt;a href="http://designinginterfaces.com/"&gt;Designing
Interfaces&lt;/a&gt; is about navigation. No
wonder the first paragraph &lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa511496.aspx"&gt;in Microsoft's developer documentation on
tree
views&lt;/a&gt;
is titled "Is this the right control?" No wonder even a small thing like
a breadcrumb bar deserves &lt;a href="http://patternry.com/p=breadcrumbs/"&gt;a full-blown description on
patternry&lt;/a&gt;. Because even with a
limited set of navigational widgets, you have many options to do it
right and many more to do it wrong. There's more to navigation than just
putting an extra menu bar somewhere. Or worse: a tree.&lt;/p&gt;
&lt;p&gt;Having written this already helps me to let go of the tree. Now it's
time to look for alternatives. In my JavaFX world this means goodbye
&lt;a href="http://docs.oracle.com/javafx/2/ui_controls/tree-view.htm"&gt;TreeView&lt;/a&gt;
and hello ...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://docs.oracle.com/javafx/2/ui_controls/menu_controls.htm#BABGHADI"&gt;Menu&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.oracle.com/javafx/2/api/javafx/scene/control/TabPane.html"&gt;TabPane&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.loop81.com/2011/11/javafx-2-breadcrumbs-and-styling.html"&gt;Breadcrumbs&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.oracle.com/javafx/2/ui_controls/list-view.htm#CEGGEDBF"&gt;ListView&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.oracle.com/javafx/2/ui_controls/accordion-titledpane.htm#CACGBAHI"&gt;Accordion&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;...?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'll save the answer for another blog post. Stay tuned.&lt;/p&gt;</content><category term="UI Design"/><category term="Confluence"/><category term="data model"/><category term="developer"/><category term="file manager"/><category term="forest"/><category term="IDE"/><category term="JavaFX"/><category term="Microsoft"/><category term="navigation"/><category term="Patternry"/><category term="sidebar"/><category term="software"/><category term="tree"/><category term="use cases"/><category term="user interface"/><category term="workflow"/></entry><entry><title>Designing Interfaces with JavaFX</title><link href="https://uwesander.de/designing-interfaces-with-javafx.html" rel="alternate"/><published>2013-04-24T05:35:00+02:00</published><updated>2013-04-24T05:35:00+02:00</updated><author><name>uwe</name></author><id>tag:uwesander.de,2013-04-24:/designing-interfaces-with-javafx.html</id><summary type="html">&lt;p&gt;Good design makes you happy. I was happy when I first saw an iPod, and
even happier when I first held it in my hand. Sometimes, you will not
notice the positive effects good design has on you, but you will always
notice the effects of &lt;a href="http://www.topdesignmag.com/20-examples-of-bad-web-design/"&gt;bad
design&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'm a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Good design makes you happy. I was happy when I first saw an iPod, and
even happier when I first held it in my hand. Sometimes, you will not
notice the positive effects good design has on you, but you will always
notice the effects of &lt;a href="http://www.topdesignmag.com/20-examples-of-bad-web-design/"&gt;bad
design&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'm a software developer, not a designer. I've known that ever since the
late 90's when I repeatedly failed at impressing my friends with my
one-minute Flash intro for my two-paged personal website over at
Geocities. Why haven't I learnt my lesson and still deal with visual
design today? Because I believe a good visual design is essential for
any software that has a user interface. If you don't believe me, read
&lt;a href="http://www.bennorthrop.com/Essays/2012/the-user-interface-and-the-halo-effect.php"&gt;what Ben Northrop wrote about the Halo
effect&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Designing a great user interface is hard, in particular when you're more
excited about your domain models and your test coverage of 101%.
Unfortunately, even smart developers can write bad user interfaces, as
&lt;a href="http://it-republik.de/jaxenter/news/Warum-so-viele-kluge-Leute-so-schlechte-Oberflaechen-entwickeln-061502.html"&gt;Karsten Lentsch explains in this Jax
session&lt;/a&gt;
(in German). It takes a lot of time, training, and user research until
you come up with a user interface that doesn't suck and that is tailored
to your user's needs.&lt;/p&gt;
&lt;p&gt;If you're lucky, you have a thorough and well-organised process for
that. If you're even luckier, you work with a professional user interface
designer who can do all those things for you. That's also a great way to
find a scapegoat in the end, by the way.&lt;br&gt;
But even working with a designer doesn't save you from analysing your
information architecture and from organising your application content.
There's still a lot to do.&lt;/p&gt;
&lt;p&gt;And this is what I'm currently doing. My job is to develop a user
interface for a client-server business application offering a compelling
user experience. Here's what I have so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a visual designer&lt;/li&gt;
&lt;li&gt;the great book &lt;a href="http://www.amazon.com/Designing-Interfaces-Jenifer-Tidwell/dp/1449379702"&gt;Designing Interfaces by Jenifer
    Tidwell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;a prototype of the client&lt;/li&gt;
&lt;li&gt;four &lt;a href="http://en.wikipedia.org/wiki/Persona"&gt;personas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;and lots of ideas&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oh wait! I have some fantastic tools, too. They are centred around
&lt;a href="http://docs.oracle.com/javafx/"&gt;JavaFX&lt;/a&gt; as we saw the potential of this
platform pretty early in our project and it has not disappointed us
since. JavaFX supports the design process by its separation into layout
(FXML), logic (controller classes), and appearance (CSS) tools. All of
these can be tied together nicely with our underlying Eclipse rich
client platform thanks to Tom's &lt;a href="http://www.efxclipse.org/"&gt;e(fx)clipse&lt;/a&gt;
project.&lt;br&gt;
There's nothing like a good tool chain. Well, a good design maybe.&lt;/p&gt;
&lt;p&gt;So I know JavaFX allows me to develop great user interfaces, but how do
I actually do it? How do I put the pieces together? This is the main
question I'm going to ask my designer and myself again and again over
the course of the next weeks and months. We have a general layout for
our application window, which is fairly common in other applications, I
would say, but one of the first things to decide now will be the
navigation. How can the user go forward and backward in our application?
How does she know where she is? And how do we tell her what else there
is?&lt;br&gt;
Exploring these questions is going to be part of my next blog posts.
Stay tuned.&lt;/p&gt;</content><category term="JavaFX, UI Design"/><category term="design"/><category term="e(fx)clipse"/><category term="Eclipse"/><category term="Halo effect"/><category term="iPod"/><category term="Java"/><category term="JavaFX"/><category term="Jenifer Tidwell"/><category term="personas"/><category term="user experience"/><category term="user interface"/></entry></feed>