Categories
Development Linux

Bughunter

Am Wochenende habe ich zum ersten Mal einen Escape Room spielen dürfen und ich muss sagen, heute habe ich mich ähnlich gefühlt, denn das Bugfixing ähnelt auch einer kreativen rätselratenden Schnitzeljagd.

Der Bug

Die von mir entwickelte Anwendung ist mittlerweile im Pilot-Betrieb und ein Benutzer meldet, dass er einen Vorgang nicht beenden kann: Vermeindlich hochgeladene Bilder, die zwingend erforderlich sind, 'verschwinden' und so kann er nicht abschließend speichern.

Die Bilder werden dankenswerterweise mitgeliefert, so dass ich auch versuchen kann, das Problem nachzustellen.

Es gibt auch einen Hinweis, das das Ausführen einer bestimmten Funktion während des Vorgangs das Problem verursacht haben könnte. ERROR-Einträge in den Logfiles unterstützen diese These, aber letztendlich hatte es damit nichts zu tun. Es war nur eine falsche Fährte, eine Ablenkung, wie bei jedem guten Spiel 😉
Und die Erkenntnis, dass da noch irgendwo ein weiterer Bug schlummert, der von mir gejagt und entdeckt werden will.

Bug reproduzieren

Als erstes versuche ich, den Bug lokal nachstellen zu können, also auf meinem Entwickler-Laptop. Das funktioniert aber nicht, da alles fehlerfrei funktioniert, incl. Upload der beiden Bilder.

Hier wäre jetzt eine gute Gelegenheit, das Ticket mit "Works on my machine" zu schließen.

Works on my machine - Imgflip

Aber ich forsche weiter und versuche, das Problem auf dem Server nachzustellen. Und hier gelingt es: Die Bilder verschwinden auf magische Art und Weise. Und ohne Einträge in den Logs.

Analyse

Der Vorgang durchläuft einen Wizard, und nachdem man die Bilder hochgeladen hat, kann man einen Schritt weiter. Wenn man dann einen Schritt zurück geht, sind die Bilder weg.

Mit meiner Testdatei (WOW!!.png) funktioniert es hingegen.

Auffällig ist, dass die andere Datei nicht korrekt in die Tabelle der Dateien übertragen wird, sondern ewig in der Upload-Ansicht verharrt.
Es gibt aber keine Fehlermeldung. Die Datei ist 6,4 MB groß, erlaubt sind Dateigrößen bis 10 MB; bei größeren Dateien kommt auch ein entsprechender Hinweis.
Hier scheint der Upload nie enden zu wollen, was auch ein Hinweis ist, warum sie später verschwindet: sie war nie auf dem Server.

Mein erster Verdacht war, dass vielleicht ein Virenscanner dazwischenfunkt und die Datei auf dem Server direkt wieder löscht. Alles schon mal vorgekommen.
Also lade ich die beiden Bilder per FTP auf den Server, was auch funktioniert und somit gegen die Virenscannerhypothese spricht.

Also zurück in den Browser und mal schauen, was sich so in der Entwicklerkonsole tut. Und dort ist auch ein Eintrag:

Der Upload wurde abgebrochen (413 - Request Entity Too Large). Das hat das JQuery- bzw. PrimeFaces-Framework nicht richtig erkannt und keine Fehlermeldung auf der Oberfläche angezeigt. Ein Bug im Framework, dass dieser spezielle Fall nicht richtig erkannt wird. Das interessiert mich aber nicht so sehr, viel spannender ist der Fehler 413. Erlaubt sind 10 MB, die Datei sind keine 7 MB. Es müsste also eigentlich passen.

In der Anwendung sind 10 MB erlaubt, das sollte nicht das Problem sein, bliebe noch der Anwendungsserver, ein Apache Tomcat, oder der davorgeschaltete ReverseProxy, ein Apache HTTP-Server.

Wenn man dem Netzwerkverkehr folgt sieht man, dass folgende Antwort beim Upload-Versuch zurück kommt:

Das Indiz für die Ursache des Problems ist das unscheinbare Wort: "nginx"

Der Verdacht erhärtet sich beim weiteren Testen:
Wenn ich die WOW-Testdatei in der Anwendung hochlade, erscheint das POST in den Logs des vorgelagerten Apache-ReverseProxies:

Der Versuch des Hochladens des Problembildes erzeugt keinen Log-Eintrag.

Es scheint also so zu sein, dass der Upload bereits vor meiner Anwendung abgebrochen wird.

Der Server

Schauen wir mal, wie es auf dem Test Server aussieht:

Meine Anwendung starte ich über ein Docker-Compose, bestehend aus:

  • Apache HTTP als Reverse Proxy
  • Apache Tomcat
  • PG Admin
  • PostgreSQL Datenbank

Auf dem Server selbst läuft aber nicht nur meine Anwendung, sondern auch noch weitere, die uA über unterschiedliche Domainnamen erreichbar sind. Und das steuert ein Nginx-Server:

Und was finde ich, wenn ich das default File Limit für Nginx suche:

By default, Nginx has a limit of 1MB on file uploads.

Die Lösung

Wir müssen also den file upload für die Anwendung erhöhten auf 10MB, besser 15MB:

vim /etc/nginx/sites-available/myapp
server {
    server_name myapp.test.company.de;

     location / {
	[...]
        client_max_body_size        15M;
     }
     [...]
}

Nach der Änderung die Konfiguration prüfen, um sicher zu gehen, dass keine Fehler eingebaut wurden und beim Neustart (bzw. Reload) der ganze Nginx-Server lahm gelegt wird:

nginx -t

Erst dann den Neustart, bzw. Reload durchführen:

service nginx reload

Test

Abschließend muss natürlich noch der Test erfolgen, ob wir damit erfolgreich waren:
Der Upload funktioniert jetzt! 🥳

Categories
Java

PrimeFaces Template App

I need an easy way to show some database data of an existing application. The architecture and technic of the application is quite old and unconfortable, so I decided to setup a new project with a modern framework.

I need overview of data in a table, maybe with CSV or PDF file export. A chart to show the number of incoming data per time etc.
I want to use a framwork that provides components for this requirements, so I do not have to code much for things like paging, file export etc.
I one of my prior projects we used Java Server Faces, and so I came up to give PrimeFaces a try. They have a good ShowCase to show their components.

Unfortunatly it was a little bit more complex to setup the project than I thought at the beginning. No rocket science, but I took me some time for the initial setup, therefore I decided to extract this to a PrimeFaces Template Application to easily reuse it next time and uploaded it to GitHub.

Setup project

Created a new Maven project in Eclipse.
Added Eclipse Gitignore defaults from GitHub and target folder (created by Maven) to .gitignore file.
Added beans.xml, web.xml and index.xhtml files to project:


CDI

For CDI we need the beans.xml file. Nothing special, it just has to be there:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
	bean-discovery-mode="all">
</beans>

PrimeFaces Configuration

Minimum setup in web.xml, except of the explicit use of the Omega theme:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">

	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<context-param>
		<param-name>primefaces.THEME</param-name>
		<param-value>omega</param-value>
	</context-param>
	
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.xhtml</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.xhtml</welcome-file>
	</welcome-file-list>
</web-app>

Dependencies

I want to use Tomcat and not a EE application server like Payara. Therefore I have to add JSF. And I want to use the current version, which is 2.3, so I have to add CDI (JBoss Weld) also.

Since this JSF version, the JSF managed bean facility @ManagedBean is DEPRECATED in in favour of CDI and CDI has become a REQUIRED dependency for JSF 2.3.

Of course PrimeFaces has to be added as dependency, currently in version 8.0 and PrimeFaces Themes.

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>deringo</groupId>
	<artifactId>primefacestemplate</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>PrimeFacesTemplate</name>
	<description></description>

	<properties>
		<maven.compiler.source>15</maven.compiler.source>
		<maven.compiler.target>15</maven.compiler.target>
		<failOnMissingWebXml>false</failOnMissingWebXml>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	</properties>

	<repositories>
		<repository>
			<id>prime-repo</id>
			<name>PrimeFaces Maven Repository</name>
			<url>http://repository.primefaces.org</url>
			<layout>default</layout>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat</artifactId>
			<version>9.0.41</version>
			<type>pom</type>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>8.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.enterprise</groupId>
			<artifactId>cdi-api</artifactId>
			<version>2.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.jboss.weld.servlet</groupId>
			<artifactId>weld-servlet</artifactId>
			<version>2.4.4.Final</version>
		</dependency>
		<dependency>
			<groupId>javax.faces</groupId>
			<artifactId>javax.faces-api</artifactId>
			<version>2.3</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.glassfish</groupId>
			<artifactId>javax.faces</artifactId>
			<version>2.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.omnifaces</groupId>
			<artifactId>omnifaces</artifactId>
			<version>3.2</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>8.0</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces.themes</groupId>
			<artifactId>all-themes</artifactId>
			<version>1.0.10</version>
		</dependency>
		<dependency>
			<groupId>org.webjars.npm</groupId>
			<artifactId>primeflex</artifactId>
			<version>2.0.0</version>
		</dependency>
	</dependencies>

	<build>
		<finalName>PrimefacesTemplate</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.2</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
					<warName>PrimefacesTemplate</warName>
					<wtpContextName>PrimefacesTemplate</wtpContextName>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

FlexGrid / PrimeFlex

Flex Grid CSS is a lightweight flex based responsive layout utility optimized for mobile phones, tablets and desktops. Flex Grid CSS is not included in PrimeFaces as it is provided by PrimeFlex, a shared grid library between PrimeFaces, PrimeNG and PrimeReact projects.

Add dependency for Webjar, so we do not need to download and copy the files in our project:

		<dependency>
			<groupId>org.webjars.npm</groupId>
			<artifactId>primeflex</artifactId>
			<version>2.0.0</version>
		</dependency>

Import PrimeFlex in index.xhtml:

<h:head>
  <h:outputStylesheet name="webjars/primeflex/2.0.0/primeflex.css" />
</h:head>

For the usage of PrimeFlex please have a look into the documentation.

PrimeIcons

The usage of PrimeIcons is well documented.
Just import the stylesheet and use them, example:

<h:head>
  <h:outputStylesheet name="primeicons/primeicons.css" library="primefaces" />
</h:head>

<h:body>
  <i class="pi pi-check"></i>
  <i class="pi pi-times"></i>
</h:body>