Java: Upgrade log4j 1.2

Si usas log4j 1.2 y estas leyendo este articulo seguramente te planteas actualizar, vayamos al grano…

Escenario 1: codigo que usa directamente log4j 1.2, no puedes tocarlo y necesitas 100% retrocompatibilidad.

Si necesitas reemplazar sin tocar codigo o sin compilar (tienes un entorno antiguo del que no tienes acceso al codigo o es un proyecto que funciona bien y meterse a cambiar codigo es poco viable, por poner un par de ejemplos), existen opciones, una es reemplazar el log4j-1.2.17.jar por reload4j-1.2.20.jar, directamente en el filesystem o bien cambiando las dependencias en el pom.xml, haciendo algun exclude y haciendo el package de nuevo. Este escenario es 100% retro-compatible (reload4j es un fork/parches sobre el log4j original). Este es de los pocos metodos que usaria en un entorno productivo «legacy» sin opcion de pruebas.

<!-- Log4j 1.2 replacement -->
<dependency>
	<groupId>ch.qos.reload4j</groupId>
	<artifactId>reload4j</artifactId>
	<version>1.2.20</version>
</dependency>

Escenario 2: usas slf4j con el backend de log4j 1.2 y necesitas mantenerlo por algun motivo.

Si estabas usando slf4j-api y slf4j-log4j12, este es practicamente como el escenario anterior, con un par de matices, puedes usar reload4j pero te recomendaria actualizar a la ultima version de slf4j (1.7.36) y cambiar slf4j-log4j12 por slf4j-reload4j (aqui hay que vigilar las dependencias finales/excludes).

<!-- Log4j 1.2 replacement -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-reload4j</artifactId>
  <version>1.7.36</version>
</dependency>

Escenario 3: tienes log4j 1.2 pero quieres pasar a log4j 2.x con pocos cambios.

Otra opcion interesante, es mediante log4j-1.2-api, con esto emularas el log4j 1.2 pero usando el backend de log4j 2.x, no requiere tocar codigo, aunque tendras que cambiar el pom.xml (asegurarte de borrar las referencias o hacer los excludes de «log4j:log4j:1.2.17») y hacer el package, la magia corre de cuenta de log4j2. Este escenario diria que cubre el 80-90% de los casos de uso. Aqui no me la jugaria sin un entorno de pruebas.

<!-- Log4j 1.x bridge -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-1.2-api</artifactId>
  <version>2.17.2</version>
</dependency>

Hay otros escenarios / combinaciones, pero diria que la mayoria rondaran slf4j, reload4j o log4j2.

Mi recomendación es que si no necesitas ninguna funcionalidad especifica del framework de logging (como por ejemplo NDC), uses slf4j-api, por debajo ya es cuestion de gustos (mi preferencia siempre será el más sencillo o en su defecto, log4j*).

Referencias:
https://reload4j.qos.ch/
https://reload4j.qos.ch/manual.html
https://www.slf4j.org/legacy.html
https://logging.apache.org/log4j/2.x/manual/migration.html
https://logging.apache.org/log4j/1.2/
https://technobcn.wordpress.com/2013/02/04/java-logs-de-aplicaciones-la-senda-del-guerrero/

Java: Browser using Eclipse SWT and Edge WebView2

Mostrar contenido web (HTML, CSS y JavaScript) en una aplicación Java con Microsoft Edge WebView2.

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

// ...

final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("Edge Browser in SWT");
shell.setLayout(new FillLayout());

Browser browser = new Browser(shell, SWT.EDGE);
System.out.println("Browser: " + browser.getBrowserType()); // edge
browser.setUrl("https://www.google.com/");

shell.pack();
shell.setMaximized(true);
shell.open();

while (!shell.isDisposed()) {
	if (!display.readAndDispatch())
		display.sleep();
}
display.dispose();

Aqui las dependencias de Maven:

<dependency>
	<groupId>org.eclipse.platform</groupId>
	<artifactId>org.eclipse.swt</artifactId>
	<version>3.118.0</version>
</dependency>
<dependency>
	<groupId>org.eclipse.platform</groupId>
	<artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId>
	<version>3.118.0</version>
</dependency>

Para que funcione, vas a necesitar el «Microsoft Edge WebView2», lo puedes descargar de la web:

https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section

La opcion que yo he usado es: «Evergreen Standalone Installer (x64)«

Referencias:
https://docs.microsoft.com/microsoft-edge/webview2/
https://www.eclipse.org/swt/snippets/#browser
https://www.eclipse.org/windowbuilder/

Linux: SquashFS benchmark

Comando para crear cada Squash:

mksquashfs /datadir/ /tmp/test.${ALGORITMO}.fs -noappend -comp ${ALGORITMO}

Comando para extraer el Squash:

unsquashfs -f -da 64 -fr 64 -d /tmp/test.${ALGORITMO}/ /tmp/test.${ALGORITMO}.fs

Las cifras:

ALGORITMO Compresion (segs) Descompresion (segs) bytes-comprimido ratio-compresion
xz 131 13 262.070.272 ! 1:3.6
gzip 63 * 6 * 308.690.944 * 1:3
lzo 92 4 ! 345.141.248 1:2.7
lz4 3 ! 6 * 439.746.560 1:2.1
  • RAW bytes original: 930.666.103
  • Leyenda: ! mejor valor; * segundo mejor valor
  • Resumen: rapido=lz4, balance=gzip, tamaño=xz

Referencias:
man 1 mksquashfs
man 1 unsquashfs

Linux: netplan, blackhole and unreachable routes

Ejemplo de rutas blackhole y unreachable en Netplan

network:
  version: 2
  renderer: networkd
  ethernets:
    lo:
      match:
        name: lo
      addresses: [ 127.0.0.2/32 ]
      routes:
        - to: 10.0.0.0/8
          via: 0.0.0.0
          metric: 240
          type: unreachable
        - to: 192.168.0.0/16
          via: 0.0.0.0
          metric: 240
          type: unreachable
        - to: 172.16.0.0/12
          via: 0.0.0.0
          metric: 240
          type: unreachable
        - to: 169.254.0.0/16
          via: 0.0.0.0
          metric: 240
          type: blackhole

Pista: sin el «address 127.0.0.2» o el «via 0.0.0.0» netplan silenciosamente ignora la configuracion.

Referencias:
Netplan design
Netplan reference
Netplan examples
man netplan

Linux: systemd basics

Comandos basicos:

## Reload systemd configuration
systemctl daemon-reload
## Enable/Disable service
systemctl <enable|disable> XXX.service
## Star/Stop/Reload/Status of Service:
systemctl <start|stop|reload|status> XXX.service
## Get Service logs
journalctl -u XXX.service
## Tail log Services
journalctl -f
## Check if service is running (scripting)
systemctl -q is-active XXX.service && echo RUNNING || echo DEAD

Ejemplo de un tunel SSH con respawn:

# systemd: /etc/systemd/system/ssh-tunnel.service
[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Type=simple
StartLimitInterval=0
Restart=always
RestartSec=3
User=root
ExecStart=-/usr/bin/ssh -2 -C -N -L 8080:127.0.0.1:8080 -i /root/.ssh/id_rsa \
                        -oStrictHostKeyChecking=no -oHashKnownHosts=no \
                        -oServerAliveInterval=30 -oServerAliveCountMax=3 \
                        -oTCPKeepAlive=yes -oExitOnForwardFailure=yes \
                        -p 22 user@host.acme.org

[Install]
WantedBy=multi-user.target

Ejemplo de un servicio dummy TCP:

# systemd: /etc/systemd/system/dummy.socket
[Unit]
Description=Dummy TCP Socket

[Socket]
ListenStream=1234
Accept=yes

[Install]
WantedBy=sockets.target
# systemd: /etc/systemd/system/dummy@.service
[Unit]
Description=Dummy TCP Server
Requires=dummy.socket

[Service]
Type=simple
ExecStart=-/bin/echo OK %H
StandardInput=socket
StandardOutput=socket

[Install]
WantedBy=multi-user.target

Referencias:
understanding systemd units
systemd inetd
man systemd.unit
man systemd.service
man systemd.socket

Java: Getting Memory Usage with MemoryMXBean

Estoy madurando una idea y me he encontrado con una de las muchas perlas de JMX de Java, la MemoryMXBean:

MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
System.out.println("HeapUsage: " + mbean.getHeapMemoryUsage());
System.out.println("NonHeapUsage: " + mbean.getNonHeapMemoryUsage());

El resultado:

HeapUsage: init = 16777216(16384K) used = 7772720(7590K) 
           committed = 16252928(15872K) max = 1037959168(1013632K)

NonHeapUsage: init = 2359296(2304K) used = 15099032(14745K) 
              committed = 15327232(14968K) max = -1(-1K)

Referencias:
java.lang.management.ManagementFactory

Amazon: Protect Instance Metadata and User Data

Es una practica extendida almacenar las credenciales en el «user-data», el problema es que desde una instancia de EC2 cualquier usuario de la maquina, autorizado o no, puede acceder a estos datos.

Ejemplo:

curl http://169.254.169.254/latest/user-data

Una regla de iptables para evitar estos accesos, salvo para el usuario «root» (uid=0):

iptables -A OUTPUT \
  -d 169.254.169.254/32 -p tcp --dport 80 \
  -m owner ! --uid-owner 0 \
  -j REJECT --reject-with tcp-reset

References:
EC2 – Instance Metadata and User Data
The best method for passing AWS credentials as user data to an EC2 instance?

Disponibilidad / Availability

Una pequeña chuleta con los porcentajes de disponibilidad y el tiempo de caida asociado:

Availability	Downtime
---------------------------------------------------
97% 			10d 22h 48m
98% 			7d 7h 12m
99% 			3d 15h 36m		***
99,5%			1d 19h 48m
99,9% 			8h 45m 36s		***
99,95%			4h 22m 48s
99,96%			3h 30m 14s
99,97%			2h 37m 41s
99,98%			1h 45m 7s
99,99%			52m 34s			***
99,999% 		5m 15s			***
99,9999%	 	32s				***
99,99999%		3s				***

Amazon: Simple WhatIsMyIP with Lambda and API-Gateway

Un ejemplo de como montar un sencillo WhatIsMyIP usando Amazon Lambda con el Backend de NodeJS y API-Gateway.

El código del Lambda (Runtime: NodeJS, Handler: index.handler, Role: Basic Execution Role):

exports.handler = function(event, context) {
    context.succeed({ "ip": event.ip });
};

Aunque para que sea funcional tienes que publicarlo a través de API-Gateway. Crear un resource, por ejemplo «text» y un metodo (GET). Tambien hacen falta algunas configuraciones de integración:

Integration Request (Content-Type: application/json, Mapping Template):

{
"ip" : "$context.identity.sourceIp"
}

Integration Response (Status 200, Content-Type: text/plain, Mapping Template):

#set($inputRoot = $input.path('$'))
$inputRoot.ip

Method Response (Status 200, Content-Type: text/plain, Model: Empty).

Si prefieres respuestas json en realidad es más sencillo aún, podrías ahorrarte la customización de la parte del Integration Response y el Method Response (Por defecto los mime-types ya son application/json y el Integration es Output passthrough).

Solo tienes que publicarla en un stage («prod» por ejemplo) y al invocar la URL con curl tendrás algo como esto:

# curl -sL https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/prod/text
219.12.34.56

NOTA-1: Si clonas un API, es posible que deje de funcionar (bug?) y tendrás que ir al «Integration Request» y volver a asignar la función, aunque aparezca la correcta, para que le asigne los permisos de ejecución.
NOTA-2: Si vas a usar Lambdas o API-Gateway para datos «binarios» (todo lo que no sea texto, de momento no está soportado), verás tus datos corromperse.

Referencias:
API-Gateway Developer Guide
API-Gateway Developer Guide: Mapping Template Reference
Lambda Developer Guide

PPP: Connection broken

Hoy voy a contar una historia de terror informático, de esas que recuerdan el día en que a Enjuto Mojamuto se le fue Internet.

El Jueves 10, víspera de festivo en Barcelona, llegué a casa del trabajo… Internet no funcionaba (conexion de Fibra/FTTH -Movistar-). Por suerte con el Tethering de Android tenía un acceso alternativo desde el portatil mientras solventaba el problema (aunque andar cambiando de la LAN al 3G era bastante tedioso).

Como buen «friki» miré mi Uptime Robot; la conexión se había caido a las 12:20PM (media mañana). Ese día hubo tormenta, asi que temía que se me hubiera quemado el router, hubiera caido un arbol y roto las lineas, una excabadora, o vete a saber…
Leer más de esta entrada