Author Archive

Server folder as photo gallery in web browser

August 26, 2020 Leave a comment

I wanted to make image folders on our family file server browseable and provide convenient image gallery features like index view, slideshow, etc.

The file server is on our home network and runs Debian GNU/Linux.

I installed webfs, configured it to use port 80 (instead of its default port 8000), added a symbolic link to the root folder of the image folders that I wanted to make browseable (/opt/data in my case) and restarted webfsd:

sudo apt install webfs
sudo sed 's/web_port=.*/web_port="80"/' -i /etc/webfsd.conf
sudo sed 's/web_root=.*/web_root="/var/www/html"' -i /etc/webfsd.conf
sudo sed 's/web_index=.*/web_index="index.html"/' -i /etc/webfsd.conf
sudo ln -s /opt/data /var/www/html
sudo service webfs restart

The name of the server is “bubba” and at this point pointing a web browser to http://bubba/ already worked, but it only showed a simple listing of file names like this:

Next I installed the Slideshow Firefox add-on on all our computers, and now the image folder is presented like this:

Mission accomplished! 😀

Categories: debian, firefox, linux Tags: , ,

Consumer Back Pressure applied to Reactive Streams in Java 9+

May 3, 2020 Leave a comment

I watched this excellent presentation on 0.75 speed to accommodate my mind, i.e. a slow stream consumer in my case, as I was feeling tired. :)

#overload #iogames

Connect to Cisco AnyConnect using Debian buster

February 28, 2020 Leave a comment

My employer uses a Cisco AnyConnect VPN.

Today I set up my Debian GNU/Linux 10 (“buster”) to connect to it, using only Open Source components.

My setup:

  • XFCE desktop
  • network-manager
  • openconnect

To install the required packages:

sudo apt install openconnect network-manager-openconnect-gnome network-manager-gnome curl xmlstarlet

The curl and xmlstarlet packages are used by, a Cisco Anyconnect CSD wrapper script included with OpenConnect.

Debian 10 by default demands at least TLS 1.3 which caused this error:

error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol

I fixed it by creating a more relaxed openssl configuration:

sudo cp /etc/ssl/openssl.cnf /etc/ssl/openssl_tls_1_0.cnf
sudo vi /etc/ssl/openssl_tls_1_0.cnf

Change only the “MinProtocol” line towards the end of the file to

MinProtocol = TLSv1.0

Then add a helper script /usr/local/bin/ to use the relaxed config:

export OPENSSL_CONF="/etc/ssl/openssl_tls_1_0.cnf"
/usr/libexec/openconnect/ "$@"

Then configure your VPN connection through the network-manager applet (you might have to logout/login to let XFCE autostart the systray applet):

Check “Allow Cisco Secure Desktop trojan” and as “CSD Wrapper Script” use /usr/local/bin/ :

And finally connect:

Categories: bash, debian Tags: , ,

Stay safe on the web using Firefox and these add-ons

November 1, 2019 Leave a comment

First get Firefox then use it to access the links below and install the respective add-ons.

uBlock Origin” by Raymond Hill
Efficient ad-blocker. Easy on CPU and memory

Privacy Badger” by EFF
Automatically learns to block invisible trackers

HTTPS Everywhere” by EFF
Enforces HTTPS encryption whenever possible, even when opening plain http links

Flagfox” by Dave G
Displays location country flag for current website, site safety check, whois lookup

Decentraleyes” by Thomas Rientjes
Prevents content delivery networks (CDN) from tracking you

Facebook Container” by Mozilla
Prevents Facebook from tracking you around the web

Categories: coding Tags: , ,


October 6, 2019 Leave a comment

I use as my general diagram drawing tool for capturing ideas, overview diagrams, etc.

Both the online version and offline app (electron based) are free.

The company behind it successfully makes money via Confluence plugin licenses.

I like the feature set and ease of use.

Adoption (vs former market leader Gliffy) is trending.

And it’s Open Source! :)

Categories: apps Tags: ,

FileWatcherReloadingTrigger for Apache Commons configuration2

August 30, 2019 Leave a comment

Apache Commons configuration2 lets you deal with configurations from many sources and formats.

If you want to be able to edit your config file and have your application pick up the changed config without restart, configuration2 provides a flexible approach, but only a somewhat unsatisfying trigger implementation (PeriodicReloadingTrigger), that uses polling to detect config file changes.

Below is my FileWatcherReloadingTrigger class that uses a WatchService to listen for change events from the underlying filesystem and avoids polling. This way your application can almost immediately use the changed configuration.

It can be used in basically the same way as the PeriodicReloadingTrigger, as described by the configuration2 user guide:

Parameters params = new Parameters();
// Read data from this file
File propertiesFile = new File("");

ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> builder =
    new ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
FileWatcherReloadingTrigger trigger = new FileWatcherReloadingTrigger(builder.getReloadingController(),
    null, propertiesFile.toPath());

And this is the FileWatcherReloadingTrigger class. It uses slf4j-api for logging:

package org.guppy4j.config;

import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.commons.configuration2.reloading.ReloadingController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class FileWatcherReloadingTrigger {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private final ExecutorService executorService;
	private final WatchService watchService;

	private final ReloadingController controller;
	private final Object controllerParameter;

	private final Path configFilePath;

	private Future<?> execution;

	FileWatcherReloadingTrigger(ReloadingController controller, Object controllerParameter, Path configFilePath) {
		Objects.requireNonNull(controller, "Reloading controller must not be null");
		Objects.requireNonNull(configFilePath, "The path for the configuration file must not be null");
		this.configFilePath = configFilePath.toAbsolutePath();
		this.controller = controller;
		this.controllerParameter = controllerParameter;
		try {
			watchService = FileSystems.getDefault().newWatchService();
			this.configFilePath.getParent().register(watchService, ENTRY_MODIFY);
		} catch (IOException e) {
			throw new IllegalStateException("Could not set up WatcherService for config file reloads", e);
		executorService = Executors.newSingleThreadExecutor();

	public synchronized void start() {
		if (executorService.isShutdown()) {
			throw new IllegalStateException("Already shut down");
		if (execution == null || execution.isCancelled()) {
			execution = executorService.submit(this::watchForFileChanges);"Execution started. Watching for file changes of {}", configFilePath);

	public synchronized void stop() {
		if (execution != null && !execution.isCancelled()) {
			execution = null;"Execution stopped. No longer watching for file changes of {}", configFilePath);

	private void watchForFileChanges() {
		try {
			for (WatchKey watchKey = watchService.take(); watchKey != null; watchKey = watchService.take()) {
				for (WatchEvent<?> event : watchKey.pollEvents()) {
					if (ENTRY_MODIFY.equals(event.kind())) {
						final Path filename = Path.class.cast(event.context());
						if (filename.equals(configFilePath.getFileName())) {
		} catch (InterruptedException e) {
			// nothing to do

	public synchronized void shutdown() {
		try {
		} catch (RuntimeException e) {
			logger.warn("Exception while shutting down", e);
		try {
		} catch (RuntimeException e) {
			logger.warn("Exception while shutting down executorService", e);
		try {
		} catch (IOException e) {
			logger.warn("Exception while shutting down watchService", e);

Cleanbrowsing DNS + dnsmasq

August 17, 2019 Leave a comment

I have children and I want to keep porn out of our home network.

Cleanbrowsing DNS provides a free “Family” filter. You can set your router to use their DNS servers. It seems quite good in comparison to other DNS filters:

However, their filter is sometimes a little strict and even blocks and, curiously, the Haligonian event magazine website

On the other hand, it does not block user-uploaded images on Twitter, and its many porn peddling accounts.

So basically what I wanted was a configurable whitelist and blacklist on top of the Cleanbrowsing Family filter.

To do that, I installed dnsmasq on a Linux server in our network and configured the DHCP server on our router to give out the IP address of that Linux box as DNS server, effectively directing all machines on our home network to get their DNS from the dnsmasq installation.

I configured dnsmasq as a proxy that by default passes on all DNS request to the Cleanbrowsing Family filter:

This is the content of /etc/dnsmasq.d/cleanbrowsing.conf :

# ignore /etc/resolv.conf

# use cleanbrowsing family nameservers as default

This is currently the content of /etc/dnsmasq.d/whitelist.conf :


This is currently the content of /etc/dnsmasq.d/blacklist.conf :

# block twitter user media servers (porn and tracker pixels)

Whenever I edit any of the above I have to restart the dnsmasq service.

The files in /etc/dnsmasq.d are read by default on my Debian GNU/Linux. If you use a different distro you might have to adjust /etc/dnsmasq.conf accordingly, look for the “conf-dir” directive.

If your router allows you to configure outgoing firewall rules, block all DNS requests from anywhere but the dnsmasq server, to prevent a savvy teenager from bypassing your DNS filter.

Categories: coding, debian, linux Tags: , , ,

Free long-term-support OpenJDK 8 for Windows with Webstart

April 30, 2019 Leave a comment

Now that Oracle Java 8 is no longer free-of-charge for commercial use, and Oracle is dropping Webstart, you might be looking for an alternative way to let your users still execute your Webstart based Java applications.

There are two community based projects that provide OpenJDK builds for various platforms for free and who have committed to providing security update builds for an extended time:

The Java 8 MSI installer from ojdkbuild has options for installing OpenJFX, Webstart (the Open Source implementation from IcedTea-web) and an Update notifier.

The AdoptOpenJDK team is working on a similar package, also offering IcedTea-web.

Overall, it seems that Oracle’s move to charge commercial users for Java might be a good boost for OpenJDK community builds.

Security updates for Java 8 until 2023 and for Java 11 until 2024, are made possible by Redhat taking over the respective OpenJDK Updates projects.

Categories: coding, java Tags: ,

Setting up a free C64 emulator for retro game fun

August 8, 2018 Leave a comment

I just installed the C64 emulator VICE on an old Windows laptop and set it up with shortcuts for some old time games that I used to play in the 80s.

My 6 year old son really likes Donald Duck’s Playground where you do odd jobs as Donald to earn cents and dollars to buy playground equipment for your nephews and let them play:

Setting up the game required the download of a zip archive containing a *.d64 image file that can be autostarted by VICE. I created a desktop shortcut to the x64.exe file in VICE with the path of the d64 file as command line parameter. That gives you a shortcut that will start VICE and autostart the game right away. Add the -fullscreen option to start the emulator in fullscreen mode.

I had to enable keyboard mapping for Joystick 2 as shown on the WinVice c64-Wiki.

VICE is a cool emulator that runs on Unix, MS-DOS, Win32, OS/2, BeOS, QNX 4.x, QNX 6.x, Amiga, Syllable or Mac OS.

Games I might try next include Spy vs Spy, Aztec Tomb, Q-Bert and I few others. :)

Klassikradio MP3 streams

April 4, 2018 Leave a comment

Klassikradio is a German radio station with an easy-to-digest selection of classical music, movie themes and other relaxing sounds, with not too many commercials.

They have many channels, all available as mp3 streams. They can be found and played in your browser at

To play the audio streams outside of a web browser, I retrieved and saved the stream urls as m3u files, using the shell script below. It used to work until Klassikradio changed its website:

#! /bin/bash


for x in $(wget -q -O - "${index_url}" | grep -o "${audio_url}"); do 
  echo "${x}" > "klassikradio-$(basename "${x}").m3u";

The resulting m3u files are still available from here.

M3U files are the easiest way to “bookmark” media streams to be played by an audio player of your choice.

I use the VLC player with the “Allow only one instance” setting and configure it as the default application for *.m3u files.

Categories: bash, music Tags: , ,