HOAB

History of a bug

IE11 et cache-control rules

Rédigé par gorki Aucun commentaire

Problem :

I had a problem, nearly simple : on an angular 11 application, font was not displayed on IE11 on customer site.

After a few tests :

  • I reproduce it on my demo site
  • I do not reproduce in dev mode

Solution :

Well, I search for a while, and here is a summary.

  1. IE11 has a problem if header "Cache-Control: nostore" is set for fonts... Not easy to find but there is some reference here
  2. On my dev, it's working, after a check, I'm not setting Cache-Control: nostore
  3. On my demo site, I have this header, but I have a Nginx as a reverse proxy. Ok, that's why
  4. On customer site, I do not have control on network elements. One must add it also

I made the assumption that if I set a Cache directive, intermediate server won't modify it.

It works on my demo site. I'll check next on my customer site.

To add cache control on springboot, thanks Baeldung !

        registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/static/hpa-portal/assets/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS));

 

 

Back to basics : hashcode and hashmap

Rédigé par gorki Aucun commentaire

Problem :

I'm building a Java agent to monitor objects. I track some objects and get metrics ont it. But to have less impact on memory, if objects are removed, I don't want to keep a reference to them. It could prevent the garbage collector to remove them.

Objects can be scanned multiple time for some reasons, so to keep a unique reference, I built an hashmap to track only one reference of these objects.

A hashmap with weak reference.

As stated by this article, weak reference can not be compared regarding their reference but only between WeakReference objects themselves. So I used the proposal of this article and create a WeakEqualReference object.

Adding is working well.

But after a while, I can not remove an object : I go through the map, I tried to remove an object given by the entrySet iterator : impossible.

Solution :

Well, I search for a while, and here is a summary.

In the article, hashcode used for WeakEqualReference is :

	@Override
	public int hashCode() {
		T value = this.get();
		return value != null ? value.hashCode() : super.hashCode();
	}

In fact, it gives :

  1. on the first call : the hashcode of the value
  2. on the second call, after value has been removed : the hashcode of the WeakEqualsReference

But when object is stored in the hashmap, this is the first hashcode which is used. And the hashmap never find the new hashcode after the value becomes null. Containskey, remove, all is wrong.

Hashcode must be consistent for the duration of the WeakEqualsReference.

So, my solution is to compute the hascode on the first call, when the object is stored in the hashmap, return it each times after that. WeakEqualsReference can now be removed safely.

import java.lang.ref.WeakReference;

public class WeakEqualReference<T> extends WeakReference<T> {

	private int hashCode = 0;

	public WeakEqualReference(T r) {
		super(r);
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean equals(Object other) {

		boolean returnValue = super.equals(other);

		// If we're not equal, then check equality using referenced objects
		if (!returnValue && (other instanceof WeakEqualReference<?>)) {
			T value = this.get();
			if (null != value) {
				T otherValue = ((WeakEqualReference<T>) other).get();

				// The delegate equals should handle otherValue == null
				returnValue = value.equals(otherValue);
			}
		}

		return returnValue;
	}

	@Override
	public int hashCode() {
		if (hashCode == 0) {
			T value = this.get();
			hashCode = value != null ? value.hashCode() : super.hashCode();
		}
		return hashCode;
	}
}

And for the story :

- one of my values referenced was a bean with a String and another instance of WeakEqualsReference. I still had issue, but due to the String ! This string value was updated so hashcode before and after was different. Same cause, same effects.

Here the example was complicated by the WeakReference usage, but with a simple HashSet :


import java.util.HashSet;
import java.util.Objects;

public class HashCodeTest {

    public static class HashTestValue {
        public String value;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            HashTestValue that = (HashTestValue) o;
            return Objects.equals(value, that.value);
        }

        @Override
        public int hashCode() {
            return Objects.hash(value);
        }
    }


    public static void main(String... args) {

        HashSet<HashTestValue> set = new HashSet();

        HashTestValue mapTestValue = new HashTestValue();
        mapTestValue.value = "1234";
        set.add(mapTestValue);

        System.out.println("set.contains(mapTestValue) = " + set.contains(mapTestValue));

        mapTestValue.value = "5678";

        System.out.println("set.contains(mapTestValue) = " + set.contains(mapTestValue));

    }
}

Conclusion : overriding equals and hascode is OK, but be careful of the consistency if you need to test the object after the insert !

 

Springboot & listening address

Rédigé par gorki Aucun commentaire

Problème :

J'ai une configuration un peu particulière :

  • je développe et teste majoritairement sous Linux, Ubuntu
  • pour certains cas, je lance une VM Windows 10 sous virtualbox pour tester IE/Edge

Je lance comme d'habitude mon application, j'essaie de m'y connecter depuis la VM impossible...

Configuration de la VM :
- en NAT
- en virtual network

Solution :

Evidemment je pense au paramètre `server.address` de Spring boot car j'ai un log qui m'affiche : Listening on 127.0.0.1:8080
Mais ça ne marche pas...

Je tente X configurations différentes : dans le fichier de configuration, en paramètre avec le -Dserver.address

Je tente avec le port forwarding NAT de Virtualbox. Nada.

La connexion SSH fonctionne : je peux me connecter depuis la VM

Je vérifie le firewall, pas activé.

Jé vérifie IPTables, ça me semble OK

Le netstat a du mal à sortir les LISTEN, j'utilise : lsof -i -P -n | grep LISTEN

Et là :

1) Springboot écoute bien par défaut sur toutes les IPv4, comme décrit a plein d'endroit, le server.address fonctionne très bien
2) le log affiché était applicatif et codé en dur : argh :(
3) c'était le iptable qui bloquait...  il fallait regarder le FORWARD et pas le INPUT... pas sur d'avoir compris pourquoi, a priori on ne change pas de réseau/machine. A creuser. Commande iptable pour ouvrir le port.

 

 

 

Springboot + Maven + Junit5

Rédigé par gorki Aucun commentaire

Problème :

Migration de springboot vers  la 2.1.1 et Junit5... et plus rien ne marche, tests introuvables.

Après de multiples tests, ça marchait dans Intellij Idea, mais pas sous maven (MockBean retournait null....)

Solution :

J'ai fait beaucoup d'essais :

  • ajouter junit-platform-commons comme vu ici
  • j'ai suivi les guides de Mkyong Springboot + Maven + Junit5
  • j'ai exclu Junit4 de surefire, ajouter plateform-commons à surefire...

Rien n'y faisait :

  • soit pas de tests
  • soit NullPointerException sur les MockBeans
  • soit "Caused by: java.lang.ClassNotFoundException: org.junit.platform.engine.support.discovery.SelectorResolver"

Au final :

  1. Faire migrer les tests junit vers junit5
    1. Corriger les API
      import org.junit.jupiter.api.Assertions;
      import org.junit.jupiter.api.Test;
    2. Remplacer le SpringRunner
      @ExtendWith({SpringExtension.class})
  2. Simplifier le POM (vu ici : https://github.com/junit-team/junit5/issues/1773)
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter</artifactId>
			<version>${test.junit.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<version>${test.junit.version}</version>
			<scope>test</scope>
		</dependency>
<!--
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<version>${test.junit.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.platform</groupId>
			<artifactId>junit-platform-commons</artifactId>
			<version>${test.junit.platform}</version>
			<scope>test</scope>
		</dependency>-->

Et bien sur :

		<!-- TEST -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>junit</groupId>
					<artifactId>junit</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

Et enfin :

			<!-- Testing -->
			<plugin>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.22.2</version>
			</plugin>

 

SystemD and tomcat hang on startup

Rédigé par gorki Aucun commentaire

Problem :

I used robertdebock/ansible-role-tomcat to install a Tomcat instance using Ansible. Works well until I deploy an application on it. Then java process hangs with 100% system CPU.

Starting with tomcat users without system work correctly.

Solution :

I suspected :

  • SELinux
  • Linux limits
  • VM slow I/O

But after a while I ran strace :

  • by modifying systemd configuration
  • by modifying catalina.sh configuration

All I have was a simple FUTEX wait...

And then I read the manual, as simple as :

strace -f -e trace=all -p <PID>

No need to trace from startup and by default, not all is traced...

After that, easy way, the process was reading recursively :

/proc/self/task/81569/cwd/proc/self/task/81569/cwd/proc/self/task/81569/cwd/proc/self/task/81569/cwd/proc/self/task/81569/cwd/proc/self/task/8156...

Just fixing the working_directory in the ansible role, and all is working.

Issue reported here.

 

Fil RSS des articles de cette catégorie