Spass mit Websphere – Teil 2: DEBUG-Logs

Ich hatte heute morgen wieder Spass mit einer Besonderheit des Websphere Application Servers 7: In dem standardmässigen SystemOut.log werden nie DEBUG-Messages ausgebeben.

Der Websphere Application Server 7 leitet ALLE Log4j-Logmessages unterhalb von INFO in das trace.log (IBM/WebSphere/AppServer/profiles/AppSrv01/logs/server1/trace.log) um. Man kann in der eigenen log4j.xml konfigurieren, was man möchte (z.B. eigene File-Appender), es wird alles in das trace.log umgeleitet.

Das trace.log kann über die Admin-Konsole zumindestens konfiguriert werden:

Websphere Trace.log Konfiguration

jmap und jhat

[Letzte Woche von einem Kollegen gelernt]: Java bietet mit jmap und jhat zwei mächtige Tools, um den kompletten Heap eines Javaprozesses zu analysieren.

Mit “jmap -dump:file=mydump” wird ein Snapshot des gesamten Java Heaps erzeugt und in einem binären Format ins aktuelle Verzeichnis abgelegt. Dieser Dump ist das Ausgangsmaterial für jhat. Ein kleiner Hinweis für die Mac-Nutzer: Unter Mac OSX funktioniert dies leider nicht. Hier muss man den Dump mit der HotSpotDiagnosticMXBean innerhalb der JConsole erzeugen.

Mit “jhat mydump” wird ein HTTP-Server gestartet, der die im Dump enthaltenen Klassen und Instanzen präsentiert. Über die Abfragesprache OQL (Object Query Language) kann der Dump auch durchsucht werden. Beispielsweise werden mit “select s from java.lang.String s where s.count >= 500” alle Strings aufgelistet, die länger als 500 Zeichen sind.

Praktisch.

Frontend Caching mit Expire Headers

Eigentlich ist es ganz einfach. Aber es wird dennoch häufig vergessen. Die Rede ist vom “Expire” HTTP-Header. Er gibt dem Webbrowser zu erkennen, ab wann eine Resource “veraltet” ist. Typischerweise werden diese Header im Webserver konfiguriert (und nicht in der eigentlichen Applikation). Hier ein Beispiel aus einer Apache HTTP Konfiguration, die dafür sorgt, dass alle statische Komponenten nach einem Tag “veralten”.

<LocationMatch "^/static(.*)"> ExpiresActive On ExpiresDefault "access plus 1 day" </LocationMatch>

Allerdings besteht die Gefahr, dass Codeänderungen aufgrund des clientseitigen Caches beim Kunden erst sich nach einem Tag manifestieren. Eine Lösung ist, den Resourcen einen Timestamp des Deployments zu geben, so dass sie quasi endlos gecached werden können. Bei einem neuen Deployment erhalten die Resourcen einen neuen Timestamp. Mehr zu Frontend Best Practises gibts hier.

*Anmerkung am 07.10.2012*:
Anstelle des Expires-Header aus der HTTP/1.0+ Spezifikation, sollte besser der Cache-Control:max-age-Header aus der HTTP/1.1 Spezifikation verwendet werden, weil dieser eine relative Zeitangabe in Sekunden nutzt. Die absoluten Werte des Expires-Header setzen voraus, dass Server und Client die korrekte Uhrzeit verwenden, was nicht immer garantiert werden kann.

*Anmerkung am 08.03.2013*:
Hier noch ein weiteres Praxisbeispiel, in dem explizit Last-Modified/ETag Header abgeschaltet wird. Zugleich werden auch nur statische Resourcen mit dem Header versehen.
Wichtig ist noch der Hinweis, dass das mod_expires.so Modul aktiviert sein muss.

<LocationMatch "/etapp/.*\.(ico|pdf|jpe?g|png|gif|js|css)$">
	Header unset Last-Modified
	Header unset Date
	Header unset ETag
	ExpiresActive On
        ExpiresDefault "access plus 20 minutes"
</LocationMatch>

Script your Server!

In fast jedem Softwareprojekt wird die Applikation auf mehrere Server mit unterschiedlichen Konfigurationseinstellungen installiert und betrieben. Neben der Konfiguration für die Produktivumgebung müssen auch die Konfigurationen für Staging- und Entwicklungsserver verwaltet werden. Beispielsweise benötigt die Staging-Instanz andere Datenbankeinstellungen als die Produktivinstanz.

Somit kommen bei einem Projekt schnell mal 6 Konfigurationsversionen zusammen:

  • dienstleister-devlocal (lokale Entwicklungsinstanz)
  • dienstleister-dev (zentrale Entwicklungsinstanz)
  • dienstleister-sta (Staging System beim Dienstleister)
  • kunde-sta (Staging System beim Kunden)
  • kunde-productive1 (Produktiv-System – Cluster 1)
  • kunde-productive2 (Produktiv-System – Cluster 2)

Jede Konfigurationsversion setzt sich aus einer Vielzahl von Dateien zusammen, die sich noch in unterschiedlichen Pfaden befinden. Wenn zudem noch ein Produkt einsetzt wird, (z.B. Day Communiqué oder Hybris) kommt man leider ab und an in die Verlegenheit Dateien des Produktes (z.B. JavaScripts) zu überschreiben. Weiteres Beispiel ist die Installation von DLLs (z.B. bei Verwendung des SAP JCO Connectors).

Das Problem ist: Manuelles Pflege auf allen Servern ist langsam, fehleranfällig und nicht zuletzt unglaublich langweilig. Die Lösung ist recht simpel: Automatisieren, Automatisieren, Automatisieren, Automatisieren… Dabei trennt man das Projekt strickt von den eigentlichen Konfigurationsdateien. Diese Trennung spiegelt sich auch im Subversion wieder.

Beispiel Projekt im Subversion:
/project
/project/core/branches
/project/core/tags
/project/core/trunk

Beispiel Konfiguration im Subversion:
/project_configuration
/project_configuration/branches
/project_configuration/tags
/project_configuration/trunk
/project_configuration/trunk/scripts
/project_configuration/trunk/instance/dienstleister-devlocal
/project_configuration/trunk/instance/dienstleister-dev
/project_configuration/trunk/instance/dienstleister-sta
/project_configuration/trunk/instance/kunde-sta
/project_configuration/trunk/instance/kunde-live1
/project_configuration/trunk/instance/kunde-live2

Auf jedem Server wird /project/core und /project_configuration ausgecheckt. Aber wie kommen Konfiguration und Projekt zusammen ? Dies geschieht über ANT-Skripte, die aus einem ausgecheckten Projekt eine lauffähige, serverabhängige Installation generieren. Dabei werden die Dateien aus dem jeweiligen Konfigurationsverzeichnis (z.B. dienstleister-devlocal) in das Projektverzeichnis kopiert und ggf. vorhandene Dateien überschrieben.

Somit kann in wenigen Minuten eine komplette Instanz installiert werden. Weiter vereinfacht wird das Ganze, wenn .bat Dateien (oder Shellscripte) diese ANT-Skripte aufrufen.

Beispiel Skriptaufruf:
checkout-project-configuration.bat (Checkt das Konfigurationsprojekt aus dem Subversion)
checkout-project.bat (Checkt das Projekt aus dem Subversion aus)
configure-dienstleister-devlocal.bat (Konfiguriert das Projekt mit “dienstleister-devlocal” Konfiguration)

Fazit:
Durch das Automatisieren sämtlicher Konfigurations- und Installationsdateien werden Flüchtigkeitsfehler vermieden und das Aufsetzen von Serverinstallationen erheblich beschleunigt. Das Programmieren solcher Konfigurationsskripte kostet zwar zu Beginn des Projekts etwas Zeit, diese Investition wird aber doppelt und dreifach wieder amortisiert.

Active Directory Virtualisierung mit Penrose

Bei einem Projekt hatte ich die Problemstellung, dass Useraccounts aus zwei Active Directories in ein (javabasiertes) Shop-Produkt importiert werden sollen. Dieses Shop-Produkt wird zwar mit einer LDAP-Anbindung ausgeliefert, allerdings kann es nur Useraccounts aus genau einem Active Directory oder LDAP importieren.
Die Lösung: Ein LDAP-Proxy, der die beiden Active Directories zu einem virtuellen LDAP zusammenführt, und gleichzeitig LDAP-Binds erlaubt, um Single Sign On mit den Windows Credentials zu ermöglichen. Das (ebenfalls javabasierte) Open Source Produkt Penrose erfüllt genau diese Aufgabe.

Beispiel:
Active Directory 1 speichert die Kundenaccounts und besitzt folgenden Wurzelknoten:
dc=customer,dc=company,dc=com

Active Directory 2 speichert die Mitarbeiteraccounts und besitzt folgenden Wurzelknoten:
dc=employees,dc=company,dc=com

Im Penrose werden diese beiden Active Directory as LDAP Proxy Partitions importiert und die beiden Wurzelknoten in das Root DSE attribut eingetragen. Penrose simuliert keinen Active Directory, sondern lediglich einen “puren” LDAP. (Active Directory hat ein paar Spezialitäten, beispielsweise einen Bind mit der user@domain Notation). Dennoch werden Active Directory-spezifische Attribute wie memberOf übernommen. Man bekommt also einen LDAP mit Active Directory Schema.

Eine (wichtige) Kleinigkeit: Der Session Timeout in Penrose kann über die /conf/server.xml angepasst werden:

<session>
   <parameter>
      <param-name>maxIdleTime</param-name>
      <param-value>10080</param-value><!-- time out von 7 tagen -->
   </parameter>
</session>