Archiv der Kategorie: Tutorial

Willkommen zu meinem nächsten Beitrag direkt aus meinem Fundus merkwürdiger Java-Lösungen für nicht ganz so alltägliche Programmierprobleme. Normalerweise würde ich meine kreativen Fehltritte sicher NIE veröffentlichen, aber dieses Mal mache ich noch eine Ausnahme, da ich glaube, es könnte den einen oder anderen auf die richtige Spur bringen, wenn er nicht weiter weiß.

Kürzlich war ich dabei, einen kleinen Indexer für dies und das zu schreiben, der einmal am Tag zu einer bestimmten Tageszeit Datensätze aus einer Datenbank zieht. Wofür genau spielt hier natürlich keine Rolle. Jedenfalls wollte ich dafür einen TimerTask verwenden, und der wird beispielsweise mit einem Delay (Verzögerung in Millisekunden bis zum ersten Start), und einem Intervall (Verzögerung in Millisekunden für alle darauffolgenden Starts) gefüttert. Randbedingung war, dass der Indexer einmal am Tag um dieselbe Uhrzeit läuft. Diese Uhrzeit soll man hinterlegen können. Den TimerTask-Kram habe ich weggelassen, und den eigentlichen Indexer-Code auch, um das Beispiel möglichst auf das Essentielle zu reduzieren.

Mein Problem war nämlich, dass ich auf Anhieb nicht wusste, wie ich herausfinden könnte, wann der nächstmögliche Zeitpunkt für eine festgelegte Uhrzeit ist, also ob beispielsweise 14:30 Uhr noch am selben Tag ist, oder erst am darauffolgenden Tag. Wieviele Millisekunden muss ich als Verzögerung des initialen Starts angeben, damit der Indexer zur nächsten Gelegenheit um eine bestimmte Uhrzeit losläuft?

Nach einer Weile habe ich mir die obige Lösung für mein simples Problem auserdacht. Die Methode timeToNextIndexTask() gibt einen int-Array zurück, der die Zeit in Stunden und Minuten beinhaltet. Das lässt sich ohne Schwierigkeiten in Millisekunden umrechnen.

Aber wie das so ist, fällt einem schon kurze Zeit später auf, wie sinnlos man sich den Kopf darüber zerbrochen hat, und dass diese Lösung zwar funktioniert, aber eigentlich viel zu aufwändig und der Code ziemlich wackelig ist. Meine Frage lässt sich mit zwei GregorianCalendar-Objekten sehr einfach beantworten:

Kurzgesagt, man legt einen GregorianCalender für den aktuellen Zeitpunkt an, und verstellt einfach die Uhrzeit, behält das Datum jedoch bei. Nun muss man lediglich mit Hilfe der Methode after() bzw. before() vergleichen, ob das verstellte Calendar-Objekt in der Vergangenheit oder in der Zukunft liegt. Falls es in der Vergangenheit liegt, muss man einen Tag dazuzählen, und schon hat man seinen gewünschten Zeitpunkt.

Aus der Kategorie „zu doof für Mathematik, aber intelligent genug um sich trotzdem was einfallen zu lassen“: Heute habe ich mir mal wieder ausgiebig meinen alten Quellcode für meine Spacola-Maussteuerung vom August 2010 angesehen und mich daher auch mal wieder ein bisschen mit den mathematischen Grundlagen (Trigonometrie und Vektorarithmetik) auseinandergesetzt. Ein wenig Schmunzeln musste ich über meine unkonventionelle Lösung eines alten Problems, das ich damals hatte.

Man nehme einen Mauszeiger, der irgendwo in einem karthesischen Koordinatensystem liegt. Nun musste ich herausfinden, welchen Winkel der Richtungsvektor Ursprung->Mauszeiger mit der Y-Achse einschließt. Da ich selten mit der Mathematik einer Meinung war, aber dafür umso mehr gewillt, diese Aufgabe von alleine zu lösen, nahm ich mir seinerzeit Papier und Bleistift zur Hand und fing damit an, mir eine Lösung zu überlegen. Und ich fand einen Algorithmus, mit dem ich den Winkel näherungsweise bestimmen konnte.

Aber zunächst zur idealen und simpelsten Lösung:

Man benötigt im Prinzip nur Delta X und Delta Y zwischen den beiden Punkten, also den Ortsvektor. Anschließend teilt man Delta Y durch den Betrag des Vektors und berechnet aus diesem Wert den Arkus-Kosinus. Das Ergebnis muss dann noch mit 360 multipliziert und wieder durch 2 Pi geteilt werden. Schnell und effizient. Ich bin kein Mathegott, aber ein paar Minuten Suchen hat mich auf die richtige Lösung gebracht.

Witzig fand ich dagegen meinen Algorithmus, der folgendermaßen aussieht:

Ich habe beliebig viele Punkte (im Extremfall 360 Stück, in meinem Fall aber nur 40) auf einem Einheitkreis um den Mittelpunkt generiert und von jedem dieser gedachten Punkte den Abstand zur Mauszeigerkoordinate bestimmt. Mein gedanklicher Ansatz war der, dass der Punkt, der den kürzesten Abstand zur Zielkoordinate hat, mehr oder weniger exakt denselben Winkel zur Y-Achse hat. Mit dem Vorteil, dass ich dessen Winkel (da selbst generiert) genau kannte. Anschließend habe ich noch eine merkwürdige Umrechnung vom Bogenmaß ins Gradmaß durchgeführt. Das Ergebnis war zufriedenstellend.

Für letzteres Codebeispiel schlagt mich bitte nicht. Ich wusste damals schon, dass das nur ein Provisorium ist. Ich weiß noch nicht einmal mehr wofür „targetCnt“ steht.

Zugegeben, meine Lösung ist extrem unperformant und relativ ungenau, aber nichts stärkt das Ego mehr als die Gewissheit, ein Problem selbst und ohne Hilfe bewältigt zu haben, wenn man es sich vorher nicht zugetraut hätte. Wie dem auch sei, heute habe ich meinen provisorischen Algorithmus abgelöst und durch die exakte Berechnung ersetzt.

Nun also doch wieder ein winziger Beitrag zur Java-Welt von mir. Da ich an Projekten arbeite, die mit Hilfe von Apache Log4j Logdateien erstellen, wurde kürzlich die Frage geäußert, wie man denn verhindern kann, dass das System irgendwann mit gigabytegroßen Logs volllaufen wird. Die bisherige „Alles-in-eine-Datei“-Implementierung sollte also ersetzt werden durch eine Lösung mit täglichem Wegspeichern der Logs vom Vortag und nach Möglichkeit direkt als ZIP-Datei, um Platz zu sparen (Plain Text lässt sich bekanntlich unheimlich gut packen). Hinzu kommt dann noch, dass nur die Logdateien der letzten x Tage vorgehalten werden sollten. Ältere Logs müssen automatisch gelöscht werden.

Nun ist Log4j ein sehr mächtiges Tool, das einem wirklich viel Arbeit abnimmt. Der RollingFileAppender z.B. erlaubt es dem Entwickler ganz einfach, Logdateien wegzuspeichern, wenn sie eine bestimmte Dateigröße erreicht haben, und die nächste Logdatei zu beginnen. Wunderbarerweise gibt es zusätzlich sogar die Möglichkeit, mit maxBackupIndex vorzugeben, wieviele Logdateien gleichzeitig gespeichert bleiben sollen. Fast perfekt, aber leider nicht auf periodisches, also z.B. tägliches Wegspeichern anwendbar. Zu meinem Glück gibt es genau für diese Anforderung den DailyRollingFileAppender, so als wäre er direkt für mich gemacht worden. Und jetzt kommt der Brüller: Der DailyRollingFileAppender hat blöderweise wieder keinen maxBackupIndex, er erlaubt also nicht das Vorhalten einer vorgegebenen Anzahl an Logdateien. Super Sache. Wie bekomme ich jetzt DEN FileAppender, der beides kann? Ich bräuchte eine Kombination aus beidem.

Unter http://wiki.apache.org/logging-log4j/DailyRollingFileAppender fand ich die Rettung in Form einer fertigen Klasse: Der DailyMaxRollingFileAppender – tägliches Wegspeichern mit Angabe des maxBackupIndex. Damit wären also zwei von drei Anforderungen erfüllt. Die ZIP-Funktionalität fehlte noch. Meine Mission war es also, einen kleinen FileAppender zu schreiben, der den DailyMaxRollingFileAppender erweitert. Geboren war der DailyMaxRollingZipFileAppender (DAS ist mal ein Klassenname!):

Dieser FileAppender benennt zum Tageswechsel das Logfile mit Angabe des Datums um, verpackt es sofort als handliche ZIP-Datei, und löscht bei Bedarf das älteste Logfile (sofern maxBackupIndex erreicht). Ist ja eigentlich schon fast schade, dass Log4j sowas nicht von Haus aus kann. Falls jemand zufällig mit Log4j zu tun hat und damit was anfangen könnte: Bedient euch.

In related news: Hurra, endlich ein Syntax-Highlighter-Plugin für SuccessDenied. Als Programmierer kann man doch beinahe keinen Blog führen ohne ein solches Plugin.

Java-Tutorials. Vor langer Zeit mal von mir angekündigt. Dann kam zu diesem Thema wieder nichts. Habe mir bisher einfach nicht die Zeit nehmen können, mir etwas zu überlegen, was gut in diese Rubrik passen könnte. Heute möchte ich aber zumindest mit einem sehr kleinen und recht simplen Beitrag beginnen, und wer weiß, vielleicht geht es danach ja sogar weiter. Je nachdem.

Kürzlich stand ich vor einem kleinen Problem, als ich in der Situation war, Daten über Lizenzen aus einem SAP-System auszulesen. Die Information lag in Form einer Tabelle als Vector<Vector<String>> vor. Aus diesem habe ich die benötigten Spalten in zwei String-Arrays rausgeschrieben: Lizenzkennzeichnung und das Gültigkeitsdatum. Diese Einträge sollten dann nach der Datums-Spalte sortiert werden. In diesem Augenblick fiel mir auf, dass ich keine spontane Idee hatte, wie ich zwei Arrays in Abhängigkeit voneinander sortieren sollte, also zwei Spalten gleichzeitig.

Eine Lösung des Problems ist mir dann aber nach kurzem Grübeln eingefallen: Eine passende Datenstruktur musste her, die ihre eigenen Sortierkriterien mitbringt:

Anschließend wollte ich einfach ein Array dieser Datenstruktur erzeugen und auf diesem toString() aufrufen, damit dann rekursiv alle toString()-Methoden seiner Elemente aufgerufen werden. Ach wie doof. So funktioniert das ja gar nicht. Kurzerhand habe ich mir also etwas ausgedacht, das toString() überschreibt, wie ich das gerne hätte:

Ich bin mir zwar relativ sicher, dass hier einige Java-Profis und Design-Pattern-Götter die Hände über dem Kopf zusammenschlagen werden, aber ich glaube ganz so unbrauchbar ist das eigentlich nicht. Und fragt mich schon gar nicht wieso ich da eine anonyme Klasse genommen habe. Vielleicht war ich einfach in der Stimmung dafür. Ich hoffe der Code ist selbsterklärend genug. Das meiste ist sowieso zu speziell, wichtig ist allein die Struktur.

Da ich bekennender Fan der Programmiersprache Java bin (aber nicht ausschließlich), möchte ich den Zeitpunkt der Veröffentlichung des JDK7 nutzen und ankündigen, dass wohl in Zukunft auch ein paar Beiträge über Java-Entwicklung folgen werden. Ich bin noch nicht wirklich sicher worüber genau, aber mir wird da schon was einfallen.

Themen, die ich mir vorstellen könnte, wären meine Erfahrungen mit dem JDK7, außerdem diverse Tricks und Techniken, die ich bei meinen zahlreichen kleineren Gamedev-Versuchen (z.B. Spacola Eclipse) gelernt habe. Womöglich profitiert nochmal jemand davon, dann hätte es sich schon gelohnt, darüber zu schreiben. Wenn alles gut läuft, werden sich die Tutorials nicht auf Java beschränken, sondern auch andere Programmiersprachen umfassen, mit denen ich mich befasse. Auch mathematische Probleme, denen man früher oder später zwangsläufig begegnet, könnte ich dabei ansprechen und einen Lösungsansatz aufzeigen.

Was Spacola Eclipse angeht: Die Entwicklung steht nicht still, aber das Spiel ist derzeit nicht meine Top-Priorität, daher geht es verständlicherweise nur langsam voran. Ich habe definitiv neue Ideen, die ich demnächst umsetzen werde. Zunächst will ich eben einige private und berufliche Dinge geklärt wissen, bevor ich mich wieder mit vollem Eifer meinem Hobbyprojekt widmen kann.