Archiv der Kategorie Empfehlungen

Migration von Trac zu JIRA

Einführung von Atlassians Confluence, FishEye und JIRA

In diesem Artikel geht es um die Installation und Konfiguration von Atlassians JIRA, FIshEye und Confluence in einer ursprünglich rein Trac-basierten Umgebung.

Ich gehe davon aus, dass die Hersteller-Guides bereits bekannt sind oder bei Fragen zu Rate gezogen werden. Es werden nicht alle Funktionen und Features der Produkte im einzelnen vorgestellt oder erklärt (etwa: Was ist ein Crowd-Server? oder Wie stelle ich den Benutzer-Signup aus?).

  • http://confluence.atlassian.com/display/JIRA/JIRA+Documentation
  • http://confluence.atlassian.com/display/FISHEYE/FishEye+Documentation+Home
  • http://confluence.atlassian.com/display/DOC/Confluence+Documentation+Home

Installationen

Für eine saubere Installation empfiehlt es sich, die Anwendungen von einander getrennt zu installieren.

Alle drei Produkte lassen sich als Standalone oder als WAR herunterladen und installieren. Während die WAR -Variante nur in einen vorhandenen Tomcat deployt wird, ist die Standalone-Variante eine fertig konfigurierte Einheit, die auch entsprechend korrekt konfiguriert ist. Aus mehreren Gründen ist die Standalone-Variante vorzuziehen: Service-Able, besseres Tuning im Performance-Problemfall.

Leider ist die Qualität der Standalone-Versionen nicht überall gleich; JIRA ist dabei am Besten und quasi vollständig.

Für diesen Beitrag installieren wir die Produkte jeweils in die drei Verzeichnisse /opt/confluence, /opt/fisheye und /opt/jira. Und natürlich unter Linux.

JIRA

Praktischerweise wird JIRA in einem selbstausführenden Installer zur Verfügung gestellt. Also sudo gestartet, sollte man hier den empfohlenen Weg nehmen und JIRA als Service mit eigenem Benutzer installieren. Da der Installer selbstständig einen Benutzer jira anlegen wird, sollte dieser nicht vorher schon existieren. Ist bereits einer vorhanden, legt der Installer jira1 an, inkl. einem gleichlautenden Service.

Danach ist JIRA installiert und muss via Web konfiguriert werden. Für die produktive Installation bedeutet dies beispielsweise eine externe Datenbankanbindung.

Hinweis: Sofern der Administratorzugang mit einem Benutzer aus dem späteren Trac-Import matchen soll, sollte der Benutzername gleich heißen.

Hinweis: Sollen mehrere Tomcats auf einer Maschine laufen, müssen die Ports entsprechend verändert werden. Die JIRA-Tomcat-Konfiguration ist unter conf/server.xml zu finden. Dort findet sich auch ein auskommentierter Connector für AJP.

Es empfiehlt sich, den Remote- und API-Zugriff sofort zu aktivieren, da dieser in der Regel immer gebraucht wird (etwa für Eclipse Mylyn).

Falls die Benutzerkonten zwischen JIRA, Confluence und FishEye synchronisiert werden sollen (und das ist sicher zu empfehlen): Das Feature heißt bei Atlassian Crowd und ist in allen Produkten implementiert. Hierbei spielt JIRA dann den Crowd-Server, d.h. es stellt die Konten und Passwörter zur Verfügung. Confluence und FishEye werden später nur passiv gespiegelt.

FishEye

FishEye wird im Gegensatz zu JIRA nicht mit einem Installer ausgeliefert. Das Archiv entpackt hinterlässt nur die Applikation. Da der Standardport von FishEye 8060 ist und sich damit selten mit anderen Diensten überschneidet, kann man dies so stehen lassen.

Da FishEye von sich aus kein Service-Script mitliefert, muss man das selber beisteuern. Außerdem muss man sich auch selber um einen Benutzer (etwa sinnigerweise fisheye) kümmern.

FishEye wird per Default in eine HSQLDB installiert. Im Gegensatz zu JIRA kann man nicht bereits zu Beginn eine alternative Datenbank auswählen. Dafür kann Fisheye jedoch on the fly via Administration/Database in eine externe Datenbank migriert werden.

Falls die Crowd-Funktionalität von JIRA verwendet werden soll, muss in der Administration der JIRA-Server als “Application-Link” angelegt werden. Anschließend kann man die Benutzer synchronisieren (etwa automatisch jede Stunde) und das lokale Registrieren abschalten.

Hinweis: Mit dem FishEye-Administrator-Passwort kommt man immer in die Admin-Backend zurück. Dennoch empfehle ich, einen Login in einem separaten Browser (oder einfach im Inkognito-Modus) auszuprobieren. Per Default werden Benutzer, die in der Gruppe jira-administrators sind, auch automatisch FishEye-Administratoren.

Erst nach Abschluss dieser Feinheiten lohnt sich das Anlegen von Repositories.

Hinweis: Lässt man die alte Trac-Installation noch weiter laufen, so kann man in den Eigenschaften eines FishEye-Repositories einen Linker definieren, damit in Commits erscheinenden Ticket-Referenzen (#123) auf Trac verweisen können (reguläres Muster: #(\d+)). Prinzipiell kann man auch einen Link auf FishEye selber erzeugen, das funktioniert natürlich nur, wenn keine Tickets aufgrund von Projekt-Splittung verschoben wurden.

Nach erfolgtem Anlegen und Indizieren des Repositorys (oder der Repositories) sollte der Übersichtlichkeit wegen Projekte anlegen — idealerweise in der gleichen Struktur wie unter JIRA. Anschließend kann man mit den Application-Links dafür sorgen, dass erstens FishEye weißt, dass ein ein bestimmtes Projekt einer JIRA-Installation in den Changeset-Kommentaren verlinkbar ist und zweitens der entsprechenden JIRA-Installation eine Backreferenz auf diese FishEye-Quelle geben.

Mit dieser Konfiguration werden Commits mit einem Verweis auf ein Ticket (“… PROJECTKEY-123…”) automatisch auf JIRA verlinkt und gleichzeitig im JIRA-Vorgang als Aktivität aufgelistet.

Confluence

Wie FishEye bietet auch Confluence nur ein entpacktes Verzeichnis an. Auch hier muss man sich um ein Service-Script und einen Benutzer selber kümmern.

Auch hier sollte man nach erfolgter Installation die Authentifizerung auf JIRA umstellen. Dafür muss man eine Referenz auf JIRA anlegen. Eventuell ist es auch nötig, eine zusätzliche Referenz seitens JIRA auf Confluence (erlaubte Abfrage der Konten) anzulegen.

Wichtiger Hinweis: Es ist dringendst zu empfehlen, vor dem Switch auf den JIRA-Crowd-Server dem eigenen Konto (in JIRA) die neu zu erstellenden Benutzergruppe confluence-administrators hinzuzufügen — ansonsten sperrt man sich sofort aus. Hat man den Abgleichtimer auf eine Stunde gestellt und will – für Confluence – nicht von vorne anfangen, ist man eine Stunde merkbefreit.

Von Trac nach JIRA

Upgrade auf Trac 0.12

Das Importwerkzeug von JIRA ist nur mit Trac-Export-Daten kompatibel, die aus einer 0.12-Installation heraus erstellt wurden.

Die vorhandene Trac-Installation lief leider noch in der Vorgängerversion 0.11. Das macht sich unter anderem daran bemerkbar, das zwar der Import prinzipiell funktioniert, aber beispielsweise alle Zeiten/Zeitstempel falsch sind (01. Januar 1970). Dieser Umstand wird durch eine Änderung der internen Speicherung von Timestamps in Trac geschuldet (0.11 speichert in Sekunden, 0.12 jedoch in Mikrosekunden).

Daher war es zunächst erforderlich, die vorhandene Trac-Installation mittels Upgrade auf 0.12 zu aktualisieren. Je nach Installation ist ein Upgrade einfach oder umständlich.

Hinweis: Es kann durchaus sinnvoll sein, dass man seine bisherige Trac-Installation unverändert lassen will. Dies ist relativ einfach zu bewerkstelligen. Auf einer separaten Maschine (dafür eignet sich auch ein schnell aufgezogenes Debian in der VM) wird Trac 0.12 etwa via apt-get installiert. Danach wird ein Trac-Repository konfiguriert und anschließend das existierende Repository 1:1 herüberkopiert. Die anschließenden Befehle trac-admin upgrade und trac-admin wiki upgrade hiefen sowohl die Trac-Datenbank als auch die Wikiseiten auf die neue Version – fertig!

Export von Trac

Die Trac-Daten werden wie folgt exportiert: Die Tickets liegen alle in einer Datenbank im Repository, die Attachments liegen als Dateistruktur ebenfalls im Repository. Man wechselt in das Verzeichnis vom Trac-Repository und packt alles zusammen in ein Zip-Archiv.

Import in JIRA

Im Import-Assistenten wählt man Trac und danach das zuvor erstellte Zip-Archiv. Je nach Größe kann es erforderlich sein, dass man in der JIRA-Administration das Upload-Limit für Dateien (kurzfristig) erhöhen muss. Der Assistent entpackt das Archiv und analysiert die Strukturen. Sofern man keine Besonderheiten hat, kann man die Schritte alle absegnen. Eventuelle CustomFields sollten ebenfalls als solche auch in JIRA eingefügt werden.

Das “No-Priority”-Issue

Beim Import der Ticket-Daten wurde das Feld Priority nicht korrekt übernommen. Das hat die Auswirkung, dass beim Editieren eines Tickets automatisch die erstbeste Priorität seitens JIRA verwendet wird. Und das ist 1 => Blocker. Etwas unschön.

Prinzipiell ist so etwas aber kein großer Aufwand, da JIRA ein sehr ausgeklügeltes und vor allem funktionierendes Mehrfachbearbeitungsmanagement mitliefert. Über “Vorgänge / Suchen / Erweitert” kann man mittes JQL eine sehr spezifische Abfrage nach Vorgängen machen. Über jede getätigte Abfrage lässt sich über den Button “Tools” in der rechten, oberen Ecke eine Mehrfachbearbeitung starten.

Hinweis: Da der Name des Trac-Felds “Priority” mit dem JIRA-Feld “Priority” konkurriert, hat der Importer ein generisches Feld (cf-xxxx) anlegt.

Über die Abfrage “status = Reopened and priority is empty and cf[xxxxx] = Blocker” lassen sich beispielsweise alle “prioritätslosen” Tickets abfragen, die aus Trac mit Blocker-Priorität kommen”. Über die Mehrfachbearbeitung setzt man dann die Priorität auf “Blocker”. Das gleiche für alle anderen Prioritäten.

Das “Reopened Issues”-Issue

Beim Import der Ticket-Daten wurden alle bereits geschlossenen und “resolved” Tickets mit dem Status “Reopened” eingeladen. Auch hierfür eignet sich die Mehrfachbearbeitung.

Hinweis: Der Workflow erfordert beim Schließen eines Vorgangs die Angabe einer Resolution. Daher ist es wie bei “No-Priority” empfehlenswert, die Abfrage gruppiert nach eine Trac-Resolution zu machen. Ansonsten verliert man die originale Resolution.

Von Trac nach Confluence

Export von Trac

Die Daten der Wiki-Seiten liegen als Dateistruktur im Trac-Repository. Da der Export bereits unter “Von Trac nach JIRA” erfolgt ist, hat man die Wiki-Daten bereits vorliegen.

Import in Confluence

Für den Import der Daten steht ein universelles Java-Programm zur Verfügung (ist in der Confluence-Administration unter Import zu finden). Damit lassen sich die Trac-Daten (aber auch noch eine ganze Reihe anderer Formate/Anwendungen) in das Confluence-Schema importieren. Das Programm ist etwas unglücklich designed und die Steuerung ist an einigen Punkten etwas unsauber implementiert — sofern man jedoch einiges beachtet, funktioniert alles einwandfrei.

Zunächst wählt man in der Liste “trac” aus. Danach wählt man den Ordner der Attachments aus, was voraussetzt, das man das Trac-Repository auf dem Rechner zur Verfügung stehen hat, auf welchem der Importer ausgeführt wird. Anschließend wählt man die Wiki-Seiten aus. Hierbei ist anzumerken, dass der Importer den kompletten, absoluten Pfad der Dateien übernimmt! Das ist äußerst unschön, weil man zwar in Confluence später zwar einzelne, aber nicht mehrere Seiten gleichzeitig verschieben kann. Unter Umständen kann es sinnvoll sein, die Wiki-Seiten für diesen Import kurzfristig auf einem /-Level anzulegen. Ein Unding ohnegleichens.

Nach der Angabe der Confluence-Server-Angaben werden die Daten importiert. Hinweis: Die externe API muss in Confluence selbstverständlich aktiviert sein.

Bonus

Hudson/Jenkins-Integration

Zwar bietet Atlassian natürlich eine perfekte Integration in und mit ihrem eigenen Buildserver Bamboo an, dies ist aber nicht zwingend erforderlich. In der Regel reicht es aus, wenn man im Ticket automatisch/sofort erkennen kann, mit welchem Build ein Ticket bzw. ein entsprechender Changeset aufgenommen wurde. Dafür gibt es das Jenkins JIRA-Plugin.

Zuallererst muss in der globalen Konfiguration von Jenkins die JIRA-Installation konfiguriert werden. In den meisten Fällen muss dafür ein Benutzer angegeben werden: Will man es schön und sauber, dann legt man unter JIRA einen eigenen Buildserver-Benutzer an (etwa mit schönem Avatar?) und trägt dessen Zugangsdaten (generisches, langes Passwort?) in der Jenkins-Konfiguration ein.

Danach kann man im entsprechenden Jenkins-Job das JIRA-Plugin aktivieren und weiteres Tuning machen.

Mit dieser Konfiguration wird ein Build als weitere Aktivität in einem JIRA-Vorgang auftauchen.

Eine Jar (OJDBC) nachträglich in die Ziel-Jar integrieren

Für das visualDependencies-Projekt stand ich eben vor dem Problem, dass zur Laufzeit ein Oracle-JDBC-Treiber benötigt wird. Leider kann man ihn jedoch nicht als Abhängigkeit konfigurieren, da zum einen das Projekt unter der GPL läuft, und zum anderen es überhaupt keinen offiziellen Weg dafür gibt. Für den eigenen Buildprozess kann man selbstverständlich ein lokales Repository nutzen, das Artefakt lokal deployen bzw. den Buildserver entsprechend konfigurieren.

Da jedoch das Projekt im Form des Sourcecodes “für alle” erreichbar sein soll, fallen diese Optionen zumindest für diese Situationen weg. Es muss also eine Möglichkeit geben, den OJDBC-Treiber nachträglich in das fertige Applikations-Jar zu integrieren. Und ja, das ist eigentlich sehr trivial.

Im folgenden Ant-Script werden sowohl die Applikations-Jar (visualDependencies.one-jar.jar) als auch der JDBC-Treiber (ojdbc14.jar) in der Konfiguration vorbelegt. Selbstverständlich kann man alle Properties überschreiben bzw. das Script entsprechend anpassen.

Die wichtigen Teile sind: Das Ant-Jar-Command muss den Zusatz “update” erhalten, denn die Ziel-Jar soll nicht ersetzt werden. Außerdem muss ein Fileset mit einer Verzeichnisstruktur angegeben werden, da die Treiber-Jar innerhalb der Ziel-Jar im Verzeichnis lib liegen muss (Struktur einer One-Jar). Das war’s.

Hinweis: Das Ant-File ist für den Gebrauch in einer Maven-Umgebung konfiguriert (basedir=../../../ entspricht dem Root-Verzeichnis bei der Annahme, dass Script unter src/main/scripts/ liegt).

<?xml version="1.0" encoding="UTF-8"?>
<project name="visualDependencies" default="help" basedir="../../../">
	<!-- The path of the actual artifact (project name), without the file suffix. -->
	<property name="project.name" value="visualDependencies.one-jar" />
	<!-- The path of the ojdbc driver, without the file suffix. -->
	<property name="ojdbc.name" value="ojdbc14" />
	<!-- DO NOT CHANGE THIS LINES UNLESS YOU KNOW WHAT YOU DO! -->
	<property name="project.jar" value="${project.name}.jar" />
	<property name="ojdbc.jar" value="${ojdbc.name}.jar" />
	<!-- Set actual paths of artifacts. -->
	<property name="application.path" location="${basedir}/target/${project.jar}" />
	<property name="ojdbc.parent.path" location="${basedir}/oracle" />
	<property name="ojdbc.path" location="${ojdbc.parent.path}/lib/${ojdbc.jar}" />
	<!-- Shows help (default target) -->
	<target name="help">
		<echo message="See http://www.knallisworld.de/blog/2010/11/23/eine-jar-ojdbc-nachtraglich-in-die-ziel-jar-integrieren/" />
		<echo message="Usage: attachOJDBC" />
	</target>
	<!-- Checks if the artifacts are available. Throws exceptions if they not exist. -->
	<target name="checkDependencies">
		<available file="${application.path}" property="application.exists" />
		<available file="${ojdbc.path}" property="ojdbc.exists" />
		<fail unless="application.exists" message="The application file (${application.path}) does not exist." />
		<fail unless="ojdbc.exists" message="The ojdbc file (${ojdbc.path}) does not exist." />
	</target>
	<!-- Integrates all files of ojdbc.parent.path into the target jar. -->
	<target name="attachOJDBC" depends="checkDependencies">
		<jar update="true" destfile="${application.path}">
			<fileset dir="${ojdbc.parent.path}" />
		</jar>
	</target>
</project>

ClickToPlugin

Seit ein paar Wochen nutze ich jetzt bereits die Safari Extension ClickToPlugin. Dabei handelt es sich um eine erweiterte Variante der Extension ClickToFlash des gleichen Entwicklers.

Während die Safari-Extension ClickToFlash nur auf das Blocken von Flash abzielt, geht ClickToPlugin weiter und blockiert auch andere Plugins (daher auch der Name): Silverlight, Java, usw. Es wäre richtig zu sagen: ClickToPlugin beinhaltet ClickToFlash.

Gemeinsam haben beide, dass sie ( vor allem bei Flash) reine Flash-Videoplayer versuchen zu erkennen und bei Erfolg in entsprechendes HTML5-Markup umwandelt.

Dabei konnte ich in den letzten Wochen bemerken, dass es erstaunlich viele Flash-Videoplayer gibt, die eigentlich nur H.264-Videoquellen abspielen. Daraus folgt glücklicherweise, dass der Browser weit aus weniger Flash laden muss (auch auf Anforderung = Click), denn die meisten Videos werden direkt als “HTML5 <video>” abgespielt.

Die Quintessenz: Ungeachtet der [mutmaßlichen] vielen “Nicht-HTML5-Ready”-Flash-Inhalten muss man jedoch anmerken, dass die Anzahl wohl tatsächlich weitaus geringer ausfallen würde, wenn entsprechende Weichen mehr eingebaut würden.

[Howto] Maven: Wie man eine ausführbare Jar in eine Java-Webanwendung (War) via Webstart integriert.

Die Situation

Es bestehen zwei lauffähige, fertige Projekte in Maven, welche beide auch vollwertige Artefakte bilden können.

Zum einen die Webanwendung — nennen wir sie hier mal webportal — mit einem WAR-Artefakt, etwa für einen Tomcat. Ob das Artefakt als Snapshot oder Release gemacht wird, ob es nur generiert oder auch in ein Repository deployt wird, ist hierbei nicht von weiterer Bedeutung.

Zum anderen die normale Clientanwendung — nennen wir sie doch einfach userclient — mit einem JAR-Artefakt. Wichtig ist natürlich, dass hier eine startfähige Main-Klasse vorhanden ist. Dies sollte jedoch der Regelfall sein, daher nur der formale Hinweis.

Zusammengefasst, und der Auftrag an dieses Howto ist also: Wie konfiguriert und erweitert man den Buildzyklus in welchem Projekt an welcher Stelle am geschicktesten, um auf einfachem Wege die JAR-Datei userclient in die Webanwendung webportal als Java Webstart zu integrieren. Dabei ist es hilfreich, wenn man via pom.xml (und natürlich der Macht der Properties) die Versionen spezifizieren kann. Der gesamte Prozess von auswählen, signieren und ausliefern soll dabei automatisch und ohne weiteres Eingreifen des Entwicklers geschehen können. Idealerweise sollte das ganze auch optional sein — dazu gibt es dann mehr unter “Optimierungen und Verbesserungen”.

Den Rest des Eintrags lesen. »

Tags: , , , ,

Kleines FritzBox/Netcologne Wächterscript – Uptime, Reconnect per Script

Aufgabe: Das automatisierte Auslesen der aktuellen Uptime (Verbindungszeit) der eigenen Internetverbindung und idealerweise auch das “reconnecten” per Scriptaufruf.

Das NetCologne Router-Modem-Gespann — welches eine spezielle gebrandete FritzBox ist — mangelt es etwas an Scripting-Möglichkeiten. Der einzige Zugriff ist eine Weboberfläche, die a) nur dürftig ist und b) darüber hinaus auch noch (wieso eigentlich?!) mit einem Sessioncookie “gesichert ist”.

Das hat Folgen. Nachdem man dann per Firebug o.ä. Tools herausgefunden hat, hinter welcher URL sich bspw. die Uptime versteckt, muss man feststellen, dass das Ergebnis des entsprechenden wgets dennoch nur die blöde Übersichtsseite enthält. Warum? Weil der web.cgi-Controller jedes Mal, wenn keine Session oder ein falscher Referrer  geschickt wird, immer jene Seite ausgibt.  Hilfreich war da ein kleines Snippet.

Herausgekommen ist ein kleines Shell-Script, welches folgende Basics kann:

  • uptime – Ermittlung der aktuellen Zeit
  • ipaddr – Ermittlung der aktuellen IP-Adresse (extern)
  • connect, disconnect, reconnect – Steuern des PPPoE-Devices.

Ob noch mehr hinzukommt, werden wir mal sehen. Aktuellen/Letzter Telefonanruf mit Namensauflösung wäre so ‘ne Idee…

Das Script kann man hier laden: netconnect.sh

Entwickelt und getestet wurde es unter einem OS X 10.6 (+ wget), aber sollte eigentlich unter jedem Unix laufen. Naja, und was Windows angeht: Mädels, besorgt Euch ein ordentliches Betriebssystem ;) Wahlweise geht sicher auch Cygwin – wer es braucht.

Hudson-Konfiguration im SVN

Keeping your configuration and data in Subversion

Auch die Konfiguration des Build-Servers selber sollte man sichern.

Howto: Ein firmeneigenes Java-Maven-Repository aufsetzen

Für diesen Artikel setze ich jetzt einfach mal voraus, dass die Begriffe und Technologien hinter Java, Maven, Repository, Eclipse und Tomcat bekannt und geläufig sind. Und nein, jeweiliger Profi muss man zum Verständnis nicht sein.

Was wollen wir?

An ein firmeneigenes Repository — oder auch: corporate repository — gelten besondere Anforderungen. Diese können bei einem “einfach privat-eigenen” verändert werden, aber man kommt meistens auf folgende Punkte:

  1. Ein Repository soll den Entwicklern zum Deployen der eigenen Artifakte und Module zur Verfügung gestellt werden. Wahlweise übernimmt dies auch ein automatischer Build-Agent wie Hudson, TeamCity und haste-nicht-gesehen.
  2. Ein Repositorymanager soll als proxy fungieren. In einer Firma spart dies nicht nur einfache Bandbreite. Da ein solcher Manager in der Regel im lokalen Netz steht, sind die Interaktionszeiten um ein Vielfaches besser.
  3. Ein so genanntes third party pepository für die Abhängigkeiten, die unbedingt notwendig sind und wovon es keine Maven-Abhängigkeiten gibt.

Für Punkt zwei spricht auch eine wesentlich einfachere Konfiguration der Clients (Entwicklerprofile), da nur noch ein Repository eingetragen werden muss. Alle “bekannten” Repositories werden zentral gebündelt, damit schwindet natürlich gleichzeitig die “Freiheit” des einzelnen Entwicklers, andere “unbekannte” Repositories zu verwenden. Dies ist jedoch vernachlässigbar, weil: Diese “Freiheit” schränkt im Endeffekt den Buildprozess und auch die Wiederverwendbarkeit ein. Das Hinzufügen von weiteren Repositories in die POM ist aus Gründen der Versionisierung und Nachhaltigkeit auch keine optimale Lösung. Dennoch, alles nur eine Sache der Konfigurations der Clients.

Was brauchen wir?

Es gibt eine Reihe von Repository-Managern, die allesamt viel können. Die Wahl auf Nexus fällt hier aus folgenden Gründen:

  • der Footprint ist mit 30 Megabyte wesentlich kleiner als bspw. Artifactory
  • die interne Verzeichnisstruktur entspricht mehr oder weniger 1:1 der realen Organisationsstruktur eines Maven-Repositorys (im Vergleich: Artifactory speichert ein eigenes Datenbankstruktur ähnliches Layout)
  • Nexus und das Eclipse-Plugin m2eclipse sind vom gleichen Hersteller Sonatype und ergänzen sich; nach dem Umstellen bemerkt der Entwickler keinen Unterschied in der Suche, Auto-Discovery, o.ä.

Eine umfangreiche Online-Dokumentation ist auf der Nexus-Seite zu finden.

Installation

Nexus wird unter anderem als WAR ausgeliefert, insofern die Installation in einen Tomcat ein leichtes ist. Beachtenswert ist dabei nur, dass Nexus ein Verzeichnis ~/sonatype-work erstellt. Da sich dort unter Umständen viele Nutzdaten ansammeln, kann ein Verschieben (symbolischer Link?) nicht verkehrt sein. Da sich mit der Laufe der Zeit einiges an Daten ansammeln kann, sollte der Platz nicht zu sparsam vermessen sein.

Umfang

Nachdem Tomcat bzw. Nexus gestartet ist, kann man sich mit dem Default-Daten admin/admin123 anmelden (analog  mit den Daten der anderen beiden Benutzern!).

Nexus kommt bereits mit einer Reihen von vorkonfigurierten, eigenen hosted repositories einher.

  • releases sammelt alle Release-Artifakte der Firma
  • snapshots sammelt alle Snapshot-Artifakte der Firma
  • third-party sammelt alle Release-Artifakte externer Quellen, wofür es keine Maven-Repositories gibt (oder wo man jenes Repository nicht generell zur Verfügung stellen will), gutes Beispiel ist ein (aktueller) Oracle-JDBC-Treiber

Daneben gibt es so genannte proxy repositories, die praktisch gesehen nur aus einem Index bestehen. Wie ein Proxy hängen sie sich zwischen dem Client und dem tatsächlichen Repository und cachen alle Artifakte lokal. Selbst der Index ist mehrere Megabytes groß, das sollte man nicht vernachlässigen. Voreingetragene proxy repositories sind Apache Snapshots, Codehaus Snapshots, Central Maven Repository (Maven1/Maven2-Repository-Konverter sind in Nexus vorhanden a.k.a. virtual repositories).

Die grouped repositories sind auch rein virtuelle Gruppierungen von verschiedenen Repositories. Das Standard Repository “Public” ist in der einfachsten Konfiguration eine Sammlung aller (aktivierten) Repositories auf dem Manager — also sowohl der externen Spiegel, der Third-Parties als auch den eigenen Artifakten.

Konfiguration

Die Aktivierung und Verwaltung von (neuen) Repositories ist abhängig der eigenen Bedürfnisse. Meistens sinnvoll ist es jedoch, bei den drei großen Spiegeln (proxy repositories) in dem Konfigurationstab das Indizieren (Download Remote Indexes) zu aktivieren. Je nach Belieben kann man in der Gruppe Administration auch das Aktualisierungsverhalten steuern.

Konfiguration: Deployment

Um ein Deployment zu gewährleisten, muss man nebst Kenntnis der Repository-URL (eben die URL) nur wissen, ob der anonyme Zugriff erlaubt sein soll, oder ob man einen Deployment-Benutzer einrichten und nutzen willst. Falls eine Richtlinie vorschreiben sollte, dass dies nur ein Build-Agent machen darf, ist ein (geheimes) Passwort oder Schlüssel unabdingbar.

Konfiguration: Client

Nehmen wir an, der Nexus-Manager ist auf dem Host 192.168.0.10:8080/nexus installiert. In der einfachen Installation und Konfiguration sammeln sich im public repository praktisch alle relevanten Artifakte (sowohl Releases als auch Snapshots).

<settings>
<mirrors>
<mirror>
<id>corporate</id>
<name>Corporate Repository</name>
<url>http://192.168.0.10:8080/nexus/content/groups/public</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
</settings>

Der Deployment-User benötigt ggf. Zugangsdaten für das entsprechende Repository.

Ein Client wie m2eclipse sollte danach einen kompletten Rebuild des Index machen.

Tipp: Douglas Crockford – The JSON Saga

Link

Der Chuck Norris im JavaScript. ;)

Beispiel?

I give permission for IBM, its customers, partners, and minions, to use JSLint for evil.

MacOS X Security

Boah, was für ein Artikel. Geht zusammengefasst um das Sicherheitskonzept von MacOS X, teilweise im direkten Vergleich zu Windows. Unter anderem auch mit der These: Der geringe Marktanteil von Mac OS X ist nicht Schuld an kaum Malware.

Nicht überaus technisch überladen, also sehr verständlich (auf deutsch) erklärt.

Es sind zu viele Informationen auf einmal gewesen, aber beim ersten Durchlesen sind mir keine Fehler aufgefallen. Einzig allein den Passus, dass “Mac OS X” in großen Teilen offen läge, finde ich unglücklich formuliert. Der Autor meinte sicher, dass viele Komponenten (wie das in dem Kontext genannte Samba) offen sind und von Apple dort auch genutzt werden.

Java Reflection API – mit Cache, ey!

Einführung in Reflection

Die Java Reflection API ist Bestandteil des JDK und ermöglicht den Zugriff auf Methoden, Felder und Annotationen von Klassen und Objekte, also den Instanzen von Klassen. Mit Ausnahme von Annotationen geschieht der Zugriff immer auf einer Meta-Ebene.

Mit einem einfachen Beispiel ist diese Meta-Ebene erklärt.

class Foo {
private String bar = "private value";
}

Stellen wir uns vor, wir wollen ganz exemplarisch von dem Objekt foo (foo = new Foo())den Inhalt von dem Feld bar erhalten. Auf dem üblichen Weg ist das nicht möglich, da das Feld ein privates ist.

Über den zugehörigen Klassentyp (via getClass()) stellt Java eine Reihe von Methoden aus dem Reflection-Paket zur Verfügung, u.a. auch Field getDeclaredField(String). Ein declared field ist ein Instanzattribut eines Objektes, während ein field (Field getField(String)) statische Felder finden.

Field field = foo.getClass().getDeclaredField('bar');

Die Variable field enthält aber – wie man dem Typ entnehmen kann – aber nicht den Inhalt von bar, sondern ein Field-Objekt. Um an den Inhalt zu kommen, verwendet man die Methode get(Object). Der Parameter bezeichnet den entsprechenden Kontext, also das Objekt foo von weiter oben.

Object value = field.get(foo);

Das bedeutet im Klartext: Auch für eine weitere Instanz der Klasse Foo (etwa foo2) kann dieses Field für dieses Feld verwendet werden. Es beinhaltet nur die Metainformationen, nicht die eigentlichen Inhalte.

Dabei ist jedoch zu beachten, dass der SecurityManager aktiv werden kann; in diesem Falle wird eine Exception bei get(Object) geschmissen, weil das Feld nicht sichtbar ist.

field.setAccessible(true);

schafft dabei Abhilfe.

Und Methoden?

Im Prinzip funktioniert das Schema bei Methoden genauso – aber bevor ich jetzt einen Radio Eriwan-Witz loslasse…

Im Gegensatz zu den vorherigen Feldern werden Methoden nicht nur über den Namen, sondern auch über die Parametertypen identifiziert (daher können Methoden sich vom Namen her in Java auch “überladen”).

class Bar {
private String name;
String getName() {
return name;
}
String getName(String defaultName) {
return (name == null) ? defaultName : name;
}
}

Die Metainformationen werden entsprechenderweise geladen:

Method method1 = bar.getClass().getDeclaredMethod("getName");
Method method2 = bar.getClass().getDeclaredMethod("getName", String.class);

Selbstverständlich ist bei Kenntnisnahme der Klasse auch folgende Zeile gleichwertend:

Method method1 = Bar.class.getDeclaredMethod("getName");

Methoden haben im Gegensatz zu Feldern keinen “Inhalt”, sondern sind eine Aktivität oder Operation — und zwar auf einem Objekt. Dementsprechend funktioniert das Ausführen mittels der Method Object invoke(Object, Object…args):

Object result1 = method1.invoke(bar); // getName()
Object result2 = method2.invoke(bar, "default name"); // getName("defaultName")

Und Annotationen?

Annotationen sind “Anmerkungen” im Quellcode, und seit Java 5 fester Bestandteil des JDKs. Technisch gesehen sind Annotationen besondere Klassen mit eingeschränkten (Java-) Möglichkeiten, deren Instanz auf einem Meta-Attribut wie Klasse, Feld, Methode oder Methodenparameter “hängen”. Mit anderen Worten bedeutet dass, dass man mittels Reflection tatsächlich eine Instanz einer Annotation erhält — allerdings mittels einem Proxy (aus der Java API).

@Entity
class Account {
@Column(name="id_x")
private Long id;
}

Die Methode getAnnotation(Class) existiert in allen Typen: Class, Field und Method.

Account account = new Account();
Entity annotation1 = Account.class.getAnnotation(Entity.class).annotationType();
Column annotation2 = Account.class.getDeclaredField("id").getAnnotation(Column.class).annotationType();
Assert.assertEquals("id_x", annotation2.name());

Performance

Im Regelfall benötigt man keinen Zugriff auf Reflection und sollte es vermeiden. Nichtsdestotrotz gestalten sich Konfigurationen, welche auf Basis von Annotationen, meist als sehr einfach, simpel und vor allem Code-konzentriert. Das JPA-Mapping (etwa mit Hilfe von Hibernate) gestaltet sich via Annotationen wesentlich einfacher, schneller und schlanker als mit XML.

Jeder Zugriff über die Reflection API kostet Zeit. Für Konfigurationen und Pläne ist das meist nebensächlich; die JPA-Konfiguration wird beim Start der Applikation eingelesen, analysiert und gespeichert. Nebenbei profitiert das natürlich von der anfänglichen “Warmup-Phase”.

Besteht jedoch ein Access on Demand, so sollte man sich Gedanken um eine geeignete Cachestruktur machen. Wichtig ist dabei, die Metadaten von den eigentlichen Inhalten zu trennen.

Cachen & Lösungsansätze

Jeder der oben genannten Reflection-Getter gibt es auch jeweils eine getAll-Variante: Class.getDeclaredFields(),Class.getDeclaredMethods(), Class.getAnnotations(), Field.getAnnotations() und Method.getAnnotations().

Mit einem einfachen Algorithmus kann man die Laufzeit drastisch senken, wenn Objekte des gleichen Typs mittels Reflection untersucht werden.

public class CacheReflectionUtil {
// cache of declared fields of class types
private final Map<Class<?>, Field[]> classDeclaredFields = new HashMap<>();
// Return the declared fields of the given class type.
public Field[] getDeclaredFields(Class<?> type) {
Field[] fields = classDeclaredFields.get(type);
if (fields == null) {
fields = type.getDeclaredFields();
classDeclaredFields.put(type, fields);
ensureAccessibility(fields);
}
return fields;
}
// Ensure that the given fields are accessible.
public void ensureAccessibility(Field[] fields) {
for (Field field : fields) {
// setAccessible will not only set a property but invoke SecurityManager stuff
if (field.isAccessible()) {
field.setAccessible(true);
}
}
}
// Return the declared field of the given class type.
public Field getDeclaredField(Class<?> type, String name) {
for (Field field : getDeclaredFields(type)) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
}

Um einen stetigen Speicherverbrauch zu verhindern, empfiehlt sich das Nutzen einer Least-Recently-Used-Struktur. Das Apache-Commons-Paket bietet dies etwa mit der LRUMap an.