Samstag, 7. November 2009
Wie komme ich zu der Ehre?
Sonntag, 18. Oktober 2009
.Net Open Space 2009
Freitag, 2. Oktober 2009
Update zu "Bug in NHibernate 2.1 die Dritte"
Update zu "Bug in NHibernate 2.1 die Zweite"
Richard Brown hat mich darauf hingewiesen, dass das Setzen des Bags im Parent auf inverse="true" das Problem behebt.
Aber seltsam ist das Verhalten doch trozdem. Ich frage mich, ob das so gedacht war.
Samstag, 5. September 2009
NHibernate 2.1 short codereview
Da ich in letzter Zeit vermehrt in den Sourcecode von NHibernate reingeschaut habe, sind doch ein paar Stellen aufgefallen die ich einfach grottig fand. Eine von diesen werde ich kurz erläutern.
In der Klasse Configuration gibt es die Methode BuildSessionFactory(). Diese sieht momentan so aus:
public ISessionFactory BuildSessionFactory()
{
ConfigureProxyFactoryFactory();
SecondPassCompile();
Validate();
Environment.VerifyProperties(properties);
Settings settings = BuildSettings();
// Ok, don't need schemas anymore, so free them
Schemas = null;
return new SessionFactoryImpl(this, mapping, settings, GetInitializedEventListeners());
}
Die rot markierte Zeile ist das Interessante an der Sache. Wenn ich so in den Qullcode schaue, würde ich ja denken hier werden Properties überprüft. Wenn wir uns nun mal die Methode anschauen stellen wir fest ...
/// <summary>
/// Issue warnings to user when any obsolete property names are used.
/// </summary>
/// <param name="props"></param>
/// <returns></returns>
public static void VerifyProperties(IDictionary<string, string> props) {}
sie macht garnichts!
Sehr cool finde ich den Kommentar.
Donnerstag, 3. September 2009
.NET Open Space 2009
Ich freue mich auf jeden Fall darauf. Wir werden uns dort sehen.
Mittwoch, 2. September 2009
Bug in NHibernate 2.1 die Dritte
Joe Smith beschreibt zuerst den Fehler, den ich schon mit einem Patch behoben hatte. Doch dann stellte sich heraus, dass es ein weiteres Problem für Oracle Datenbanken im Zusammenhang mit der LikeExpression zu geben scheint.
Oracle macht einen unterschied, ob man "like 'UPPER%'" oder "like 'lower%' schreibt. Bei mir tritt das Problem nicht auf, da ich momentan entweder mit MySQL oder MS-SQL arbeite und dort like case insensitive ist.
Nach ca. 30 Minuten suchen im NHibernate Quelltext habe ich das Problem gefunden. Wenn die LikeExpression auf ignoreCase geschaltet wird, wird nur für die geprüfte Spalte lower() aufgerufen jedoch nicht für den übergebenen Parameter. Ich habe für Joe auf die schnelle einen Patch fertig gemacht indem lower() auch für den Parameter aufgerufen wird. Diesen habe ich auch an NHibernate-JIRA geschickt, mit der Anmerkung, dass das gleiche Problem auch in der InsensitiveLikeExpression besteht (warum gibt es eigentlich zwei unabhängige Klassen und nicht eine, die auf die Andere aufbaut?).
Eigentlich dachte ich, damit sei die Sache erst mal erledigt aber Fabio Maulo ist wohl nicht der Ansicht, dass dieses Verhalten ein Bug sei. Schließlich sei der Entwickler seiner Meinung nach selbst für den Parameter verantwortlich und nicht NHibernate.
Das macht für mich allerdings überhaupt keinen Sinn.
- Wenn ich NHibernate mitteile, dass es case insensitive arbeiten soll, dann ist mir doch egal wie der Parameter aussieht, es soll einfach case insensitive arbeiten.
- Das Verhalten vor NH2.1 scheint genau so gewesen zu sein, wie von Joe (und mir) eigentlich erwartet. Von daher soll es sich doch auch mit NH2.1 weiterhin so verhalten.
- Wenn ich mich selbst um den Parameter kümmern muss, müsste ich auch wissen darüber haben, wie NHibernate arbeitet. Ich weiß erstmal nicht, dass lower() angewendet wird. Könnte ja auch sein das jemand gerne upper() verwendet. Ich weiß es nicht. Dies ist ein reines Implementierungsdetail und als Benutzer sollte es mir vollkommen egal sein können.
Sonntag, 30. August 2009
Bug in NHibernate 2.1 die Zweite
public class Parent{
public long Id{get;set;}
public string Name{get;set;}
private IList<Child> children;
}
<class name="Parent">
<id name="Id">
<generator class="native" />
</ id>
<property name ="Name" />
<bag name ="children" access="field">
<key column="ParentId">
<one-to-many class="Child" />
</ bag>
</ class>
public class Child{
public long Id{get;set;}
public string Text{get;set}
public long ParentId{get;set}
}
<class name="Child" >
<id name="Id" >
<generator class="native" />
</ id>
<property name="Text" />
<property name="ParentId" />
</ class>Es ist Absicht, dass ein Child nur die ParentId kennt und nicht den Parent direkt. Es ist ebenfalls Absicht, dass children ein private field ist ohne irgend einen Zugriff, da ich diese Variable zu NH 1.2 Zeiten ausschlisslich für Query-Zwecke brauchte. Es gab nun folgende Criteria:using(var session = sessionFactory.OpenSession){
using(var transaction = session.BeginTransaction){
session.CreateCriteria<Parent>()
.Add(Restrictions.IsNotEmpty("children"))
.List<Parent>();
transaction.Commit();
}
}Diese Query funktioniert wunderbar. Was mich stört, ist die unnüze Variable children. Durch das neue none Feature, was eigentlich genau dafür gemacht wurde, dachte ich diese Variable elemenieren zu können. Also löschte ich nun children aus der Parent-Klasse und änderte das Parent-Mapping:<class name="Parent">Wenn ich die Query erneut ausführe, passiert jetzt etwas seltsames. Aus irgend einem Grund versucht NHibernate nun die ParentId im Child-Objekt auf NULL zu setzen. Mir ist schleierhaft wie das zu stande kommt. Auch cascade expliziet auf none zu schalten macht keinen Unterschied.
<id name="Id">
<generator class="native" />
</ id>
<property name ="Name" />
<bag name ="children" access="none">
<key column="ParentId">
<one-to-many class="Child" />
</ bag>
</ class>
Ich haben den Fehler im NHibernate JIRA gemeldet und zähneknirschend das private field children wieder in die Parent-Klasse eingebunden.
Montag, 24. August 2009
Clean Code Developer (CCD)
Mit jedem Rang arbeitet man daran bestimmte Prinzipien und Praktiken während der Softwareentwicklung anzuwenden. Die ersten Ränge sind so aufgebaut, dass man die Praktiken für sich alleine erarbeiten kann. Dazu zählen einfache Prinzipien wie DRY (Don't repead your self) und KISS (Keep it simple and stupid) Bei späteren Rängen sollte das gesamte Team und letztendlich auch das Management beteiligt sein, da hier zum Teil auch Infrastruktur geschaffen werden muss. Als Beispiel sei Continuous Integration und Iterative Entwicklung genannt.
Ich persönlich arbeite gerade am gelben Grad und bin froh darüber, dass ich viele der Praktiken schon kenne und anwende. Somit kann das, was ich bisher gemacht habe, nicht komplett falsch gewesen sein.
Also ... keep the clean code!!!
Samstag, 22. August 2009
Bug in NHibernate 2.1 die Erste
Leider enthielt NH 2.1 nicht nur neue Features und Bugfixes, sondern auch tolle neue Bugs. So lieferten auf einmal Suchfunktionen der Software keine Ergebnisse mehr, die eine QBE verwendeten.
Also habe ich mir den Quelltext von NHibernate runtergeladen und selbst nach dem Fehler gesucht. Anscheinend wurde in der Example Klasse vergessen beim "EnableLike" den MatchMode zu übergeben. Ich habe den Bugfix an NHibernate JIRA geschickt, der in Version 2.1.1 und 3.x enthalten sein wird.
Beim schauen in den Quelltext sind mir ein paar Sachen aufgefallen wo es mich doch etwas gruselt. Aber dazu später mehr.
Sonntag, 8. März 2009
Behaviour-Driven Development (BDD) and Syntactic Shugar
Man hätte dann etwas in folgender Form:
When the application was told to register a new user
-a new user will be created
-a register email will be send
-a success message will be shown
Dahinter würden sich nun entsprechende Methodenaufrufe verbergen die verifizieren ob die Anforderungen eingehalten werden. Das hat den Vorteil, dass man noch nach einer langen Zeit genau weiss, wie sich das Objekt verhalten sollte. Der andere Vorteil ist, dass man dem der die Anforderungen stellt (z. B. der Kunde oder Chef), die Tests als Protokoll zeigen kann.
Ich komme jetzt gerade auf das Thema, weil ich dabei bin Anforderungen für mein Eingangsportal zu sammeln. Jetzt wollte ich anfangen den ersten BDD-Test für mein Projekt zu schreiben. Da ist mir aufgefallen, dass ich noch keine BDD-Lib habe um eine schöne Lesbare Syntax hin zu bekommen. Vor allem für das Setup und Asserts von gemockten Objekten habe ich mir überlegt wie kann man eine schöne lesbare Syntax hinbekommen. In Anlehnung an schon bekannte Frameworks habe ich mir für das Setup Folgende Syntax überlegt und in einem standard Unit-Test spezifiziert:
[Test]
public void when_a_mockobject_was_told_to_return_a_value_it_will_return_it() {
var mockRepository = new MockRepository();
var testobject = mockRepository.Stub<ITestDependency>();
When.The(testobject).was_told_to(x => x.DoSomething(4)).Then.Return("success");
mockRepository.ReplayAll();
string result = testobject.DoSomething(4);
result.will_be_equal_to("success");
mockRepository.VerifyAll();
}
public interface ITestDependency
{
string DoSomething(int value);
}
Also ich finde, dass die Zeile
'When.The(testobject).was_told_to(x => x.DoSomething(4)).Then.Return("success");'
schon sehr fluffig aussieht. Was man nicht alles für Spielereien machen kann :-)
Sonntag, 1. März 2009
Refactoring die Zweite
Durch meinen guten Kollegen Stefan Lieser bin ich auf diese Seite gestoßen. Dort geht es darum, eine Enumeration nicht als enum zu verwenden, sondern eine Klasse zu bauen mit statischen Enumerations-Objekten. Die Klasse lässt sich verwenden wie eine normale Enumeration hat aber den Vorteil, dass man noch zusätzliche Attribute hinterlegen kann. Ich fand die Idee sehr interessant und habe mir überlegt, ob ich das für mich irgend wie einsetzen kann. Ursprünglich hatte ich für die Bewegungsrichtungen der Kugeln eine Enumeration mit allen möglichen Richtungen.
1: public enum Direction {
2: UpperLeft, 3: UpperRight, 4: Left, 5: Right, 6: LowerLeft, 7: LowerRight 8: } Das Problem daran ist, dass ich zu jeder Richtung wissen muss, ob die x/y Koordinate hoch oder runter gezählt werden muss oder ob sie gleich bleibt. Desweiteren muss ich zu einer Richtung die Gegenrichtung kennen.
Der erste Ansatz war eine Methode der den inkrementellen Wert für die Richtung über ein switch-case zurück gibt und ein Dictionary, dass als Schlüssel eine Direction hat und als Value die entsprechende Gegenrichtung.Viel schöner finde ich es, dass über die Enumeration-Klasse zu machen.
Hier die neue Implementierung der Direction:
1: public class Direction : Enumeration {
2: private readonly Func<Direction> inverseProjection;
3: public int Xincrement { get; private set; }
4: public int Yincrement { get; private set; }
5: 6: public Direction InverseDirection { get { return inverseProjection(); } }
7: 8: public Direction(string displayName, int xincrement, int yincrement, Func<Direction> inverseProjection)
9: : base(displayName) {
10: this.inverseProjection = inverseProjection;
11: Xincrement = xincrement; 12: Yincrement = yincrement; 13: } 14: 15: public static readonly Direction UpperLeft = new Direction("UpperLeft",-1,-1, () => LowerRight);
16: public static readonly Direction UpperRight = new Direction("UpperRight",0,-1, () => LowerLeft);
17: public static readonly Direction Left = new Direction("Left", -1,0, () => Right);
18: public static readonly Direction Right = new Direction("Right", 1, 0, () => Left);
19: public static readonly Direction LowerLeft = new Direction("LowerLeft", 0, 1, () => UpperRight);
20: public static readonly Direction LowerRight = new Direction("LowerRight", 1, 1, () => UpperLeft);
21: }Wie man sehen kann, wurde der Klasse X und Y Inkrement hinzugefügt. Die Erweiterung ist nicht sehr aufregend, da ich einfach im Konstruktor die entsprechende Konstante übergebe.
Viel interessanter ist die InverseDirection. Im ersten Versuch habe ich einfach im Konstruktor die entsprechende Gegenrichtung übergeben. Leider legt sich das Programm direkt auf die Nase, da zum Zeitpunkt der Zuweisung die Gegenrichtung noch NULL ist. Deshalb wird hier die Lambda Expression verwendet.Was man noch refactoren könnte ist, dass im Konstruktor keine nackten Zahlen mehr übergeben werden. Das Problem ist, dass nach mehreren Monaten keiner mehr weiß was “-1” bedeutet.
Das schöne ist, dass ich meine switch-case Anweisung und das Dictionary nun wegwerfen kann und der Code wesentlich sauberer aussieht. Im Gegensatz zu
1: private Ball GetNextBall(Direction dir, int posx, int posy) {
2: int xinc, yinc;
3: GetIncrement(dir, out xinc, out yinc);
4: if (IsOutOfField(posx + xinc, posy + yinc)) {
5: return null;
6: } 7: return GetBall(posx + xinc, posy + xinc);
8: } 1: private Ball GetNextBall(Direction dir, int posx, int posy) {
2: if (IsOutOfField(posx + dir.Xincrement, posy + dir.Yincrement)) {
3: return null;
4: } 5: return GetBall(posx + dir.Xincrement, posy + dir.Yincrement);
6: }1: private void GetIncrement(Direction dir, out int xinc, out int yinc){
2: switch(dir){
3: case Direction.UpperLeft:
4: xinc = -1; 5: yinc = -1; 6: break;
7: ...Sonntag, 8. Februar 2009
Refactoring die Erste
Hier noch einmal die Methode vom letzten mal:
Sofort ist mir aufgefallen, dass die erste Bedingung der for-Schleife überflüssig ist. Generell kann ich ruhig jede Kugel prüfen und damit reicht die zweite Bedingung aus.
public bool IsInRow(List<Ball> balls) {
if (balls.Count <= 1) {
return true;
}
int minX = balls.Min(ball => ball.PosX);
int minY = balls.Min(ball => ball.PosY);
int dx = balls.Max(ball => ball.PosX) - minX;
int dy = balls.Max(ball => ball.PosY) - minY;
int xinc = Convert.ToInt32(dx > 0);
int yinc = Convert.ToInt32(dy > 0);
for (int i = 0, x = 0, y = 0; (x <= dx && y <= dy) ||
i < balls.Count; x += xinc, y += yinc, i++) {
if (balls.Find(ball => ball.PosX == (x + minX) &&
ball.PosY == (y + minY)) == null) {
return false;
}
}
return true;
}
...Als nächstes kann ich die Suche nach einer Kugel an der jeweiligen Position durch eine Methode austauschen.
for (int i = 0, x = 0, y = 0; i < balls.Count;
x += xinc, y += yinc, i++) {
...
}
...Da ich den Test so oft durchführen muss, wie Kugeln in der Liste sind kann ich die Iteration auch über eine foreach-Schleife machen. So brauche ich selbst nicht mehr auf das Ende der Liste prüfen.
for (int i = 0, x = 0, y = 0; i < balls.Count;
x += xinc, y += yinc, i++) {
if (ThereIsNoBall(balls, minX + x, minY + y)) {
return false;
}
}
...
private bool ThereIsNoBall(List<Ball> balls) {
return balls.Find(ball => ball.PosX == x && ball.PosY == y) == null;
}
...
int x = 0;
int y = 0;
foreach (Ball ball in balls) {
if (ThereIsNoBall(balls, x + minX, y + minY)) {
return false;
}
x += xinc;
y += yinc;
}
...
Im nächsten Schritt werde ich zwei Dinge verändern. Zum einen werden die Variablen dx und dy nur an einer stelle verwendet. Also kann ich mir diese Variable auch komplett sparen. Als nächtes werde ich noch die index-Variablen x und y entfernen, da ich auch direkt minX und minY hochzählen kann. Sinnvollerweise benenne ich minX und minY gleich in posX und posY um.
...Da jetzt die Zeile um x-/y-inc zu ermitteln etwas zu kryptisch aussieht, werde ich das noch in eine Methode auslagern. Ausserdem passiert im wesentlichen in beiden Zeilen das Gleiche, was ebenfalls für eine neue Methode sprechen würde.
int posX = balls.Min(ball => ball.PosX);
int posY = balls.Min(ball => ball.PosY);
int xinc = Convert.ToInt32((balls.Max(ball => ball.PosX) - posX) > 0);
int yinc = Convert.ToInt32((balls.Max(ball => ball.PosY) - posY) > 0);
foreach (Ball ball in balls) {
if (ThereIsNoBall(balls, posX, posY)) {
return false;
}
posX += xinc;
posY += yinc;
}
...
...Als letztes werde ich noch überall das List
int xinc = GetStepSize(balls, posX, ball => ball.PosX);
int yinc = GetStepSize(balls, posY, ball => ball.PosY);
...
private int GetStepSize(IEnumerable<Ball> balls, int pos,
Func<Ball, bool> positionProjection) {
return Convert.ToInt32((balls.Max(positionProjection) - pos) > 0);
}
public bool IsInRow(IEnumerable<Ball> balls) {
if (balls.Count() <= 1) {
return true;
}
int posX = balls.Min(ball => ball.PosX);
int posY = balls.Min(ball => ball.PosY);
int xinc = GetStepSize(balls, posX, ball => ball.PosX);
int yinc = GetStepSize(balls, posY, ball => ball.PosY);
foreach (Ball ball in balls) {
if (ThereIsNoBall(balls, posX, posY)) {
return false;
}
posX += xinc;
posY += yinc;
}
return true;
}
private int GetStepSize(IEnumerable<ball> balls, int pos,
Func<Ball, bool> positionProjection) {
return Convert.ToInt32((balls.Max(positionProjection) - pos) > 0);
}
private bool ThereIsNoBall(IEnumerable<Ball> balls, int x, int y) {
return balls.FirstOrDefault(ball => ball.PosX == x && ball.PosY == y) == null;
}
Ansich finde ich, dass der Code so wensentlich besser zu lesen ist als vorher. Was man vielleicht noch machen könnte wäre, die x/y Werte zu einem Point zusammen zu fassen. Ich denke aber, dass es fürs Erste reichen sollte.
Wie gut, dass ich vorher Tests geschrieben habe und nun überprüfen kann, ob mein Refactoring irgend welche Auswirkungen auf das Verhalten hat :-)
Mittwoch, 4. Februar 2009
Das Spiel Abalone und die Algorithmen dahinter
Als ich mir Abalone als Spiel aussuchte dachte ich mir "Hmm die Regeln sind ja recht einfach. Das sollte nicht sonderlich schwer sein umzusetzen". Als ich mit der Implementierung der Spielmechanik begonnen hatte, hat sich meine Annahme als Irrtum herausgestellt. Es fängt mit so einfachen Dingen an wie "Liegen die gewählten Kugeln in einer Reihe?" und hört bei "Wo liegen die Kugeln nach dem Verschieben?" auf.
Als Beispiel erläutere ich, wie ich erkenne, ob Kugeln in einer Reihe liegen. Generell benötige ich diese Funktionalität beim Selektieren der einzelnen Kugeln und beim Verschieben einer Reihe.
Ich betrachte in der Methode immer eine Menge von Kugeln. Da ich mein Spielfeld als zweidimensionales Array darstelle haben meine Kugeln eine X und eine Y Koordinate.
Als erste Variante habe ich einfach versucht über die Kugeln zu iterieren um zu schauen, ob die nachfolgende Kugel in Reihe zu der vorherigen Kugel liegt. Der Versuch schlug schon nach erstem Probieren Fehl. Erstens weiß ich nicht in welcher Richtung die Kugeln liegen und zweitens können die Kugeln völlig unsortiert sein. Es könnte also passieren, dass ich drei Kugeln in Reihe habe die durchnummeriert sind aber in der Iteration die Reihenfolge 1:3:2 haben. Somit würde diese Variante spätestens bei der zweiten Kugel in der Liste davon ausgehen, dass es keine Reihe ist.
Ich benötige also eine Möglichkeit die wahrscheinliche Richtung meiner Kugeln zu bestimmen, so dass ich später nur noch die Position in die Richtung verschieben und schauen muss, ob dort eine Kugel gibt.
Hier also der Algorithmus:
- Bilde die Differenz zwischen der Maximalen und der Minimalen X und Y Koordinate die die zu Prüfenden Kugeln haben.
- Ist die Differenz größer 0 so muss später bei der Iteration die X / Y Koordinate hochgezählt werden.
- Iteriere so über das Spielfeld, dass bei der Kugel mit geringstem X, Y Wert begonnen wird
- Suche in der Liste nach Kugeln die diese Koordinate haben
- Inkrementiere die X / Y Koordinate je nach Ergebnis in Schritt 2.
- Wiederhole den Vorgang bis entweder eine Kugel nicht gefunden wird (bedeutet dass die Kugeln nicht in Reihe liegen) oder alle Kugeln eine der erwarteten Koordinaten haben
public bool IsInRow(List<Ball> balls) {Irgend wie müsste ich das Ganze nochmal Refactorn. Die for-Schleife ist mir noch eine Nummer zu kryptisch. Als ich nach etwa einem 3/4 Jahr nochmal draufgeschaut habe dachte ich mir "Huch was hast du denn da verbrochen" :-)
if (balls.Count <= 1) {
return true;
}
//finde kugel mit kleinstem x und kleinstem y
int minX = balls.Min(ball => ball.PosX);
int minY = balls.Min(ball => ball.PosY);
int dx = balls.Max(ball => ball.PosX) - minX;
int dy = balls.Max(ball => ball.PosY) - minY;
int xinc = Convert.ToInt32(dx > 0);
int yinc = Convert.ToInt32(dy > 0);
for (int i = 0, x = 0, y = 0; (x <= dx && y <= dy) ||
i < balls.Count; x += xinc, y += yinc, i++) {
if (balls.Find(ball => ball.PosX == (x + minX) &&
ball.PosY == (y + minY)) == null) {
return false;
}
}
return true;
}
PS: Hat jemand eine Ahnung, wie man hier Quelltexte vernünftig darstellen kann?
Samstag, 10. Januar 2009
Mein Aktuelles Webprojekt
wie im letzten Post erwähnt, werde ich hier Sachen zu meinen Projekten vorstellen. Bei meinem aktuellen Projekt geht es allgemein um Spiele die man über das Web gegeneinander spielt. Mir ging es darum, dass man möglichst keine zusätzliche Software wie Silverlight oder Flash benötigt. Eignen würden sich hierfür Rundenbasierte spiele. Um erst einmal mit einem möglichst einfachen Spiel zu beginnen werde ich versuchen das gute alte Abalone umzusetzen. Tatsächlich habe ich schon eine Version die Theoretisch funktioniert. Nur läuft diese viel zu langsam, ist so gut wie ungetestet was automatisierte Tests angeht und die Struktur gefällt mir nicht.
Eines der Hauptprobleme das bei einem rundenbasierten Spiel entsteht ist, dass ich vom Server keine Benachrichtigung an den Client schicken kann. Die einzige mir bekannte Möglichkeit besteht darin, den Status beim Server immer wieder abzufragen.
Das gesamte Projekt wird in .NET entwickelt. Für das Userinterface werde ich vermutlich das ASP.NET MVC Framework verwenden.
Ich denke das ich in den nächsten Wochen ein paar Sachen vorstellen kann.
Freitag, 2. Januar 2009
25C3
In diesem Blog will ich über laufende Projekte und allgemein über Softwareentwicklung schreiben. Bin mal gespannt was da in den nächsten Monaten zusammen kommt.
Da ich auf dem 25C3 letztes Jahr war, will ich dies als Anlass für meinen ersten Post nehmen. Es war eine sehr interessante Veranstaltung. Es gab viel zu lernen über diverse Sicherheitslücken und Rechtliches.
Im Zusammenhang mit der Online-Durchsuchung und Durchsuchungen allgemein fand ich es sehr beunruhigend, dass nach Aussage von Ulf Buermeyer, Richter am Landgericht Berlin, ein Richter im Schnitt nur 2 Minuten Zeit hat zu entscheiden, ob er eine Durchsuchung genemigen soll oder nicht. Wird die Genehmigung nicht erteilt, muss er dies sogar noch begründen. Andersherum wäre es mir lieber, da die richterliche Genehmigung den Bürger eigentlich ja schützen soll.
Was ich leider etwas negativ an dem 25C3 anmerken muss ist, dass Politik ein sehr stark vertretenes Thema war. Auch wenn Themen wie Datenschutz und Online-Durchsuchung sehr wichtig sind, hätte ich mir etwas mehr zu Entwicklung und Hacking gewünscht.
Das Highlight zum Ende des Kongresses war für mich der Hack einer NPD Seite wie auf Heise.de zu lesen ist. Wer kommt eigentlich immer auf solche Ideen?