Voorwoord

Waar ben ik tegenaan gelopen en hoe heb ik het opgelost.

Ik heb een aardig geheugen, ik kan mij heel goed herinneren dat ik een probleem eerder ben tegengekomen. Ik weet zelfs nog dat ik het opgelost. Hoe ik het heb opgelost is een ander verhaal. Dat verhaal staat hier beschreven.

Privacy statement: deze site zet geen cookies. De standaard Apache logging staat aan, die logfiles blijven ongeveer twee weken bewaard. In die logfiles is je IP adres te vinden, de pagina die je opgevraagd hebt, welke browser je gebruikt en hoe laat je dat gedaan hebt. Deze gegevens worden aan niemand doorgegeven.

Veertien dagen, waarom eigenlijk? Als ik ooit de logfile van gisteren bekeken heb, houdt het wel op. Het is nu twee dagen geworden.

Deze site

Deze site is geschreven in AsciiDoc en naar HTML vertaald met AsciiDoctor. Asciidoc is een formaat wat gemakkelijk te gebruiken is en waar toch veel mee mogelijk is.

De huidige versie van de site bestaat uit enkel AsciiDoc bestanden. De oplettende lezer zal hebben gezien dat de stylesheet van fonts.googleapis.com komt en een aantal fonts van fonts.gstatic.com. Een volgende versie zal alles van mijn eigen server halen.

Linux

Ik gebruik Debian met Gnome. De beschrijvingen van de oplossingen gelden dus in ieder geval voor deze omgeving.

Gnome goed zetten

Het probleem

Bij een nieuwe gnome-installatie missen een aantal instelling:

  • Pictogrammen op het werkblad

  • Alt-Tab wisselt tussen wel bureaubladen en niet tussen applicaties met de zelfde naam

  • Het normale menu is niet meer zichtbaar

  • Je kunt niet iets wissen door op delete te drukken in nautilus

  • Gelocked scherm na resume of suspend

Pictogrammen op het werkblad

In het programma 'afstelhulp' is een linkerbalk, klik daar op 'Bureaublad'. Rechts staat een knop met "Pictogrammen op bureaublad', schuif die simpelweg naar 'AAN'

Alt-Tab

In het programma 'afstelhulp' is een linkerbalk, klik daar op 'Uitbreidingen' en zet 'Alternatetab' op 'AAN'. Nu zullen alleen programma’s van het huidige werkblad getoond worden.

Normale menu zichtbaar maken

In het programma 'afstelhulp' is een linkerbalk, klik daar op 'Uitbreidingen' en zet 'Applications menu' op 'AAN'. Eindelijk weer het menu terug.

Delete

De standdaard toets is Ctrl-Delete geworden. Maar je kunt het veranderen. Open het bestand ~/.config/nautilus/accels, zoek naar ; (gtk_accel_path "<Actions>/DirViewActions/Trash" "<Primary>Delete") en verwijder het <Primary> stukje en de ; aan het begin van de regel. Je zult nautilus wel even moeten herstarten (of je hele PC als je net van windows komt).

Gelocked scherm

Op m’n laptop wil ik geen gelocked scherm na een resume of suspend. Open dconf-editor, kies dan links: org/gnome/desktop/screensaver en zet lock-enabled uit.

Find files

Het zoeken van files en er iets mee doen is altijd weer zoeken, hier een voorbeeld.

Zoek files van 20 dagen of ouder en toon ze: find . -mtime +20 -exec ls -l {} \;

Certificaten om in te loggen

Wat is er mooier dan zelf certificaten maken om via https in te loggen in je eigen website. Deze beschrijving regelt het maken in installeren van die certificaten. De authenticatie gaat niet verder dan het controleren of het certificaat klopt.

Selfsigned server certificate maken

Maken een private key:

openssl genrsa -des3 -out server.key 2048

Maak een CSR

openssl req -new -key server.key -out server.csr

Verwijder het wachtwoord van de key

cp server.key server.key.org
openssl rsa -in server.key.org -out server.key

Maak een self signed certificate (dit zou je ook kunnen gebruiken voor je server key, maar daar gebruik je tegenwoordig let’s encrypt voor.

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

De client keys

Nu gaan we de keys maken waarmee je client via de browser kan inloggen.

Maak een client key aan

openssl genrsa -des3 -out jaap.key 2048

Maak een CSR voor de client

openssl req -new -key jaap.key -out jaap.csr

(vul hier de velden in. Common Name wordt belangrijk, hier kan je b.v. mee zoeken in de LDap)

ga nu de client key tekenen

openssl x509 -req -days 365 -in jaap.csr -CA server.crt -CAkey server.key -set_serial 01 -out jaap.crt

Maak er een pkcs van

openssl pkcs12 -export -clcerts -in jaap.crt -inkey jaap.key -out jaap.p12

Maak er een gecombineerde pem van

openssl pkcs12 -in jaap.p12 -out jaap.pem -clcerts

Maar wat we met dit laatste moeten, is mij niet duidelijk. Het stond zo aardig in het voorbeeld…​

De Server

Nu moet het geheel op de server worden geïnstalleerd. Om het goed te laten werken zal je de headers module moeten laden.

Een mooie beschrijving is te vinden op Apache site. Daar kan ik verder weinig aan toevoegen. Dat weinige zijn de headers. Het zou kunnen dat je client applicatie iets wil doen met de gegevens van de ingelogde gebruiker. Hieronder een voorbeeld:

<Directory /home/jaap/public_html>
SSLVerifyClient require
SSLVerifyDepth 1
Header set MyHeader "Hello Jaap, dit is een header voor de browser, op de terugweg."
RequestHeader set X-Client-Name "%{SSL_CLIENT_S_DN_CN}s"
RequestHeader set X-Client-Verify "%{SSL_CLIENT_VERIFY}s"
RequestHeader set X-Client-Serial "%{SSL_CLIENT_M_SERIAL}s"
RequestHeader set X-Client-DN "%{SSL_CLIENT_S_DN}s"
</Directory>

Exim4

Frozen messages

Verwijderen van frozen messages kan met de volgende opdracht:

exim -bpr | grep frozen | awk {'print $3'} | xargs exim -Mrm

Wil je het aantal verstuurpogingen verlagen, pas dan /etc/exim4/exim4.conf.template aan. Geef ignore_bounce_errors_after en/of timeout_frozen_after een lagere waarde. Vergeet dan vooral niet Exim te herstarten. Zie: exim site.

Java

JaxB marshalling

Het probleem

Bij het marshallen van een JAXB object komt de merkwaardige foutmelding "javax.xml.bind.JAXBException: package.Class is not known to this context". De class Class is wel te vinden, maar toch gaat het fout.

De oorzaak

Het bleek dat er meerdere WSDL’s waren waarmee code was gegenereerd. In de pom.xml waren de WSDL’s allemaal in een execution gemaakt. Die WSDL’s hadden in de zelfde namespace verschillende classes waardoor er een verkeerde ObjectFactory is gemaakt.

De oplossing

Voor elke WSDL een nieuwe packagename laten genereren, dit kan door meerdere executions te gebruiken. Zie http://stackoverflow.com/questions/17674456 En ja, de pom.xml wordt wel erg groot.

Lessons learned

Maak WSDL’s zorgvuldig: niet de zelfde namespace gebruiken voor verschillende dingen.

Date naar LocalDate conversie (en terug)

Probleem

LocalDate en Date moeten vaak van de een naar de ander omgezet worden, een heel makkelijke methode is er niet Oplossing

LocalDate naar Date

LocalDate localDate = ...
Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());

Maar, het kan nog makkelijker:

Date date = java.sql.Date.valueOf(localDate);

Date naar LocalDate

Date date = ...
LocalDate ld = new java.sql.Date(date.getTime()).toLocalDate();

Het kan ook weer langer:

Date date = ...
LocalDate ld = Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();

JSF FacesContext testen

Testen van controllers met een FacesContext is wat gedoe, maar als je de test overerft van onderstaande test, krijg je geen NullPointerException bij FacesContext.getCurrentInstance().get…​()

@RunWith(MockitoJUnitRunner.class)
public abstract class FacesContextMockingTest {
    @BeforeClass
    public static void facesContextSetup() {
        FacesContext facesContext = TestFacesContext.mockFacesContext();
        ExternalContext external = mock(ExternalContext.class);
        when(facesContext.getExternalContext()).thenReturn(external);
        assertThat(FacesContext.getCurrentInstance()).isEqualTo(facesContext);
    }

}

abstract class TestFacesContext extends FacesContext {
    public static FacesContext mockFacesContext() {
        FacesContext context = mock(FacesContext.class);
        setCurrentInstance(context);
        return context;
    }
}

Prachtige code

Exception throws Exception

Een beetje programmeur weet hoe hij een exception moet maken, maar niet de programmeur van dit stukje code:

public class FlutException extends Exception {
  public FlutException(String message) throws FlutException {
    super(message);
  }
}

Een exception die zichzelf throwed?

VI

search/replace

:%s/search_string/replacement_string/g

Verplaats regel omhoog

ddkP

Wis een regel (zet die in de buffer), ga omhoog en voeg in boven de huidige regel.

Oracle en Hibernate

Dubbele nummertjes

Het probleem

De applicatie draaide prima. Geen enkele klacht was ons ter ore gekomen. Plotseling kregen we vreemde foutmeldingen in onze ontwikkelomgeving: er was een DB constraint waar we tegenaan liepen. Al snel bleek het de primary key te zijn. Uit de code bleek dat we netjes een uniek getal uit een Oracle sequence haalden. Toch zagen we in de database records met een id dat een paar honderd hoger was dan de laatste sequence.

Het duurde even voor we het lek boven hadden. De sequence in Oracle was gedefinieerd met een increment van één, dus elke keer als iemand een nieuwe waarde opvroeg werd het getal met één verhoogd.

In de Hibernate configuratie stond een increment_size van 1000 en een optimzer die 'pooled-lo' was. Uit de documentatie bleek dat Hibernate in dat geval een getal uit de sequence haalt en daar intern door mee gaat tellen tot hij 1000 getallen had uitgegeven, daarna zou Hibernate pas een nieuw getal op vragen.

We hadden meerdere applicaties draaien in onze ontwikkelomgeving. Het is mij een raadsel hier niemand eerder tegenaan is gelopen. In alle andere omgevingen ging het wel goed, daar was de sequence in Oracle wel gedefinieerd met een increment van 1000.

Uitleg

Hibernate heeft een optimalizatie bedacht waardoor zij niet telkens aan de database om een sequencenummer moet vragen. Hibernate vraagt een nummer, en hoogt dat zelf een aantal keer op. Pas als er een 'increment' aantal keer een nieuw nummer is uitgegeven, vraagt Hibernate weer een nieuw nummer. Dit gaat natuurlijk alleen goed als de database een zelfde aantal nummers overslaat als de database.

Dit geldt voor Hibernate versie 3 en 4.

Lessons learned

Kijk naar de specs, wat bedoelen ze, snap je het.

Performance issues oplossen is leuk, maar bedenk dat dat altijd weer andere problemen oplevert. Dit is een mooi geval van optimalisatie die verkeerd uitpakt. Wellicht heb je iets minder communicatie met je database, maar de kans op fouten is een stuk groter.

Wildfly

Op het moment gebruik ik versie 10.1 van Wildfly.

Installatie als daemon

Kopieer het init.d script uit Wildfly:

cp /opt/wildfly/docs/contrib/scripts/init.d/wildfly-init-debian.sh /etc/init.d/wildfly

Kopieer de defaults:

cp /opt/wildfly/docs/contrib/scripts/init.d/wildfly.conf /etc/default/wildfly

Pas /etc/default/wildfly aan naar je eigen wensen, vooral de JDK is natuurlijk van belang.

Maak /var/log/wildfly/ aan en maak de Wildfly user en groep daar eigenaar.

Zorg dat wildfly ook echt als service kan draaien:

update-rc.d wildfly defaults
update-rc.d wildfly enable

Start tenslotte Wildfly service wildfly start

Poorten veranderen

In mijn geval was poort 8080 al bezet. Wildfly heeft een file waar de standalone configuratie aangepast kan worden: $WILDFLY_HOME/standalone/configuration/standalone.xml

Zoek naar de string jboss.socket.binding.port-offset:0 en maak van die 0 een ander getal, alle poorten zullen met dat getal verhoogd worden.

Logging

Logging gaat standaard naar de algemene log. Dit is vrij eenvoudig om te zetten. Als er een bestand met (o.a.) de naam jboss-log4j.xml dan zal Wildfly dit bestand gebruiken.

Als er een fileappender wordt gebruikt, wil je hier geen hardcoded path in hebben. Dit kan door in Wildfly een system property aan te maken met b.v. de naam "LOGDIR" en de waarde "/var/log/wildfly". In je appender gebruik je dan:

<param name="file" value="${LOGDIR}/mijnlog.log">;

Nadeel van deze manier is dat als je je jboss-log4j.xml aanpast, Wildfly opnieuw opgestart moet worden voordat je het resultaat ziet.

Wildfly, JSF en Apache Shiro

Uitloggen

Uitloggen gaf een vreselijke stacktrace op het scherm, Shiro logt uit door de sessie te wissen, maar JSF heeft dat liever niet.

Dit heb ik als volgt opgelost:

    public String uitloggen() {
        currentUser.logout();
        return "/index.xhtml?faces-redirect=true";
    }

Docker

Table 1. Docker
Command Description

docker version

Does Docker work?!

docker run learn/tutorial echo "hello world"

Run the command echo "hello world" in tutorial.

docker run learn/tutorial ps -l

Check out the processes in the container.

docker run -t -i ubuntu /bin/bash

Start a shell in the ubuntu container.

docker exec -ti {HASH} /bin/bash

Connect to a running container.

docker run –d {imagename}

Run container in background

docker build -t {imagename} .

Build a new image from the Dockerfile in the current directory.

docker images

List all your local images.

docker ps

List all your running containers.

docker ps -a

List all your containers.

docker inspect {HASH}

Show the properties of a docker container

docker stop {HASH}

Stop a docker container

docker exec -ti {HASH} {COMMAND}

Start an interactive session with a container and start a command (i.e. bash)

docker rmi {HASH}

Remove an image.

docker rm {HASH|NAME}

Remove a container.

docker rm $(docker ps -aq)

Remove all containers.

Table 2. Docker-compose
Command Description

docker-compose up –d

Start multiple containers defined in de docker-compose.yml file

docker-compose stop

Stop multiple containers defined in de docker-compose.yml file

docker-compose rm

Remove multiple containers defined in de docker-compose.yml file

Table 3. Docker Machine
Command Description

docker-machine ls

Show the virtualboxes, also with the URL’s

docker-machine env

Show the environment variables, can be used to set the environment vars (see last line)

Docker-machine ssh

Log in to the virtual box where docker runs with a ssh connection.

Advanced

Get the shell on the current running docker:

docker exec -ti `docker ps -q` /bin/bash`

Error

Error with "No such image:" in docker-compose. An old cache issue.

docker-compose rm will probably solve the error.

Certificate signed by unknown authority

In Linux:

  • Create a folder /etc/docker/certs.d

  • In that folder: servername.com:portnumber

  • Create a file ca.crt with the certificate of that server.

Git

Branches

Branch maken van tag

git checkout -b <nieuwnaam> <bestaande-tag>

Clone met fork

Werken met een git repo waar je niet direct naar mag pushen, maar wel naar de fork:

Iets toevoegen aan voorgaande commit

  • git add .

  • git commit --amend

Mocht de commit al gepushed zijn, dan moet de volgende push met -f

Cherry Pick

Maak veel aparte commits, dat maakt het cherry picken een stuk makkelijker.

Er is een aanpassing gedaan in een aparte branch. Daarvan is weer een release gemaakt. Die release tag moet weer terug naar de master.

  • git checkout master

  • zoek de commits die je wilt hebben (bv in gitlab)

  • git cherry-pick 00d812..db2 13644d…​b9c (maar dan met de hele hashes)

Als je een verkeerde commit hebt gedaan en je wilt daar de pom.xml niet van meenmemen:

  • git reset pom.xml

  • git checkout pom.xml

Tenslotte kan de boel gecommit worden:

  • git commit

Oeps, oude commit was fout

Stel dat een commit fout was die niet de laatste was. Hoe pas je dat aan.

Tel in git log de hoeveelste commit dit was.

Doe git rebase -i HEAD~5 en verander de aan te passen commit van pick in edit.

Doe de veranderingen en ga weer met git aan de gang:

  • git add .

  • git commit --amend

  • git rebase --continue

  • git push --force

Die laatste --force is natuurlijk alleen nodig als de oude commits al remote stonden.

Andere fork pulllen

git remote add fork-x git@git02.example.org:andere.fork/derepo.git

git pull fork-x

git checkout BRNC-1507

Nu is het probleem: hoe krijg je dit weer in je eigen repo…​

Mergen naar de pilot of master branch

Ga naar je feature branche:

git checkout BRNC-1507

Maak een tijdelijke branch aan:

git checkout -b BRNC-1507-TMP

Dan een rebase:

git rebase pilot

Ervan uitgaande dat er geen conflicten zijn:

git checkout pilot

git merge BRNC-1507-TMP

en tenslotte:

git push

Als je de tijdelijke branch wilt verwijderen:

git branch -D BRNC-1507-TMP

Met een hoofdletter D, er is nog geen push gedaan naar de fork.

Submodules

Als er een project is waarin een git submodule zit, is dit als volgt te activeren:

git submodule init

git submodule update

Push van bepaalde commit

Enkel een bepaalde commit pushen, maar niet je laatste:

git push origin PURP-790~2:PURP-790 -f

Libre Office

Samenvoegen bestanden

Doel is om een bestand samen te voegen met een spreadsheet. Vervolgens moet per regel uit het spreadsheet (een adres) een apart PDF bestand gemaakt worden zodat er makkelijk gemaild kan worden.

  1. Maak het spreadsheet met de adressen

  2. Maak het tekstbestand met de tekst

  3. In dat laatste bestand: menu Bewerken, Database wisselen

  4. Ga met bladeren naar het spreadsheet met adressen, open dat

  5. Klik in het venster recht boven het spreadsheet open en selecteer het blad wat je wilt gebruiken, klik dan Definiëer

  6. Dan kan je velden toevoegen: Invoegen, Velden, Overige

  7. Daar kan je in tabblad database je velden vinden

Zo, je document is gemaakt.

Nu het maken van de PDF’s.

  1. Kies Bestand, Afdrukken

  2. Libre Office vraagt of je een standaard brief wilt afdrukken: Ja

  3. In het volgende scherm selecteer je het volgende:

    • Records: alles

    • Uitgave: Bestand

    • Sla op als afzonderlijke documenten

    • Selecteer het veld wat je wilt gebruiken voor de bestandsnaam

    • Selecteer opmaak (PDF) en pad.

    • OK

Klaar.

Maven

Verander de versie van het hele project

mvn versions:set -DnewVersion=<nieuwe-versie>

Oracle en Hibernate

Dubbele nummertjes

Het probleem

De applicatie draaide prima. Geen enkele klacht was ons ter ore gekomen. Plotseling kregen we vreemde foutmeldingen in onze ontwikkelomgeving: er was een DB constraint waar we tegenaan liepen. Al snel bleek het de primary key te zijn. Uit de code bleek dat we netjes een uniek getal uit een Oracle sequence haalden. Toch zagen we in de database records met een id dat een paar honderd hoger was dan de laatste sequence.

Het duurde even voor we het lek boven hadden. De sequence in Oracle was gedefinieerd met een increment van één, dus elke keer als iemand een nieuwe waarde opvroeg werd het getal met één verhoogd.

In de Hibernate configuratie stond een increment_size van 1000 en een optimzer die 'pooled-lo' was. Uit de documentatie bleek dat Hibernate in dat geval een getal uit de sequence haalt en daar intern door mee gaat tellen tot hij 1000 getallen had uitgegeven, daarna zou Hibernate pas een nieuw getal op vragen.

We hadden meerdere applicaties draaien in onze ontwikkelomgeving. Het is mij een raadsel hier niemand eerder tegenaan is gelopen. In alle andere omgevingen ging het wel goed, daar was de sequence in Oracle wel gedefinieerd met een increment van 1000.

Uitleg

Hibernate heeft een optimalizatie bedacht waardoor zij niet telkens aan de database om een sequencenummer moet vragen. Hibernate vraagt een nummer, en hoogt dat zelf een aantal keer op. Pas als er een 'increment' aantal keer een nieuw nummer is uitgegeven, vraagt Hibernate weer een nieuw nummer. Dit gaat natuurlijk alleen goed als de database een zelfde aantal nummers overslaat als de database.

Dit geldt voor Hibernate versie 3 en 4.

Lessons learned

Kijk naar de specs, wat bedoelen ze, snap je het.

Performance issues oplossen is leuk, maar bedenk dat dat altijd weer andere problemen oplevert. Dit is een mooi geval van optimalisatie die verkeerd uitpakt. Wellicht heb je iets minder communicatie met je database, maar de kans op fouten is een stuk groter.

Wildfly

Op het moment gebruik ik versie 10.1 van Wildfly.

Installatie als daemon

Kopieer het init.d script uit Wildfly:

cp /opt/wildfly/docs/contrib/scripts/init.d/wildfly-init-debian.sh /etc/init.d/wildfly

Kopieer de defaults:

cp /opt/wildfly/docs/contrib/scripts/init.d/wildfly.conf /etc/default/wildfly

Pas /etc/default/wildfly aan naar je eigen wensen, vooral de JDK is natuurlijk van belang.

Maak /var/log/wildfly/ aan en maak de Wildfly user en groep daar eigenaar.

Zorg dat wildfly ook echt als service kan draaien:

update-rc.d wildfly defaults
update-rc.d wildfly enable

Start tenslotte Wildfly service wildfly start

Poorten veranderen

In mijn geval was poort 8080 al bezet. Wildfly heeft een file waar de standalone configuratie aangepast kan worden: $WILDFLY_HOME/standalone/configuration/standalone.xml

Zoek naar de string jboss.socket.binding.port-offset:0 en maak van die 0 een ander getal, alle poorten zullen met dat getal verhoogd worden.

Logging

Logging gaat standaard naar de algemene log. Dit is vrij eenvoudig om te zetten. Als er een bestand met (o.a.) de naam jboss-log4j.xml dan zal Wildfly dit bestand gebruiken.

Als er een fileappender wordt gebruikt, wil je hier geen hardcoded path in hebben. Dit kan door in Wildfly een system property aan te maken met b.v. de naam "LOGDIR" en de waarde "/var/log/wildfly". In je appender gebruik je dan:

<param name="file" value="${LOGDIR}/mijnlog.log">;

Nadeel van deze manier is dat als je je jboss-log4j.xml aanpast, Wildfly opnieuw opgestart moet worden voordat je het resultaat ziet.

Wildfly, JSF en Apache Shiro

Uitloggen

Uitloggen gaf een vreselijke stacktrace op het scherm, Shiro logt uit door de sessie te wissen, maar JSF heeft dat liever niet.

Dit heb ik als volgt opgelost:

    public String uitloggen() {
        currentUser.logout();
        return "/index.xhtml?faces-redirect=true";
    }