schakko.de

Netzwerkadministration, Softwarentwicklung und privater Krams

Connecting ASP.NET MVC, MSBuild, CasperJS and TeamCity for user interface testing

I must admit I have been ducking of implementing a working integration test suite in our current project for a long, long time. Some days ago I had to react on this fact as we needed to test some code of the  JavaScript frontend which had to interact with the ASP.NET backend through AJAX requests. The last time I tried to implement integration tests, I used Selenium which was some kind of … ugly so I decided to give CasperJS a try.

You can find the complete example in my Github repository: https://github.com/schakko/asp.net-msbuild-casperjs-teamcity.

Eine interessante Bewerbung zum Beruf des Fachinformatikers schreiben

Momentan befindet sich die NeosIT GmbH in der Situation, dass wir zum 1.8.2013 zwei neue Azubis suchen. Nachdem wir vor einigen Wochen die Ausschreibung beim Arbeitsamt aufgegeben hatten, kamen viele Bewerbungen herein. Als Ausbilder bin ich der direkte Ansprechpartner für potenzielle Auszubildende und dementsprechend stark in den Auswahlprozess involviert.

Einige Sachen sind mir während der letzten Wochen aufgefallen:

  • Als Unternehmen, dass zum Fachinformatiker ausbildet, wollen wir die Bewerbungen auch digital bekommen. Das Standardformat für so etwas ist PDF und nicht .doc oder .rtf.
  • Sollte die Bewerbung analog bei uns eintrudeln, ist nichts nerviger, als solch eine Aufklapp-Bewerbungsmappe (so etwas zum Beispiel). Da wir die Bewerbungen sowieso einscannen, ist eine Mappe mit Klemme für uns die beste Wahl.
  • Eine Bewerbung soll einheitlich sein (d.h. eine Schriftart) und keine grammatikalischen Fehler enthalten. Ein Satzzeichen wird von einem Leerzeichen gefolgt. Ein Foto gehört zu einer Bewerbung dazu!
  • Einer der Bewerber hatte eine DVD mit seinen Arbeiten erstellt (Initiativbewerbung Mediengestalter). Bei uns kam das gut an: Wir schauten uns die Werke auf der DVD rund 20 Minuten an.
  • Das Anschreiben einer Bewerbung entscheidet über den Ersteindruck. Wenn du beispielsweise das Studium abgebrochen hast, erwarten wir auch eine Begründung. Wenn du bereits Vorkenntnisse hast, erzähl uns davon. Das beste Anschreiben, dass wir bisher bekommen hatten, ging übrigens über 1 1/2 Seiten. Wir waren von dem Inhalt so geflasht (sagt man das heutzutage noch?) gewesen, dass wir den Bewerber einluden und er schließlich bei uns auch Azubi geworden ist.
  • Wenn du dich zum Anwendungsentwickler bewirbst und bereits programmiert hast, liefere uns Links zum oder eine DVD mit Teilen des Quellcodes mit. Falls du eine Ausbildung zum Systemintegrator machen willst, schick uns ein Foto von deinem gemoddeten Computer, einen Plan deines Heimnetzwerks o.ä. mit.
  • Wenn du dich sozial engagierst (z.B. leitende Tätigkeit im Sportverein, Kassenwart, Betreuung im Altersheim), erwähne das auf jeden Fall in der Bewerbung!
  • Unsere Anforderungen an einen Auszubildenden sind recht hoch. Auch wenn eine Ausbildung dafür gedacht ist, einen Beruf zu erlernen, heißt es nicht, dass man vorher darüber nichts wissen darf! Wie für jeden anderen Beruf auch, gilt: du solltest vorher wissen, ob dir die Ausbildung Spaß macht. Für dich heißt das: informiere dich vorher beim Arbeitsamt über den Beruf des Fachinformatikers. Frage bei den Unternehmen nach, in welche Themenbereiche sie dich einarbeiten werden (Welche Programmiersprache? Welche Datenbank? Welche Betriebssysteme? usw.).
    Die hohen Anforderungen begründen sich darin, dass viele Bewerber bereits Vorkenntnisse mitbringen (abgebrochenes Studium, Informatikunterricht auf dem Gymnasium, eigene Projekte etc.). Die Konkurrenz ist dementsprechend hoch.
  • In vielen Anschreiben, die zu einer Bewerbungen für die Systemintegration gehörten, musste ich lesen, dass die Bewerber “Spaß an der praktischen Arbeit mit der Computerhardware haben”. Bei der NeosIT GmbH verbringen wir nicht einmal ein Prozent der Arbeitszeit mit “Rechner schrauben”. Der größte Teil ist Planung, Systemadministration und kaufmännische Tätigkeiten.
  • Wichtig ist eines: du musst richtig Lust auf die Ausbildung haben. In einer Bewerbung liest man schnell, ob ernsthaftes Interesse da ist oder ob es “nur eine weitere Bewerbung um der Bewerbung willens” ist. Zeig’ uns, dass du Spaß an der Materie hast. Es bringt weder dir noch uns etwas, wenn du nach ein paar Monaten merkst, dass Programmieren zu kompliziert oder aber der wirtschaftliche Teil der Ausbildung zu langweilig ist.
  • Wir hatten bisher deutlich mehr Bewerber für die Systemintegration als für die Anwendungsentwicklung. Ich gehe davon aus, dass dies auch in anderen Unternehmen der Fall sein wird. Du hast also mit einer guten Bewerbung für den Bereich Anwendungsentwicklung recht hohe Chancen, schnell eine Ausbildung zu finden.

Momentan sind wir noch auf der Suche nach Auszubildenden.  Die Ausschreibung sagt schon ziemlich viel. Hier noch ein paar private Statements.

  • Die Berufsschule ist gleich auf der anderen Straßenseite. Kurze Wege für dich von der Berufsschulklasse zur Kaffeemaschine.
  • Wir haben einen professionellen Kickertisch und wissen damit umzugehen!
  • Unser Team macht gerne Sport. Wenn du Lust hast, nehmen wir dich gerne mit zum Schwimmen, Biken, Joggen oder Bouldern.
  • Unsere Küche wird regelmäßig genutzt um Nicht-Fast-Food-Speisen zu kochen ;-)
  • Wir machen nicht nur bei Netzwerkparties mit, wir haben diese auch organisiert – z.B. die WRC-LAN im Ruderclub Wolfsburg mit knapp 100 Teilnehmern.
  • Aktuelle Smartphones und Tablets wirst du bei den Kollegen öfter finden.
  • Unsere Büros liegen direkt in Wolfsburg. Restaurants und Bars sind gleich um die Ecke.
  • Du hast bei uns uns die einmalige Möglichkeit, deine Kenntnisse und Fähigkeiten in den Bereichen zu vertiefen, die dich auch interessieren.

Studium ist endlich fertig

Nachdem ich zwischen Ende November und Ende Januar die ganze Zeit an der Bachelorarbeit gewerkelt habe, habe ich nun endlich mein Bachelor-Studium Web- und Medieninformatik am Freitag (1.3.2013) erfolgreich abgeschlossen. Das Kolloquium lief richtig gut und es hat Spaß gemacht, über das Thema Integration von Xtext in einen bestehenden Softwareentwicklungsprozess zu referieren – was sich auch in der Note niedergeschlagen hat ;-)

Freitag Abend wurde der Abschluss mit den Kommilitonen erfolgreich begossen, so dass ich bei der Abschlussfeier am Samstag Mittag noch einen leichten Kater hatte. Sehr symbolisch war, dass am Samstag zum ersten Mal in diesem Jahr die Sonne heraus kam. Im Anschluss an die Entlassung fuhr ich noch ins Kletterwerk zum Bouldern.

Die komplette Bachelorarbeit zum Thema (PDF + LaTeX) steht jetzt unter https://github.com/schakko/bsc-thesis-documentation zum freien Klonen zur Verfügung. Den Quellcode der Arbeit kann ich allerdings aus gegebenen Gründen nicht veröffentlichen ;-)

Mein zweites Ziel für dieses Jahr (neben der Bachelorarbeit) habe ich übrigens auch gepackt: Mit den Kollegen bin ich heute Abend bei der JUG Ostfalen in der Autouni gewesen, wo wir uns von Oliver Gierke und Stefan Tilkov zum Thema REST anhörten.

Build and deploy your Xtext DSL with Maven Tycho

A few days ago I finished my bachelor thesis with the title Integrating Xtext in an existing Software development process. I developed a domain specific language with Xtext and some extensions for easily adding new generators as new Eclipse plug-ins.

After finishing the thesis I added a Maven Tycho based build configuration. The build configuration can be easily deployed to TeamCity, Hudson or any other build server of your choice. I assume that you have already developed an Xtext based DSL and you know a little bit about OSGi and Maven.

The complete project source code can be found at https://github.com/schakko/xtext-plugin-with-maven-tycho

Preparations

First of all you should think about a sensible directory structure. I use the following organization

  • /plugins contains your Eclipse based plug-ins
  • /features contains the Eclipse features for bundling your plug-ins
  • /releng is used for the release engineering process and holds the project for the p2 update site
  • /pom.xml is the master POM for your complete project.

Directory structure of your DSL project

Set up the master pom.xml

Every pom.xml inside the subdirectories inherits from the master pom.xml. The configuration defines all modules of your project, sets the needed plug-in repositories and plug-ins. The most interesting part of the lifecycle configuration is, that the xtend-maven-plugin is not executed in generate-sources but generate-resources. This is needed as the MWE2 workflow of your DSL project has to be executed first. If you have both plug-ins inside the generate-sources phase the xtend-maven-plugin will be called first, resulting in a ClassNotFoundException as the DSL infrastructure is not generated yet. So the compilation of Xtend is moved to generate-resources which is executed after generate-sources phase. Another solution would be moving the xtend-maven-plugin to every child pom but this means duplicate configuration code.

Set up your DSL project

pom.xml

The pom.xml contains the plug-in for executing the MWE2 workflow. Notice that your <workflowDescriptor> must begin with src/ or you will receive the message Cannot create a resource for…. All the other stuff is straight forward. The packaging type eclipse-plugin is introduced by the tycho-maven-plugin.

build.properties

The file build.properties contains the source and binary folders. This is important, as tycho-maven-plugin uses this file as reference for compiling and packaging the sources. The xtend-gen directory must be added.

META-INF/MANIFEST.MF

It is important, that your Bundle-Version must end with the suffix .qualifier, e.g. 1.0.0.qualifier. The string is automatically replaced with the build date.

Set up your test project

Copy generated files

At first the two automatic generated files from src-gen (*InjectorProvider.java and *UiInjectorProvider.java) must be copied to the src folder if you want use the xtend-maven-plugin. The Xtend Maven plug-in does not allow (at least in version 2.2.1) any additional source pathes so that none class in src-gen can be found by xtend-maven-plugin. Remove the src-gen directory from your Eclipse classpath too.

pom.xml

The pom.xml for the test project uses a different Xtend version because I ran into heavy problems with the xtend-maven-plugin using the ValidatorHelper. Referencing the ValidatorHelper class results in a GenericSignatureFormatError. Sven Efftinge mentioned yesterday that the bug should be fixed in the latest snapshot.

The packaging type must be eclipse-test-plugin.

pom.xml of your test project

build.properties

Include the xtend-gen directory and make sure that the src-gen directory is not included because of the xtend-maven-plugin problem.

META-INF/MANIFEST.MF

The MANIFEST.MF uses the Import-Package statement for importing Mockito. See the next section how to set up this dependency.

Creating a dependency project

I used Mockito as mocking framework inside my tests. Adding this dependency is a little bit tricky:

  • Adding the mockito-all-1.9.5.jar to your Eclipse build path does not work. Maven does not know anything about your .classpath settings.
  • Adding a dependency to the pom.xml means that the compilation with Maven would work but not the installation on the client because there is no OSGi package for Mockito available.
  • Converting the mockito package to an OSGi package is not ideal. Every dependeny must be repackaged.

A much better solution is including the required JAR files inside an own OSGi package. I took the idea from the atlassian-connector project.

New Plug-in project

Create a new Eclipse plug-in project, create a folder lib and copy the  mockito-all-1.9.5.jar inside.

pom.xml

Nothing new, just use eclipse-plugin as packaging type.

build.properties

Your build.properties must include the lib directory.

META-INF/MANIFEST.MF

Here comes the interesting part. We use Bundle-ClassPath to define the additional path to the mockito JAR file. Every package inside this JAR file is re-exported via Export-Package.

Include the mockito framework in an OSGi bundle

Set up your UI project

pom.xml

The pom.xml must use the packaging type eclipse-plugin.

build.properties

I added the schema and icons directory and plugin.xml. This depends on your needs.

META-INF/MANIFEST.MF

Nothing new.

Set up your feature projects

Features are bundles which consist of different plug-ins. Lars Vogel made a good tutorial which you can use for more information

We need to create two features:

  • A runtime/core feature with your DSL and UI plug-in
  • A test feature with the test and dependencies plug-in

pom.xml

Create a pom.xml and use the packaging type eclipse-feature.

Configure the core feature

Set up your update site

The update site contains every feature the user can install. Lars wrote a tutorial about this but my needs were different. I wanted to deploy the content on a local webserver with a simple copy command.

To do so, generate a new update site project inside your releng directory and add a category definition (in Eclipse: Plug-in Development > Category Definition).

category.xml

The category.xml contains the features which are available through he update site. First of all, add a new category named “Xtext” and assign both of your previously created features to this category.

pom.xml

The packaging must be eclipse-repository. I use the copy-maven-plugin for copying the update site to the local webserver.

Configure the update site for your features

Run it local

With all these settings the project can be build inside Eclipse without m2e plug-in and on the command line. Go to the command line and execute mvn install:

Successful build on command line

Move it to your build server

Create  a new configuration in your TeamCity instance and add a Maven buildstep. I pass the -DupdateSite.target parameter for specifying the destination directory of the update site.

Configure the Maven build step in your TeamCity instance

Install your plug-ins

Enter the path to your update site and you will see your installable Xtext DSL

Install your Xtext DSL in Eclipse

Use Confluence Play SQL Plug-in with MySQL

Play SQL is a an Atlassian Confluence plug-in for querying database tables and displaying the results inside a Confluence page. The plug-in has only native support for PostgreSQL and HSQL but other drivers can be used via a JNDI datasource.

For using MySQL with Play SQL you have to download the latest MySQL JDBC driver and extract the .jar file to your confluence/lib directory as the Tomcat container searches this path for additional libraries. Now register the new JNDI datasource by adding the following code to your confluence/conf/server.xml:

<pre>
  <Engine name="Standalone" defaultHost="localhost" debug="0">
    <Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="false">
      <Context path="" docBase="../confluence" debug="0" reloadable="false" useHttpOnly="true">
        <Resource
          name="jdbc/my_datasource"
          auth="Container"
          type="javax.sql.DataSource"
          url="jdbc:mysql://${SERVER}/${DATABASE}?user=${DATABASE}&amp;password=${PASSWORD}"
          driverClassName="org.gjt.mm.mysql.Driver"
          maxActive="20" maxIdle="10" maxWait="20000" />

          <Manager pathname="" />
      </Context>
    </Host>
  </Engine>
</pre>

Restart your Confluence instance (/etc/init.d/confluence restart) and open Play SQL configuration page inside the Confluence administration page. Change the Global Connection to Type: Use JNDI connection, Dialect: Generic and JNDI Name: java:comp/env/jdbc/my_datasource.
That’s it.

Processing annotations in Xtext and receiving “Could not find field…”

As I mentioned in my last post the current Xtext DSL I am working on provides annotation support. Every artifact generator plug-in can provide additional annotations which influences the generation process of different artifacts. My unit tests were all green but at runtime in Eclipse/OSGi environment I received the following error:

Caused by: java.lang.NoSuchFieldException: Could not find field de.schakko.mydsl.artifact.extension.EXTERNAL_OUTPUT
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.featureCallField(XbaseInterpreter.java:691)

I debugged the source code and noticed that my main plug-in (the .ui project of my Xtext DSL) throws an inner ClassNotFoundException as de.schakko.mydsl.artifact.extension is not available through the OSGis DefaultClassLoader. The reason for this error was obvious: de.schakko.mydsl parses the model of the DSL and maps every XAnnotation to the corresponding JVM type. The parsing process is triggered by de.schakko.mydsl.ui so this classloader is used. The annotation itself is inside the de.schakko.mydsl.artifact.extension.jar and can not be seen by de.schakko.mydsl.ui. Adding de.schakko.mydsl.artifact.extension to the Require-Bundle/Import-Package declartion inside de.schakko.mydsl.ui/META-INF/MANIFEST.MF was no solution, because the UI project does not know anything about the artifact generators.
I tried using the Eclipse-BuddyPolicy/Eclipse-RegisterBuddy solution, but this does not work for unknown reasons. Another option was to provide every classloader of the artifact generators to a defined extension point inside de.schakko.mydsl.ui. Not really nice.
In the end I found the DynamicImport-Package declaration of the OSGi standard. This manifest header instructs the OSGi classloader to lookup the class in the standard required and imported packages. If this fails other bundles on classpath are searched through.

Accessing the values of an annotation inside your Xtext-DSL

Today I implemented the last feature of my bachelor thesis and struggled upon a small problem: My DSL makes use of XAnnotation to annotate various grammar elements. The generators can extend the DSL by introducing new annotations which can be used for modifying the generation process of the resulting fragments. Checking, whether an annotation is present or not is easy but retrieving the value of a named parameters in a really easy way was not so obvious at first. Thankfully Xtext/Xbase provides the class XbaseInterpreter for evaulating XExpression objects on the fly. This is the helper method:

class AnnotationExtension {
	@Inject
	XbaseInterpreter interpreter;

	public Object getValue(XAnnotation annotation, String simpleName) {
		IEvaluationResult result = null;
		if (simpleName.equals("value")) {
			result = interpreter.evaluate(annotation.getValue());
		}

		if (result != null) {
			for (XAnnotationElementValuePair pair : annotation
					.getElementValuePairs()) {
				if (pair.getElement().getSimpleName().equals(simpleName)) {
					result = interpreter.evaluate(pair.getValue());
				}
			}
		}

		return (result != null) ? result.getResult() : null;
	}
}

And here is the snippet of the usage inside the DSL:

import de.schakko.rapid.artifact.fpanalysis.runtime.annotation.UseFunctionpointType
import de.schakko.rapid.artifact.fpanalysis.runtime.types.FunctionpointType

// ...

domain d5_fixture {
  @UseFunctionpointType(FunctionpointType::EXTERNAL_INQUIRY)
  process p5_custom {
  }
}

Jahresrückblick 2012

Nun haben wir heute den 21. Dezember und die Welt ist nicht untergegangen. Wie überraschend. Teile meiner eigenen kleinen Welt sind allerdings im Laufe der letzten Monate doch tatsächlich untergegangen, da wirklich viel passiert ist.

Das Jahr begann mit meinen beiden vorletzten Klausuren. Eine davon war recht easy, die andere schon recht anspruchsvoll. Hab dann aber beide doch sehr gut bestanden. Leider war der Klausurtermin einen Tag nachdem Sven Efftinge bei der JUG Ostfalia Xtext vorstellte. Somit konnte ich an der Präsentation nicht teilnehmen, da ich lernen musste. Etwas ärgerlich. Nach den Klausuren hatte ich verständlicherweise gute Laune, da ich alle schweren Arbeiten erledigt hatte.

Der Hammer kam dann am Valentinstag, dem 14.02.2012. Unsere Gesellschafterin, die Frau vom verstorbenen Geschäftsführer der ECW, war der Meinung, das Unternehmen an ein IT-Systemhaus aus Braunschweig verkaufen zu müssen. Für uns alle war das ein echter Schlag ins Gesicht, weil keiner darüber im Vorfeld informiert worden ist. Nach den ersten Informationen stellte ich dann fest, dass das Unternehmen, dass die ECW aufkaufen wollte, vermutlich nicht ganz so professionell arbeitete. Deren Website war seit Monaten gehackt, gleiches galt für eine ihrer Kundenwebsites (ein großes Reiseportal). Über die Sicherheitslücke informierte ich natürlich die betreffenden Personen, die es nach 4 Wochen (!) geschafft haben, das Sicherheitsleck zu stopfen. Zumindest auf ihrer eigenen Firmenwebsite. Die beim Kunden ist immer noch anfällig. Für mich war das ein absolutes no-go, weil ich auf solche Sachen zwangsläufig viel Wert lege.
An sich war beim Verkauf des Unternehmens das Problem, dass die Gesellschafterin die Mitarbeiter nicht gefragt hat, was sie denn wollen. Die nachfolgende Betriebsversammlung werde ich mein Leben lang nicht vergessen. Nachdem ich mir einige Stellenangebote angeschaut hatte und bereits mir mein Zwischenzeugnis hab ausstellen lassen, wurde dann in einer ziemlich abgefahrenen Nacht- und Nebelaktion über mögliche Alternativen geredet. Das Ergebnis bestand dann darin, dass die NeosIT gegründet worden ist. Ziemlich abgefahrene Situation ist das gewesen. Die Solidarität unter den Mitarbeitern war echt enorm.

Ich reichte also meine Kündigung irgendwann Anfang/Mitte März ein und … hatte dann ab April knapp 3 Monate Urlaub. Meine Resturlaubstage und Überstunden wollten schließlich irgendwie abgefeiert werden. Ende März war dann meine letzte Prüfung in Dortmund, die gut lief. Ein gutes Gefühl, mit dem Flachmann in der Hand aus dem Prüfungsraum zu gehen ;-)  Der Präsenztermin an sich war relativ unspektakulär und ich meldete im direkten Anschluss meine Projektarbeit an.

Ab April war dann das Wetter bereits so gut, dass ich wieder regelmäßig Joggen gewesen bin. Weiterhin begann ich bereits im Januar mit dem Bouldern in Braunschweig, was ich bis jetzt auch mindestens einmal in der Woche gemacht habe.  Sowieso habe ich dieses Jahr viel Sport gemacht. Mit meinem neuen Bike habe ich seit Juni dieses Jahr knapp 1700km gerissen, war im Schnitt 4km/Woche Schwimmen und noch Bouldern & Joggen.
Den Frühling und Sommer verbrachte ich eigentlich größtenteils damit, meine Projektarbeit fertig zu machen. Thema war natürlich Nostradamus, das Social Forecasting-Netzwerk. Da ich leider etwas im Stich gelassen wurde, musste ich mich um die ganze Implementierung und Umsetzung selbst kümmern. Da ich die durch den Urlaub den ganzen Tag zu Hause gewesen bin, konnte ich abwechselnd arbeiten und parallel auf dem Balkon lesen. Das Ergebnis war, dass ich bereits Ende Mai eine gesunde braune Färbung hatte.

Mit der Kündigung bei ECW und der Neugründung der NeosIT kam es auch zu Veränderungen. Zwei der drei Azubis kamen mit in das neue Unternehmen, der dritte verließ uns leider und ging zurück nach Berlin. Allerdings wegen familiärer Umstände. Während dieser Zeit hatte die IHK auch ein offenes Ohr für unsere Probleme. Mit dem Verkauf von ECW wurde ebenfalls das Inventar verkauft. Ich staubte den Kickertisch für einen ganz guten Preis ab und war nicht mehr an ein Weiterarbeiten bei ECW nach Ende des Studiums (quasi als Bedingung für den Ausgleich der Studienkosten) gebunden.

Zum 1.7. begann dann die Arbeit bei der NeosIT. Eine chaotische Situation, bei der wir mit 8 – 10 Personen in einem winzigen Büro saßen, da die neuen Büros elendig lange zur Fertigstellung brauchten. War andererseits aber auch total witzig. In dieser Zeit habe ich auch meine Liebe zu den Produkten von Atlassian entdeckt, so dass wir endlich JIRA und Confluence einsetzten. Ich kümmerte mich dann um die Einrichtung von Netzwerk, Build Server, Migration von bestehenden Repositories von SVN zu Git und andere Dinge. War auf jeden Fall spaßig.

Mitte September stellte ich beim Präsenztag meinem Prof meine Projektarbeit vor, die im Endeffekt mit einer 1.0 bewertet worden ist. War ein ziemlich spannendes und konstruktives Gespräch, was mich in den Möglichkeiten der Applikation bestärkt hat. Tags zuvor fuhr ich noch mit Martin in die Kletterhalle Bergwerk zum Bouldern, was auch ziemlich spaßig gewesen ist.

Der Oktober bestand darin, im Projektgeschäft Features zu implementieren und Prozessbegleitung zu machen. Hier hat in den letzten Monaten das Entwicklerteam echt gute Arbeit geleistet, so dass ich aus Projektsicht auf dieses Jahr glücklich zurückblicke. Leider waren die sozialen Einschnitte dieses Jahr echt groß. Zwei Elternteile meiner besten Freunde sind dieses Jahr gestorben und eine langjährige Freundschaft musste ich wegen inakzeptablen Randbedingungen beenden. Das waren alles Erlebnisse, die die zweite Hälfte des Jahres geprägt haben.
Natürlich gab es auch gute Momente, z.B. das Jogging mit Julian und meiner Liebsten, die Fernsehabende, die nächtlichen Gespräche mit Whiskey auf der Parkbank am Allersee, Hochzeiten, spontane Kneipenbesuche nach der Arbeit oder auch der gestrige Besuch in der Autostadt. War also schon schön.

Wie dem auch sei. Im November wurde es dann stressig. Zuerst fuhren Jenny und ich ins Tropicals Islands nach Berlin, wo ich ein echt schönes Zimmer gebucht hatte. Wir verbrachten die beiden Tage in der Sauna und konnten gut entspannen. Direkt einen Tag später war das Barcamp in Braunschweig, bei der ich eine kurze Präsentation über Xtext hielt, nachdem ich Mitte November meine Bachelorarbeit zum Thema Xtext angemeldet hatte. Letzte Woche waren Jenny und ich noch beim Toten Hosen-Konzert in Hannover, was auch total gut gewesen ist. Silvester wird dieses Jahr in der BarCelona gefeiert.

Trivia:

  • Mit der Band haben wir durch die Todesfälle und Studium leider wenig geprobt. Dafür habe ich privat wieder deutlich mehr Gitarre gespielt.
  • Sport, Sport, Sport! Knapp 1 1/2 Jahre Nichtraucher und ich mache so viel Sport in der Woche.
  • Bei einer Akupunktur kann ich total schnell einschlafen.
  • In den letzten zwei Monaten habe ich zu viele Serien gesehen (Person of Interest, Arrow, Continuum, Nikita, Homeland, Fringe, Misfits, Revolution, Alcatraz, Gravity, NTSF:SD:SUV, Once Upon a Time)
  • Auf meinem Smartphone findet sich in der Playlist seit Monaten nur noch Broilers und Betontod.
  • Ich liebe mein Nexus S!
  • Bis Anfang Dezember war es hier ziemlich warm.

Rückblickend schaue ich mal auf meine Vorsätze des letzten Jahres

  • Meine Klausuren fertig schreiben: yes! Schnitt: 1.0.
  • Endlich mal bei der JUG Ostfalia auftauchen: nope, nächstes Jahr?
  • Nostradamus endlich an den Start bringen: nope, Domain ist aber schon gebucht und die Anwendung läuft auch
  • Mehr Holzarbeiten: nope, Studium hatte Vorrang
  • Wieder Schwimmtraining – wie mir das fehlt: Ja, in der Spitzenzeit 2 1/2km pro Schwimmeinheit geschwommen
  • Gödel, Escher, Bach zu Ende lesen: Ich … äh… habe weitergelesen!
  • Diät wegen Nahrungsmittelunverträglichkeit machen: Ja. Festgestellt, dass Tomaten, Nüsse, Rotwein und Sojasauce nicht so meins ist. Außerdem ist Weizen auch nicht so gut für mich :-(
  • Endlich zum Bouldern gehen: Ja. Was für ein geiler Sport :-)
  • Mein privates Mediacenter fit machen: Nein. Nächstes Jahr
  • Eventuell neuen Fernseher kaufen: Nein. Definitiv nächstes Jahr

Eigentlich recht wenig geschafft. Für 2013 steht folgendes auf dem Plan

  • Bachelor mit 1.0 schaffen
  • Sobald Bachelor geschafft ist: Neuen Fernseher kaufen und Mediencenter aufsetzen
  • Nostradamus online bringen

 

TeamCity: Make the NuGet repository available without authentication

Task for today was making the NuGet repository of TeamCity available in our local network. Sounds easier as it was as our TeamCity instance is available from the Internet but you can only access the rontend with valid domain credentials (LDAP/Active Directory authentication). Enabling the guest account feature in TeamCity would have mean a small security break in our concept.

Here is the success-story how to enable the NuGet repository for local accesss without authentication and without enabling the TeamCity guest account:

Creating a new proxy instance

One of our Apache servers got the order to act as reverse proxy for the NuGet repository. The reverse proxy was running inside an own virtual host (nuget.mydomain.local) and was registered in our local DNS.
The configuration looked like this:


<VirtualHost *:443>
 SSLEngine on
 SSLProxyEngine on

ServerAdmin admin@mydomain.local
 ServerName nuget.mydomain.local
 ServerAlias nuget.mydomain.de

LogLevel warn
 ErrorLog logs/nuget.mydomain.local-error_log
 CustomLog logs/nuget.mydomain.local-access_log combined

# Map other source to FeedService URL
ProxyPass / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/
ProxyPassReverse / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/

</VirtualHost>

Adding a domain proxy user

With the configuration above we still have the problem that there is no user who can access the repository without credentials. So we need to add a domain user, let me call it teamcity-nugetfeed-proxy-user. For security reasons this account has a very long random generated password and is only used for providing the NuGet feed.

Using the domain proxy user in our Apache configuration

We have to add a Basic Authorization header in our Apache configuration so that every access between the proxy and TeamCity is authenticated.


RequestHeader set Authorization "Basic <Base64(teamcity-nugetfeed-proxy-user:$password)>"

The phrase <Base64(teamcity-nugetfeed-proxy-user:$password)> must be replaced by an base64-encoded string with format $username:$password.

After restarting the Apache you should be able to use the NuGet stream in Visual Studio. Look at the Apache logs for occuring errors.

TeamCity is failing – NuGet return “File contains corrupted data”

Till yet you are able read the stream but TeamCity will fail downloading your repository files. The reason is easy: The FeedService.svc returns an XML file which contains absolute pathes mapping to teamcity.mydomain.local and not nuget.mydomain.local. So again we have no authorization and the error File contains corrupted data occurs because of the TeamCity login dialogue.

We have to rewrite the content of the FeedService.svc replacing every occurence of teamcity.mydomain… with nuget.mydomain…
At first I tried mod_proxy_html but in the end using mod_substitute was the only solution that worked.


# Rewrite FeedService URL
 Substitute "s|https://teamcity.mydomain.local/guestAuth/app/nuget/v1/FeedService.svc|https://nuget.mydomain.local|i"
# Rewrite repository access
Substitute "s|https://teamcity.mydomain.local/guestAuth/repository|https://nuget.mydomain.local/repository|i"

# Execute every substitution only for XML/HTML
 AddOutputFilterByType SUBSTITUTE text/html
 AddOutputFilterByType SUBSTITUTE text/xml
 AddOutputFilterByType SUBSTITUTE application/atom+xml

# Map local repository path to remote repository
 ProxyPass /repository https://teamcity.mydomain.local:443/guestAuth/repository
 ProxyPassReverse /repository https://teamcity.mydomain.local:443/guestAuth/repository

# Unset gziped accepted encoding
RequestHeader unset Accept-Encoding

After restarting everything should work fine.

The complete configuration is now


<VirtualHost *:443>
 SSLEngine on
 SSLProxyEngine on

ServerAdmin admin@mydomain.local
 ServerName nuget.mydomain.local
 ServerAlias nuget.mydomain.de

LogLevel warn
 ErrorLog logs/nuget.mydomain.local-error_log
 CustomLog logs/nuget.mydomain.local-access_log combined

# Rewrite FeedService URL
 Substitute "s|https://teamcity.mydomain.local/guestAuth/app/nuget/v1/FeedService.svc|https://nuget.mydomain.local|i"
 # Rewrite repository access
 Substitute "s|https://teamcity.mydomain.local/guestAuth/repository|https://nuget.mydomain.local/repository|i"

# Map local repository path to remote repository
 ProxyPass /repository https://teamcity.mydomain.local:443/guestAuth/repository
 ProxyPassReverse /repository https://teamcity.mydomain.local:443/guestAuth/repository
 # Map other source to FeedService URL
 ProxyPass / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/
 ProxyPassReverse / https://teamcity.mydomain.local:443/guestAuth/app/nuget/v1/FeedService.svc/

# Execute every substitution only for XML/HTML
 AddOutputFilterByType SUBSTITUTE text/html
 AddOutputFilterByType SUBSTITUTE text/xml
 AddOutputFilterByType SUBSTITUTE application/atom+xml

RequestHeader set Authorization "Basic <Base64(teamcity-nugetfeed-proxy-user:$password)>"

 # Unset gziped accepted encoding
 RequestHeader unset Accept-Encoding
</VirtualHost>

NuGet and TeamCity: Repository for third party libraries

Later this day I switched over to another task. TeamCity provides a fully functional NuGet repository server. My plan was migrating our own libraries and some 3rd party libraries from a direct dependency (binary file or as Git submodule) inside the project Git repositores to a system like Maven or Ivy or … well … NuGet for .NET.

First I set up a new Git repository containing all our 3rd party dependencies which are not available in the public NuGet repositories. The Git repository had the following structure

/.nuget
  /NuGet.config
  /NuGet.exe
  /NuGet.targets
/External.Lib.1
  External.Lib.1.nuspec
  /lib
    External.Lib.1.dl
/External.Lib.2
  External.Lib.2.nuspec
  /lib
    External.Lib.2.dl

The .nuget directory came from Visual Studio. I used the NuGet.exe binary for local testing.

In TeamCity I created a new Build Configuration and chose **/*.nupkg as Artifact Path.

The rest of the Build Step configuration can be seen on the attached image.

After running the first build I realized that the .nupkg had the wrong directory structure. The structure External.Lib.1.nupkg for example was

/External.Lib.1
  External.Lib.1.nuspec
  /lib
    External.Lib.1.dl

This is not the expected directory tree.

Reason for that is, that TeamCity automatically appends the parameter -BasePath to the NuGet.exe statement resulting in (taken from build log)

NuGet command: <tc>\buildAgent\tools\NuGet.CommandLine.2.0.40001.nupkg\tools\NuGet.exe pack <tc>\buildAgent\work\80f29a1b916940aa\External.Lib.1\External.Lib.1.nuspec -OutputDirectory <tc>\buildAgent\work\80f29a1b916940aa\dist -BasePath <tc>\buildAgent\work\80f29a1b916940aa -Verbose -Version 0.7 -Properties Configuration=Release

There is no frontend option to disable the setting of BasePath parameter. The configuration parameter Additional commandline arguments did not work as expected as it quotes the entire string.

Thanks to the  parameter Build Path > Properties we can do a little bit of quoting magic and overwrite the BasePath option

Configuration=Release " -BasePath "

Yes, there are two quotes as the usage of Properties puts the NuGet command itself in quotation marks.

The complete command resolves to

... -BasePath <tc>\buildAgent\work\80f29a1b916940aa -Verbose -Version 0.15 -Properties "Configuration=Release " -BasePath ""
and everything is fine ;-)

WSUS: Moving from Windows Internal Database to external SQL Server 2008 and receiving “Token-based server access validation failed with an infrastructure error”

Today I had to move the WSUS internal database to one of our backend database servers. Microsoft has a good instruction how to do this, nevertheless I ran into a problem.

Microsoft SQL Server 2008 did not allow me to add the machine account of our WSUS frontend server (let me call it WSUS-SRV), so I created a new Active Directory security group called WSUS Administrators containing the WSUS-SRV machine account. This security group I gave the permission to access the database.

After starting the IIS Admin Service and Update Services the database backend server showed the error Token-based server access validation failed with an infrastructure error (event-id 18456). Oops.
One workaround  would have been to disable the UAC (http://blogs.msdn.com/b/sqlserverfaq/archive/2010/10/27/troubleshooting-specific-login-failed-error-messages.aspx). Not a solution I was very keen about.

I fixed the problem by creating a local security group on the database server and adding the maching account of WSUS-SRV into it.

mod_proxy_connect.so: The specified module could not be found.

Today I wanted to update the Apache configuration in our DMZ to enable Git over SSH over SSL (another story) but while deploying the changes from my workstation to the server, Apache threw the error

Cannot load modules/mod_proxy_connect.so into server: The specified module could not be found.

As always procmon.exe was my friend and showed that mod_proxy_connect.so was referencing the msvcr100.dll which was not existent on the server
I fixed the error by copying msvcr100.dll from my %WINDIR%\SysWOW64 to the SysWOW64 directory of the server (both are x64 machines).

Remove version signature from TeamCity, Confluence and JIRA

If you want to make your TeamCity, Confuence or JIRA instance accessible from outside of your LAN, you should remove all version signatures so that no attacker can easily lookup for existing exploits.

  • TeamCity: Open <TeamCity installation dir>/webapps/ROOT/WEB-INF/tags/version.tag and remove the full content from this file. You must restart the TeamCity service afterwards.
  • Confluence: Open <Confluence installation dir>/confluence/decorators/includes/footer-content.vm and remove the line
    <li class=”print-only”>$action.getText(‘printed.by.atlassian.confluence’,["$generalUtil.versionNumber"])</li>Restart of Confluence service/instance is required.
  • JIRA: Open <JIRA installation dir>/atlassian-jira/WEB-INF/classes/templates/plugins/footer/footer.vm and remove the line
    <span id=”footer-build-information”>(v${build.version}#${build.currentBuildNumber}${commitId}${partnerName})</span>

    Restart of JIRA service/instance is required.

Das Zertifikat für den Web Deployment Service austauschen

Für heute hatte ich geplant, dass eines unserer Projekte automatisch mit Hilfe von Microsoft WebDeploy auf einem IIS veröffentlicht werden sollte. Die Applikation wurde mit TeamCity und MSBuild-Scripten erstellt und somit hatte ich auch bereits die passende *.deploy.cmd-Datei generiert bekommen.

Als ich danach das Deployment erst einmal manuell testen wollte, bekam ich den Fehler ERROR_CERTIFICATE_VALIDATION_FAILED geliefert. Das war auch klar, da der Webverwaltungsdienst bei der Installation ein eigenes Zertifikat erstellt, dieses aber natürlich von meiner Arbeitsstation mangels autorisierter Root-CA nicht erkannt wird. An dieser Stelle hätte ich natürlich das Standard-Zertifikat in meinem eigenen Zertifikatspeicher und danach im Zertifikatespeicher des TeamCity-Benutzerkontos installieren können. Ist aber natürlich nicht Sinn der Sache, da das Root-Zertifikat unseres Domänen-Controllers sowieso per Gruppenrichtlinie auf alle Clients veröffentlicht wird und wir somit auch direkt von unserer CA ein Zertifikat ausstellen lassen können.

Die Option, “-allowUntrusted=true” an die *.deploy.cmd zu hängen, war übrigens nicht erfolgreich, da Microsoft im Batch-Script geschlampt hat und die Gleichheitszeichen durch Leerzeichen ersetzt werden.

Wie dem auch sei: Ich entschloss mich also dazu, ein Zertifikat auszustellen. Zuerst überprüfte ich die einzelnen Zertifikatespeicher auf dem Webserver. Der Webverwaltungsdienst greift dazu auf die Zertifikate des Computerkontos zu und präsentiert sie dann in der Auswahlliste.

Ein neues Zertifikat zu erstellen gestaltete sich nun schwieriger als ich anfangs dachte. Per Weboberfläche des Certifcation-Service vom Domänen-Controller konnte ich mir zwar ein Webserver-Zertifikat ausstellen lassen und dieses auch im Computerkonto installieren, allerdings lies sich nach dem Auswählen des Zertifikats im Server-Manager unter Rollen > Webserver (IIS) > Internetinformationsdienste > $Instanz > Verwaltungsdienst der Dienst nicht mehr starten. Die Ereignisanzeige meldete, dass der private Schlüssel nicht exportiert worden war. Dies scheint standardmäßig bei den Webserver-Zertifikaten nicht der Fall zu sein.

Ich folgte nun einem anderen Weg und rief dazu in der Zertifikate-MMC unter Zertifikate (Lokaler Computer) > Eigene Zertifikate > Zertifikate > Rechtsklick “Alle Aufgaben” > Neues Zertifikat anfordern auf.
Dies sorgt dafür, dass man mit Hilfe eines kleinen Tools direkt bei der CA bzw. DC ein Certification Request einreichen kann. Auf der zweiten Seite bekommt man die verfügbaren Zertifikatsvorlagen zur Verfügung, die für den gerade ausgewählten Zertifikatespeicher benutzt werden können. Da ich beim Aufsetzen der CA nur die Kopien von den Vorlagen Benutzer, Computer und Key Recovery Agent gemacht habe, stand für das Computerkonto nur die Vorlage Computer zur Verfügung.

Schnell die MMC Zertifikatsvorlagen aufgerufen, als Domänen-Administrator mit der Zertifizierungsstelle verbunden und mit Rechtsklick auf Webserver > Doppelte Vorlage eine neue Vorlage erstellt. Die Gültigkeitsdauer setzte ich auf einige Jahre hoch und wählte unter dem Tab Anforderungsverarbeitung die Option Exportieren von privatem Schlüssel zulassen.

Nach dem Speichern hätte ich nun meine Zertifikatsvorlage beim Certification Request eigentlich angezeigt bekommen müssen. Tat es aber nicht – ich bekam nur die Fehlermeldung “Sie besitzen keine Berechtigung zum Anzeigen dieses Typs von Zertifikat“. Nach dem Überprüfen der Einstellungen war ich etwas ratlos, dass ich diesen Fehler bekam. An sich hatte alles gestimmt. Ich war ja Domänen-Administrator und hatte dementsprechend für diese Zertifikatsvorlage durchaus eine Berechtigung (Tab Sicherheit in der von mir duplizierten Zertifikatsvorlage).
Diese Berechtigungen waren aber nicht ausreichend. Ich stellte ja schließlich für das Computerkonto ein Zertifikat aus, also musste der Computer im Sicherheit-Tab die Rechte Lesen, Schreiben und Registrieren bekommen.

Nun bekam ich auch die Zertifikatsvorlage angezeigt, wählte bei den zusätzlichen Einstellungen als Allgemeiner Name den internen FQDN des Webservers und überprüfte, dass der private Schlüssel exportiert wird.
Im Anschluss konnte ich das so eben erstellte Zertifikat auch bei den Eigenschaften des Verwaltungsdiensts im IIS auswählen.

Using L2TP in Ubuntu 12.04 Precise Penguin

For our new company I want to provide VPN access through L2TP in addition to PPTP.

After I had installed network-manager-strongswan the configuration dialogue of network-manager crashed. Being not the only one having this problem I installed the 1.3.0 build of network-manager-strongswan and now I can configure my L2TP connection as expected:


wget http://launchpadlibrarian.net/108979339/network-manager-strongswan_1.3.0-0ubuntu1_amd64.deb
sudo dpkg -i  network-manager-strongswan_1.3.0-0ubuntu1_amd64.deb

Alles neu: Arbeitgeber, Domain, Design, Hobby

In den letzten paar Monaten ist viel passiert – wie man auch an der sinkenden Anzahl meiner Blogposts und Tweets sehen kann.

Zu Beginn des Jahres habe ich endlich mal den Hintern hoch bekommen und bin das erste Mal Bouldern gewesen. Nachdem ich eine knappe  Woche  Mukselkater und Blasen an den Händen hatte, machte ich mich wieder auf in die Halle. Seitdem versuche ich einmal pro Woche zum Bouldern zu gehen. Zum Schwimmen ist das ein super Ausgleich und fördert enorm die Konzentration und Körperbeherrschung. Kann ich nur empfehlen.

Am Valentinstag – das ist der 14. Februar 2012 gewesen – eröffnete die Gesellschafterin meines Arbeitgebers meinen Kollegen und mir, dass das Unternehmen verkauft werden sollte.
Aus diversen Gründen standen die meisten aus der Belegschaft, mich eingeschlossen, dieser Idee nicht ganz so euphorisch gegenüber, wie sie uns anfänglich präsentiert worden war. In den folgenden Wochen häuften sich die Sorgen und der Plan der Gründung eines neuen Unternehmens konkretisierte sich langsam.
Ich reichte meine Kündigung ein, unterschrieb den Vertrag beim neu gegründeten Unternehmen. Viele meiner Kollegen taten es mir gleich und somit fangen wir mit mehr oder weniger dem selben Team gemeinsam komplett neu an. Was die Solidarität unter den Kollegen angeht, könnte sich jede Partei oder Gewerkschaft eine Scheibe von uns abschneiden.
Da ich noch einige Wochen Resturlaub und Überstunden hatte, nahm ich diese und bin deshalb noch bis zum 31.5. offiziell im Urlaub. Danach fange ich dann direkt im neuen Unternehmen an.

Dieses ganze Vorgehen klingt unspektakulär, aber was hinter den Kulissen passierte und teilweise immer noch passiert, ist … dezent ausgedrückt: interessant. Die komplette Geschichte würde einen ganzen Abend ausfüllen. Kopfschütteln und wiederkehrende Facepalmierung inklusive.

Neben den Sorgen auf der Arbeit musste ich mich auch noch um das Studium kümmern. Ende März schrieb ich die letzte Klausur und begann direkt zum 1. April mit meiner Projektarbeit. Über dieses awesome Projekt werde ich zu gegebener Zeit ausführlich berichten. Spätestens aber zum 1. Juli, denn dann ist Abgabetermin. Die Bachelorarbeit werde ich im Laufe des Jahres beginnen. Wenn alles klappt, wird es irgendwas mit Xtext, MDA und Prototyping werden.

Da der alte Blog innerhalb der DMZ des alten Unternehmens gehostet worden ist (und dies auch noch bis zum 31.5. der Fall ist), musste ich mich um einen neuen Webspace kümmern.
Unser Haus-und-Hof-Anbieter Artfiles bietet zwar sehr guten Service und Produkte, allerdings sind die recht teuer und bieten nicht alles, was ich brauchte. Letztendlich habe ich mich dann für ein Zuhause bei den Übernauten entschieden.
Einhergehend damit buchte ich endlich mal die Domain schakko.de - was ich schon vor Jahren vor hatte. Das langweilige alte Design des Blogs wurde durch ein schlichtes, pragmatisches, weißes Design abgelöst. Mich spricht das total an. Da hat der Ersteller des Themes, Jim Barraud, saubere Arbeit geleistet.

Fehlerhafte Unittests in NUnit und xUnit von TeamCity nacheinander ausführen lassen

Aus äh… historischen Gründen ist es so, dass eines unserer C#-Projekte sowohl NUnit als auch xUnit für die Unittests nutzt. Über Sinn und Zweck dieser Konfiguration brauchen wir nicht zu diskutieren – Fakt ist, dass sich das nicht auf die Schnelle ändern lässt.
Ich wollte nun dieses Projekt in TeamCity integrieren, so dass sowohl die Tests von NUnit als auch von xUnit ausgeführt werden. xUnit muss in TeamCity zwangsweise über ein MSBuild-Script integriert werden, was aber einfach zu bewerkstelligen ist. NUnit wird hingegen von TeamCity nativ unterstützt.
Mein erster Ansatz war nun, für den Buildprozess zwei Buildsteps anzulegen. Einmal für NUnit (nativ), einmal für xUnit (msbuild). Problem dabei: wenn einer der beiden Buildsteps fehl schlägt, wird der nächste Buildstep ebenfalls nicht ausgeführt. Das bedeutet also, dass man mit einem Buildlauf nicht die komplette Fehlerliste enthält, sondern immer nur die, des gerade laufenden Unittest-Frameworks (treten Fehler erst im zweiten Buildstep auf, ist dies natürlich egal).

“Schuld” an diesem Verhalten ist die Option Fail build if: at least one test failed. Ist diese nämlich aktiviert (Standardeinstellung), werden nachfolgende Buildsteps beim ersten auftretenden Fehler nicht mehr ausgeführt – was ja auch durchaus Sinn macht. Deaktiviert man die Option hingegen, werden alle Buildsteps ausgeführt, auch wenn in den Unittests Fehler aufgetreten sind. Problem dabei: Der Build wird auch bei aufgetretenen Fehlern in den Unittests grün mit “OK” markiert. Somit bekommt bei fehlgeschlagenen Unittests keiner eine Benachrichtigung über etwaige Fehler.

Ich entschloss mich zu folgendem Ansatz, der mit (N)Ant auf jeden Fall funktioniert hätte: Unser Test-Teilprojekt bekommt ein eigenes MSBuild-File, dass nur die Targets für das Ausführen der NUnit- bzw. xUnit-Tests enthält. Glücklicherweise gibt es von der .NET-Community bereits einige vorgefertigte Tasks, die das bewerkstelligen.

Zuerst müssen die MSBuild Community Tasks installiert werden (letztes Release). Dass die Dependencies zu den Unittest-Frameworks (xunit.runner.msbuild.dll) im Projektverzeichnis vorhanden sind, setze ich einfach mal voraus. Im Projekt mit den ganzen Unittests wird nun eine neue .msbuild-Datei mit folgendem Inhalt erstellt:

<Project DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<UsingTask AssemblyFile="$(MSBuildProjectDirectory)..DependenciesLibsxunit.runner.msbuild.dll" TaskName="Xunit.Runner.MSBuild.xunit" />
	<Import Project="$(MSBuildExtensionsPath)MSBuildCommunityTasksMSBuild.Community.Tasks.Targets"/>
	<PropertyGroup>
		<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
	</PropertyGroup>
	<ItemGroup>
		<CompiledAssemblies Include="$(MSBuildProjectDirectory)bin$(Configuration)YourTestProject.dll" />
	</ItemGroup>

	<Target Name="Build">
		<MSBuild Projects="YourTestProject.csproj" Targets="Build" Properties="Configuration=$(Configuration)"></MSBuild>
	</Target>

	<Target Name="Test-xUnit">
 		<xunit assembly="@(CompiledAssemblies)"  ContinueOnError="true" />
	</Target>

	<Target Name="Test-NUnit">
		<NUnit Assemblies="@(CompiledAssemblies)" ContinueOnError="true" />
	</Target>

	<Target Name="TestOnly" DependsOnTargets="Test-xUnit;Test-NUnit">
	</Target>

	<Target Name="Test" DependsOnTargets="Build;Test-xUnit;Test-NUnit">
	</Target>
</Project>

Das gesamte Projekt ist so strukturiert, dass verschiedene Unterprojekte in die Solution integriert sind. Das Test-Projekt ist abhängig von den anderen Projekten. Diese Informationen liegen in YourTestProject.csproj und hängen natürlich von den Projektgegebenheiten ab.
Im UsingTask-Tag muss noch der korrekte Pfad zur xunit.runner.msbuild.dll hinterlegt werden.
Wenn nun das Target Test bzw. TestOnly aufgerufen wird, werden beide Unittesting-Frameworks aufgerufen. Wenn eines von beiden fehl schlägt, wird trotzdem weitergermacht (ContinueOnError=”true”).

Weiter geht es: Als erster bzw. zweiter Buildstep wird nun Test bzw. TestOnly als Target aufgerufen. Sieht dann so aus:

Sobald nun der Build ausgeführt wird, werden die Ergebnisse von xUnit und NUnit aggregiert und als ein Ergebnis dargestellt. Das bedeutet also, dass es keinen Unterschied mehr macht, aus welchem Framework die Tests stammen. Sollte nun ein Test fehlschlagen, werden alle anderen Tests trotzdem ausgeführt und es findet kein Abbruch statt. Mission accomplished!

Windows 7 & Lokales System: %PATH% Umgebungsvariable nicht vollständig

Momentan bin ich dabei, TeamCity aufzusetzen und die von uns benutzten Programmiersprachen inklusive der Unit-Testing-Frameworks zu integrieren.
Für PHPUnit gibt es zwei ganz passable XML-Dateien (Ant-Script zum Ausführen von PHPUnit & PHPUnit-Beispielkonfiguration), die an sich auch funktionieren. Ich testete nach einigen Anpassungen die Konfiguration mit Ant und PHPUnit unter meinem Benutzeraccount und war zufrieden. Nachdem ich dann allerdings das Projekt in TeamCity eingebunden hatte, lieferten mir die Log-Dateien (phpunit.txt etc. die im verlinkten Ant-Script hinterlegt sind) folgenden Fehler zurück: “Der Befehl “phpunit.bat” ist entweder falsch geschrieben oder konnte nicht gefunden werden.“.

Höchst seltsam – ich hatte zuerst McAfee in Verdacht, die Ausführung von Batchdateien unter dem Benutzeraccount Lokales System (denn darunter läuft TeamCity standardmäßig) durch eine Richtlinie zu unterbinden. Allerdings funktionierte es mit deaktiviertem Virenscanner immer noch nicht. Also gab ich in der build.xml den absoluten Pfad zur phpunit.bat und siehe da: das klappte.
Der Verdacht lag nun nahe, das phpunit.bat nicht in einem Verzeichnis der Umgebungsvariablen %PATH%  des Systems definiert worden war, sondern in der PATH-Variable meines Benutzeraccounts. Dem war allerdings auch nicht so. phpunit.bat lag im %PATH% des Systems.
Ich fügte in der build.xml zum Testen also Folgendes ein

<exec executable="cmd" failonerror="false" output="${buildDir}/logspath.txt">
	<arg line="/c echo %PATH%" />
</exec>

und bekam in der path.txt einen äußerst kurzen Eintrag mit zwei Ordnern geloggt, die ich so nicht bewusst eingetragen habe.
Ein kurzes Suche in der Registry brachte dann das Ergebnis, dass die %PATH%-Variable unter HKEY_USERS/.DEFAULT/Environment/PATH definiert wird.
Nachdem ich wusste, wonach ich zu suchen hatte, fand ich auch schnell die passende Erklärung.

Zusammenfassung: Dienste, die unter dem Account Lokales System (Local System) laufen und auf eine .bat-, .exe- oder .cmd-Datei zugreifen wollen, die innerhalb der %PATH%-Umbgebungsvariable liegen, können dies nur tun, wenn der Pfad unter HKEY_USERS/.DEFAULT/Environment/PATH eingetragen ist.
Die Optionen Benutzervariablen (logisch) und Systemvariablen haben für den Account unter keine Auswirkungen.

Windows Server 2003 as a central Git repository with Apache 2.2

After some years of working with (and fighting against) Subversion I decided to setup a Git repository for our company. Every developer should decide on their own what Version Control System he wants to use.

Jeremy Skinner wrote an excellent article about hosting a Git repository on Windows which was really helpful. Nevertheless I had to do some customizing.

Using ScriptAliasMatch

This drove me crazy: ScriptAliasMatch seemed to work, but the parameter (the repository argument) was not passed to git-http-backend.exe. Instead of this, I saw that Apache/httpd.exe tried to open c:/program files/git/libexec/git-core/git-http-backend.exe/repositoryname.

Solution: You must enable mod_cgi, otherwise the path argument “…exe/$1″ can not be resolved.

Using Apache 2.2 + SSL

The git client failed to connect with the virtual host which was secured with SSLv3. I received the error message “error:140920DF:SSL routines:SSL3_GET_SERVER_HELLO:parse tlsext“. Anders Brownworth had this problem too, but I fixed it by disabling SSLv3 – which was at least acceptable in our environment. Use this line in your ssl.conf:

SSLProtocol +SSLv2 +SSLv3

Using a network share as repository

Our Subversion and Git repositories are stored on a network share, primarily for backup reasons. The path of the network share must be entered as “\\srv\share” – the doublequotes are important!
We are using the direct network share path and not a network drive, because after a restart of the machine containing the network share, the network drive will be unavailable. Windows marks it as offline and you can not use it longer until you restart the machine or reconnect the network drive manually. Accessing the network share via full UNC path will work as soon as the share is available again.

Running Apache on Windows 7 / Windows Server 2008

I tested the Git environment on Windows 7 (German) machine, and ran into the problem that the git-http-backend.exe could not be found. My fault was that I used “c:/Programme/Git/libexec/git-core/git-http-backend.exe” and notc:/Program Files“. Apache seems not be able to access the junction “c:/Programme“.

If you are running a x64 operating system, you must use c:/Program Files (x86)/Git/libexec/git-core/git-http-backend.exe.

Error “Client denied by server configuration”

Receiving the error “Client denied by server configuration” while setting up Git means in most cases that the access to the Git directory is denied. Take a look at the Apache Wiki and change your Order directives.

Running Apache under a special service account

If you want to run Apache under a special network service account (e.g. webserver@yourdomain.local), you have to keep this in mind.
The service account must have full access to your network share (\srvshare) and the repositories inside of it. Otherwise you will receive errors inside your Apache log like “error: insufficient permission for adding an object to repository database ./objects“.

Using Active Directory authentication/authorization for your Git repository

Authentication and authorization against the Active Directory/LDAP can be easily done with mod_authnz_ldap (mod_ldap prior to Apache 2.2.x):


	SetEnv GIT_PROJECT_ROOT "\\srv\git_repos"
	SetEnv GIT_HTTP_EXPORT_ALL
	ScriptAliasMatch "(?x)^/git/(.*/(HEAD | info/refs | objects/(info/[^/]+ | [0-9a-f]{2}/[0-9a-f]{38} | pack/pack-[0-9a-f]{40}.(pack|idx)) | git-(upload|receive)-pack))$" "c:/program files/git/libexec/git-core/git-http-backend.exe/$1"

	AuthType Basic
	AuthName "LDAP"
	AuthBasicProvider "ldap"
	AuthzLDAPAuthoritative Off
	require valid-user
	require ldap-group CN=security group,OU=your security group container,DC=domain,DC=local

All bare Git repositories inside of \srvgit_repos are available to http://webserver/git/. Every repository access will be authenticated and authorized against the given ldap-group. Authenticated read-only access can be done with LimitExcept directives.
Please note, that the centryl Git repository on your share must have the “sharedRepository = true” option. The option http.receive-pack is not needed, because every access is already authenticated through mod_authnz_ldap.

A working example without Active Directory authentication

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

<Directory "C:/srv/document_root">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

<Directory "c:/Program Files (x86)/git/libexec/git-core/">
    Order allow,deny
    Allow from all
</Directory>

SetEnv GIT_PROJECT_ROOT "C:/repos_git"
SetEnv GIT_HTTP_EXPORT_ALL

# Using x64 operating system
ScriptAliasMatch "(?x)^/git/(.*/(HEAD | info/refs | objects/(info/[^/]+ | [0-9a-f]{2}/[0-9a-f]{38} | pack/pack-[0-9a-f]{40}.(pack|idx)) | git-(upload|receive)-pack))$" "c:/Program Files (x86)/git/libexec/git-core/git-http-backend.exe/$1"

Databases in developers environment – Versioning and migrations

One of the most annoying things I get in touch with software development beside the question How can I format this date is How can I put my database under version control?

If you have not already read K. Scott Allens Database series, do it now.
At first follow his three rules:

  1. Never use a shared database server for development work.
  2. Always Have a Single, Authoritative Source For Your Schema
  3. Always Version Your Database

Requirements

Ask your customer which database and which environment is used. Most of these questions should be answered by a well worked out requirement questionnaire.
Ask for

  • Which operating system is used
  • How is the operating system localized
  • Which database management system is installed
  • Which version and service packs of the DBMS are used
  • Which language version of DBMS is used

It might sound irrelavant but database management systems differs from version to version. Setup your development database server with exactly the same settings your customer uses.

Database environment

The ideal situation is that every developer owns at least two database instances on this server. One for unittests, one for integration tests.
Your continuous integration server should also own his two databases.
So you could have these databases:

  • $PROJECT_$DEVELOPER_unittest
  • $PROJECT_$DEVELOPER_integration
  • $PROJECT_CI_unittest
  • $PROJECT_CI_integration (used for Selenium)
  • $PROJECT_customer_integration

The last database should be used for database dumps which came back from your customer for debugging purposes.

Okay, most of this should be clear. Never use a shared database for development work.

Versioning

Why should you now versionize your database? Because of the same reason you versionize your code: Going back in time and retrace occuring bugs of your customer who uses an old but stable version of your program.

In my opinion there are two approaches to halfway achieve this goal:

  • Store every database object (table, view, stored procedure, function) in its own .sql file
  • Do database migrations like Ruby on Rails does

1 on 1

Storing every database object means that you have a directory structure like

/tables/tbl1.sql
/tables/tbl2.sql
/views/view1.sql
/views/view2.sql
/sps/stored_procedure1.sql
/functions/function1.sql

You get it. Every SQL script contains the CREATE statement for your database object.
This very charmant, because you can track the stuctural changes via your commit log.

The problem begins when you want to migrate your developer integration databases and the database of your customer. Stored procedures or functions can be easily replaced because you can use CREATE OR REPLACE syntax. Views can be replaced by DROP VIEW view1; CREATE VIEW view1 AS…
But – and that’s a big but – you have to compare the current database of your customer with your current table definition and write the ALTER TABLE statements by hand. Not so funny.
Additionally you must delete unused views, stored procedures or functions by hand. Otherwise you’ll get a grave with dead database objects which are no longer used.

I know none database tool which can automatically generate a 100% working update script without any pitfalls. And writing a DBMS independent tool on your own is a lot of work.

1 on n (migrations)

The second approach is to create database migration scripts for every completed database change.
At first the negative aspect: You wil get a lot of files and you can not easily make a git/svn diff of the structural change of your database object because the changes are distributed over many files.

The good news is: You don’t have to write additional ALTER statements on deploy time because they are already there. That means, that you just give your customer your migration SQL scripts, he executes the SQL scripts in a given order and will get the latest database version.

To weaken the problem of diffing different database versions, you can migrate one database to version n and another database to n+1. After that you make an SQL export and use diff (n).sql (n+1).sql.

A few days ago I came to the conclusion that there is no other effective way to achieve the goal, so I decided to further speculate about the second approach.

Implementing migrations

There were some requirements I thought about

  • The database development process and file naming have to be standardized
  • The current installed version of your database must be stored inside the database itself
  • The migration process must be ran easily
  • The migration process must support pre/post scripts
  • The migration process must support different scenarios (deploying on customer, no need for migrations on developer site – just install latest version, different databases)

Standardization was easy. Every project contains a folder named db which contains four sub folders

Directory structure of database migrations

Directory structure of database migrations

  • /coredata contains the core data which every database must contain.
  • /fixtures/[integrationtest|unittest] contains the fixtures which will be used in test cases
  • /handlers can be executed before or after migrations
  • /migrations itself contains the migration scripts

It is important that all files in /coredata, /fixtures and /migrations have an ongoing number. These must not be identical to your SVN revision. It is only important that they can be executed successively.
You can see that that I use the format YYYYMMDD_$DAYVERSION as ongoging identifier.

The database must store their migration version. This is needed for doing migrations. Every migration file greater than $DATABASE_VERSION must be executed.
In my structure above the schema migration table is executed first (20111219_001_schema_migration.sql) and contains only one statement

CREATE TABLE schema_migration (id int not null auto_increment, migration_on DATETIME, version char(20)  NOT NULL, filename LONGTEXT, PRIMARY KEY(id));

After these point I started to write a small prototype migration definition file which supported different scenarios (supporting different databases and servers, migration process, recreate process).

For this definition (see my first draft) I had to write my own parser or use Xtext, so I transformed it into an XML (see my template) sample. It seemed to be ideal for xsd.exe and C#/Visual Studio.
But after I had struggled with serialization problems I took a closer look to the XML file. It looked like… Ant. The processes were more or less the same.
I did a research and came to the conclusion that almost every requirement could be solved with Ant. I implemented a halfway working solution.
The build.xml currently does not insert the migration version after a file is applied. I will fix it ASAP.
Please note, that you need to have js.jar, bsf.jar (both for JavaScript), commons-logging.jar and ant-contrib.jar (for task) in your Ant classpath for using the build.xml.
The build.xml itself is designed for MySQL. You need to customize the build.xml for every DBMS you use. But this should be easy to implement.

Update: Jens Schauder published a post about Tips for Testing Database Code.