Archiv für die Kategorie 'Softwareentwicklung'

Mai
07

In den letzten Tagen haben wir bei Zalando ein neues, internes Development-Wiki an den Start gebracht. Das dürfte eine häufig wiederkehrende Aufgabe in vielen Unternehmen sein. Daher möchte ich an dieser Stelle meine bisherigen Erfahrungen für ein funktionierendes Setup zum Besten geben.


Wiki-Engine

Ich benutze Mediawiki – kostenlos, Open Source, PHP + MySQL. Läuft auf jedem XAMPP-basierenden Webserver bzw. auf jedem Webserver, der PHP und MySQL unterstützt. XAMPP gibt es wiederum für jedes halbwegs verbreitete Betriebssystem.

Hat man Mediawiki installiert kann man eigentlich schon loslegen. Allerdings gibt es in der Regel bei internen Wikis noch ein paar zusätzliche Bedürfnisse. Dafür stehen diverse Extensions zur Verfügung.

Benutzerregistrierung und Freischaltung
Um eine Registrierung neuer Benutzer mit einer administrativen Freischaltung zu verbinden bietet sich die Extension Confirm user accounts an (http://www.mediawiki.org/wiki/Extension:ConfirmAccount). Von Hause aus besitzt Mediawiki nämlich keinen derartigen Workflow, sondern es kann sich einfach jeder registrieren.

Letzte Änderungen anzeigen
In meinen Augen ein wirklich wichtiges Plugin: News (http://mediawiki.org/wiki/Extension:News). Zeigt die letzten Änderungen im Wiki an, kann auch nach Kategorien filtern und bietet noch etliche weitere Einstellungen. So kann man z.B. eine Startseite bauen, welche die neuesten Einträge pro Abteilung anzeigt.


Alte Artikelversionen löschen

Special:DeleteOldRevisions2 (http://www.mediawiki.org/wiki/Extension:SpecialDeleteOldRevisions2) ermöglicht das Löschen alter Artikelversionen. So kann man z.B. Platz sparen oder sensitive Informationen aus der History eines Artikels verschwinden lassen.


Benutzer Löschen

Möchte man Benutzeraccounts löschen, weil der betroffene Anwender z.B. ausgeschieden ist oder es sich um einen Testaccount handelte, ist man mit User Merge and Delete (http://www.mediawiki.org/wiki/Extension:User_Merge_and_Delete) gut beraten. Die Extension löscht nicht nur den Account. Sie sorgt auch dafür, dass der Datenbestand konsistent bleibt, indem die Beiträge des gelöschten Users einem anderen (im Zweifel anonymen) Benutzer zugeordnet werden.

Syntax Highlighting
Für IT/Entwickler-Wikis, welche Quellcode enthalten, interessant: Syntax Highlighting für bessere Lesbarkeit. Hier habe ich mit SyntaxHighlight GeSHi (http://www.mediawiki.org/wiki/Extension:SyntaxHighlight_GeSHi) gute Erfahrungen gemacht.


Skype Links

Wird Skype im Unternehmen (oder wo auch sonst) eingesetzt bietet sich noch die Skype-Extension (http://www.mediawiki.org/wiki/Extension:Skype) an. Diese muss zwar vor der Installation erst von Hand in eine PHP-Datei kopieren, anschließend kann man aber Skype-Links direkt ins Wiki-Markup einbauen und so Ansprechpartner quasi per Klick erreichen.

Die Extensions laufen übrigens alle problemlos mit der Mediawiki-Software 1.15.3.

Mai
06

Der Trend zu Cloud Computing macht auch vor Entwicklungsumgebungen nicht halt. Aktuelles Beispiel: Das Mozilla Bespin-Projekt.

mozilla_bespin.jpg

Das Bespin-Projekt baut einen webbasierenden, einfach erweiterbaren Code-Editor mittels des HTML5-Standards. Der Editor läuft komplett im Browser . Er kann entweder in eigene Anwendungen eingebettet oder auf einem Server installiert werden. Bei Mozilla Labs gibt es unter https://bespin.mozillalabs.com/ eine Demo-Installation. Der Editor beherrscht bereits Syntax Highlight für diverse Programmiersprachen und die Anbindung an ein Version Control System.

Weitere Beispiele für den “IDE-in-the-cloud” – Trend sind Ecco oder der Zoho Creator.

Feb
13

Falls jemandem “Risiko beim Einsatz von Drittanbieter-Komponenten” nichts sagt dann braucht er sich eigentlich nur die aktuelle Situation bei Toyota anzuschauen: Das klemmende Gaspedal, welches gerade dabei ist, das Markenimage zu ramponieren, kommt vom Zulieferer CTS, einem Drittanbieter also.

In der Produktion Teile von Drittanbietern einzusetzen ist gang und gäbe. Ob in der Autoindustrie oder in der Softwareentwicklung. Die Motivation dahinter ist letzendlich, Zeit und Geld zu sparen. Warum sollte ich das Rad neu erfinden? Der Drittanbieter hat in der Regel bereits beträchtliche Summen in die Perfektion seiner Komponente investiert und meine eigenen Ausgaben würden höchst wahrscheinlich ähnlich hoch sein, bis ich ein vergleichbares Produkt selbst entwickelt habe.

Allerdings birgt die Abhängigkeit von einem externen Anbieter immer Risiken, auch in der Sofwareentwicklung. Beispiel aus der Praxis: Wir entwickeln in meiner Firma ein geschäftskritisches, internes Projektmanagement-System, eine Windows-Client/Server-Anwendung auf Basis von Microsoft .NET. Für die Benutzeroberfläche setzen wir stark auf die telerik RadControls for WinForms, ein (auf den ersten Blick) ziemlich beeindruckendes Framework von UI-Controls. Allerdings stoßen wir hier immer wieder auf ärgerliche Bugs. Für viele lässt sich in der Regel ein schneller Workaround finden, so dass die Investitionen in die RadControls insgesamt noch deutlich unter dem Aufwand für eine entsprechende Eigenentwicklung liegen.

Allerdings gibt es momentan ein Problem, für dass wir keinen Workaround entwickeln können: Die ganze Anwendung stürzt beim Drücken einer Akzent-Taste ab. Da wir ein Übersetzungsdienstleister sind, der u.a. auch Französisch anbietet und mit Kunden und Lieferanten in aller Welt zusammenarbeitet, ist das natürlich kritisch. Mehrmals pro Tag hört man hier im Büro die Flüche der Anwender, die gerade wieder einmal ihren Client per Akzent ins Nirvana befördert haben. Der Fehler ist nun bereits an telerik gemeldet und bestätigt worden, für dessen Behebung gibt es allerdings noch keinen Termin.

Diese Situation ist natürlich extrem unbefriedigend. Was haben wir für Alternativen? Wir könnten auf die RadControls verzichten. Das bedeutet aber entweder extrem hohen Aufwand für die Eigenentwicklung brauchbarer Alternativen oder Investitionen in eine Controlsuite eines Konkurrenzanbieters, einschließlich des Lernaufwandes der Entwickler – und wer garantiert uns, dass diese Control letztendlich fehlerfreier sind? Wir könnten auch den Sourcecode der betroffenen Controls selbst anpassen, der uns aufgrund unserer Lizenz zur Verfügung steht. Damit läuft man aber wieder in Probleme beim Update auf ein neues Release der Controls. Oder wir warten einfach, bis telerik den Fehler behoben hat, gegebenenfalls kann man versuchen, den Prozess durch regelmäßiges Nachfragen beschleunigen.

Alles in allem also nur die Wahl zwischen Pest und Cholera. Hier ist wie gesagt “nur” unser internes PM-System betroffen. Nicht auszudenken, wenn es sich um eine extern releaste Software handeln würde. Die Nutzen und Risiken sollten in dem Fall also tatsächlich gut abgewogen werden.

Mrz
10

Dass Entwickler kreative Menschen sein müssen sieht man an diesem Projekt:

http://mark.michaelis.net/Blog/BuildStatusUsingLavaLampsByKenNichols.aspx

Da lässt doch tatsächlich jemand den Build-Status seines Projekts computergesteuert von verschiedenfarbigen Lavalampen anzeigen.

Sehenswert :)

Feb
26

In letzer Zeit setze ich bei Webprojekten vermehrt Castle ActiveRecord ein, das ja bekanntlich auf NHibernate basiert. An sich eine super Sache, ActiveRecord (respektive NHibernate) ist wirklich ein ziemlich komfortabler O/R-Mapper, der wahre Produktivitäts-Sprünge beschert.

Allerdings gibt es ein kleines Problem bei der Zusammenarbeit von NHibernate und MySQL im Zusammenhang mit dem MySQL Connector/NET: MySQL bietet, warum auch immer, so genannte “Null-DateTimes” an, d.h. Daten der Art 00/00/0000. Außer MySQL kann damit allerdings niemand so recht etwas anfangen, schon gar nicht das System.DateTime-Struct von .NET. Die Eigenintelligenz von NHibernate gibt sich nun zwar alle Mühe, den Inhalt eines nicht-leeres DATE(TIME)-Feld in der DB in ein DateTime zu konvertieren, erntet aber leider eine Exception, da .NET, verständlicher Weise, keine Null-DateTimes akzeptiert.

Eine in meinen Augen schlaue Lösung wäre, die Null-DateTimes wie ein leeres Feld (NULL) zu behandel und dementsprechend statt eines normalen DateTimes ein DateTime?, also ein nullable DateTime für die entsprechende Porperty zu benutzen. Was nun noch fehlt ist die Möglichkeit, das Feld in der Datenbank ohne Exception entsprechend zu konvertieren, d.h. die Intelligenz von NHibernate durch einen eigenen “Konverter” zu erweitern.

Genau für solche Zwecke bietet NHibernate den IUserType an. Eine Klasse, die dieses Interface implementiert, kann für die entsprechenden Properties einer O/R-Klasse als ColumnType angegeben werden. Das Lesen und Schreiben der Property, bzw. dessen Vorbereitung, erledigt dann die IUserType-Implementierung. Im folgenden nun ein Beispiel für eine geeignete Vorgehensweise bezüglich der MySqlDateTime-Problematik:

UserType:

namespace ReneMt.Data
{
public class HibernateNullableDateTime : NHibernate.UserTypes.IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}

public object DeepCopy(object value)
{
return value;
}

public object Disassemble(object value)
{
return DeepCopy(value);
}

public new bool Equals(object x, object y)
{
if (null == x && null == y)
return true;
else if (null != x)
return x.Equals(y);
else
return y.Equals(x);
}

public int GetHashCode(object x)
{
return x.GetHashCode();
}

public bool IsMutable
{
get { return false; }
}

public object NullSafeGet(System.Data.IDataReader rs, string[] names, object owner)
{
if (names.Length == 1)
{
object obj = rs[names[0]];

if (null == obj || obj is DBNull)
return new DateTime?();

if (obj is MySqlDateTime)
{
MySqlDateTime mySqlDateTime = (MySqlDateTime)obj;
if (mySqlDateTime.IsNull)
return new DateTime?();
else
{
if (0 == mySqlDateTime.Day || 0 == mySqlDateTime.Month || 0 == mySqlDateTime.Year)
return new DateTime?();
return new DateTime?(mySqlDateTime.GetDateTime());
}
}
else if (obj is DateTime)
return new DateTime?((DateTime)obj);

throw new InvalidCastException("Can not convert object of type " +
obj.GetType().FullName + " to " + typeof(MySqlDateTime?).FullName);
}
throw new InvalidCastException("Single column expected.");
}

public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
System.Data.IDataParameter param = cmd.Parameters[index] as System.Data.IDataParameter;

param.DbType = System.Data.DbType.DateTime;

if (null == value)
{
param.Value = DBNull.Value;
}
else if (value is MySqlDateTime)
{
MySqlDateTime mySqlDateTime = (MySqlDateTime)value;
if (mySqlDateTime.IsNull)
param.Value = DBNull.Value;
else
param.Value = mySqlDateTime.GetDateTime();
}
else if (value is DateTime)
{
param.Value = value;
}

throw new ArgumentException("Object of type " + typeof(MySqlDateTime).FullName +
" expected, but object of type " + value.GetType().FullName + " has been passed.");
}

public object Replace(object original, object target, object owner)
{
return original;
}

public Type ReturnedType
{
get
{
return typeof(DateTime?);
}
}

public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get
{
return new NHibernate.SqlTypes.SqlType[] { NHibernate.NHibernateUtil.DateTime.SqlType };
}
}

}
}

O/R-Klasse:

using Castle.ActiveRecord;

namespace ReneMt.Data
{
[ActiveRecord("TEST")]
public class NaturalPerson : ActiveRecordBase
{
private int m_id;
private DateTime? m_dayOfBirth;

[Property("dayofbirth",
ColumnType = "ReneMt.Data.HibernateNullableDateTime, ReneMt.Data")]
public DateTime? DayOfBirth
{
get { return m_dayOfBirth; }
set { m_dayOfBirth = value; }
}
}
}

That’s it :)

Dez
19

thering.jpg

Ich arbeite gelegentlich für eine Firma, die vermutlich genau den Code geschrieben hat ;-)

Hoffentlich wird da nie Open Source draus.

Nov
27

entwicklerfrust.jpg

Mitunter hat man mal so richtig Lust, es diesem blöden Visual Studio zu zeigen. Aber da steht es wohl drüber ;)

Könnte aber auch daran liegen, dass wir in der Firma mit einer deutschen Version arbeiten – wobei alleine das oftmals schon die Pest ist.)

Nov
24

Ich sitze hier, am Samstag morgen, und hacke (nach längerer Zeit) diese Zeilen in mein Blog. Was daran jetzt so außergewöhnlich sein soll?

Ich sitze an meinem Laptop, auf dem Linux läuft :-)

Nein, ich werde jetzt nicht der Microsoft-Welt abschwören (zumindest nicht komplett), und auch statt “richtigem” .NET nur noch Mono machen. Die Geschichte geht so:
“Paradigmenwechsel” weiter lesen…

Okt
16

Mitten im Umzugs- und Renovierungs-Stress nach langer Zeit nun wieder mal ein Artikel aus dem .NET-Bereich. Über das Thema bin ich heute bei der Arbeit gestolpert.

Hintergrund: Ich bastle gerade eine Multithreading-Anwendung, eigentlich eher eine Dualthread-Anwendung: Ein Thread für die eigentliche Arbeit, einer für die Benutzeroberfläche inklusive “Abbrechen”-Schaltfläche :-) Die Idee ist altbekannt: Der Workerthread läuft im Hintergrund und soll seine Arbeit abbrechen, wenn der User in der Oberfläche auf den entsprechenden Button klickt. Da die Teilergebnisse seiner Arbeit in meinem Fall bei einem Abbruch uninteressant sind kann der Thread also tatsächlich sofort aufhören zu arbeiten, wenn der User das will.
Die simpelste Möglichkeit, ohne irgendwelche Flags als synchronisierte Membervariablen o.ä. zu implementieren, ist ein Aufruf von Thread.Abort. Der Aufruf führt dazu, dass – sowohl im aufrufenden als auch im Workerthread – eine ThreadAbortException geworfen und der Thread (in der Regel) unverzüglich termniniert wird.

Die ThreadAbortException kann allerdings etwas lästig werden, sie ist nämlich nicht tot zu kriegen. Wie die MSDN dazu ausführt:

"ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. "

.

Das kann zur Folge haben, dass die ThreadAbortException an mehreren Stellen in diversen Exception-Handlern hängen bleibt, und der Anwender z.B. in einer MessageBox darüber benachrichtigt wird. Folglich sind diverse Sonderbehandlungen notwendig.

Allerdings ergibt sich hier eine interessante Möglichkeit: Man kann die ThreadAbortException auch dazu benutzen, den WorkerThread davon zu benachrichtigen, dass er sich beenden soll. Möglich wird das, weil die Exception ja auch in ihm (also in seinem Thread) geworfen wird – und dort auch gefangen werden kann. So kann z.B. eine aufgerufene Methode sauber null zurückliefern. Wenn da nicht dass Problem wäre, dass die Exception immer erneut geworfen wird.

Abhilfe schafft hier die Methode Thread.ResetAbort . Diese bricht einen Abort-Request für einen Thread wieder ab, so dass dieser im Prinzip völlig desinteressiert weiterlaufen könnte – oder sich aber auch selbst beenden. In beiden Fällen verschwindet auch die ThreadAbortException wieder. Somit ließe sich ein "sauberer" Abbruch des WorkerThreads nach folgendem Muster realisieren:


class MainForm
{
  protected void btnCancelWorkerThread_Click(object sender, EventArgs e)
  {
    m_workerThread.Abort;
  }
}

class HeavyWorker
{
  public byte[] DoHeavyWork(string params)
  {
    try
    {
      return InternalHeavyWork(params);
    }
    catch (ThreadAbortExcpetion)
    {
      Thread.ResetAbort();
      return null;
    }
  }
}

Jul
26

Man stelle sich vor, man habe eine Menge, sagen wir 15, Dateien nach einen Begriff zu durchsuchen. Manuell, weil aus verschiedenen Gründen eine automatisierte Suche nicht möglich ist. Das Öffnen einer Datei dauert dabei gute 20 Sekunden. Also öffnet man die ersten drei Dateien, um festzustellen, dass der Begriff hier nicht enthalten ist. Dann die letzten drei, hier ist er natürlich auch nicht zu finden. Resignierend durchsucht man darauf von vorn angefangen alle verbleibenden Dateien.

Quizfrage: In welcher versteckt sich der Begriff?

Antwort: Richtig, in der viertletzten…

vBulletin statistics