BigBasti's Blog About Me & my Digital Lifestyle

18Feb/100

Interessantes aus der Java Welt Teil 2

In diesem Artikel möchte ich kurz was über statische Objekte in Java erzählen. Das ist ein sehr interessantes Thema, dass oft zu unerwarteten Fehlern führen kann.

Ich möchte hier zunächst mit einem kleinem Beispiel anfangen. Lest euch diesen kurzen Code durch und schaut mal ob ihr sagen könnt ob dieser kompiliert werden kann, wenn nein warum nicht und wenn ja was kommt raus?

Ernsthaft, nehmt euch eine Minute Zeit:


public class Demo {

	static String text = "Ich bin die Klasse Demo";

	public Demo getInstance(){
		System.out.println("Ich bin die Methode");
		return null;
	}

	public static void main(String [] args){
		System.out.println(new Demo().getInstance().text);
	}
}

Lasst uns den Code mal gemeinsam durchgehen. Wir deklarieren eine statische Klassenvariable vom Typ String und initialisieren diese auch gleich, desweiteren haben wir eine Methode getInstance() die einen Text auf der Konsole ausgibt und dann den Wert null zurückgibt!

Als letztes folgt die main()-Methode die das Programm ausführt, schauen wir uns diese etwas genauer an, denn hier ist der interessante Teil!

In der Main()-Methode wird eine Instanz der eigenen Klasse angelegt und dann die Funktion getInstance() ausgeführt, soweit alles ok, aber nun gibt die getInstance-Methode null als Rückgabewert zurück, also wird hier eine komplett "leere" Klasse zurückgegeben, und aus dieser "leeren" Klasse lesen wir nun die Variable text aus und geben diese auf Der Konsole aus.

Kann das Funktionieren? Kann ich eine Variable aus einer nicht instanzierten "leeren" Klasse benutzen? - Ja das können wir. Dementsprechend erhalten wir folgendes im Konsolen Fenster:


Ich bin die Methode
Ich bin die Klasse Demo

Doch warum ist das so? Dazu muss man wissen, dass es so etwas wie das Klasseninventar und Objektinventar gibt. Alle Statischen Objekte gehören zum Klasseninventar und alle nicht statischen zum Objektinventar.

Wo ist der Unterschied? Worin unterscheidet sich eine Klasse von einem Objekt? - Ein Objekt entsteht aus einer Klasse, daher kann es immer nur eine einzige Klasse geben, aber mehrere Objekte.

Schauen wir unsere Demo-Klasse mal an. Wir können beliebig viele Demo-Objekte aus der Demo-Klasse erstellen aber die Klasse selbst bleibt einzigartig.

Und genau das selbe geschieht auch mit den Objekten des Klasseninventars, alle diese Objekte sind nur ein einziges Mal vorhanden! Das heist, auch wenn man 100 Objekte vom Typ Demo anlegt hat man trotzdem nur eine einzige text-Variable! - Man braucht eine Weile um das zu schlucken, das gebe ich zu!

Aber vielleicht wird es etwas ersichtlicher an einem Beispiel. Nehmen wir an, wir programmieren mit Java eine Besucherzähler Klasse. Diese Klasse wird jedes Mal erzeugt wenn ein Benutzer eine Webseite aufruft und erhöht den Wert einer Zähler Variable um 1.

Da aber jeder Besucher seine eigene Instanz der Klasse bildet hat jeder Benutzer sein eigenes Besucherzähler Objekt. Wenn die Variable, die für das Zählen der Besucher zuständig ist nicht Statisch ist, hat jeder Benutzer eine neue - eigene - Variable die erhöht wird, und wenn mehrere Benutzer zur selben Zeit auf der Seite sind erhöhen Sie NUR ihre eigene Variable aus dem Objektinventar und nicht die allgemeine Variable.

Man muss hier also die Zähler Variable statisch machen, damit alle Objekte, egal wie viele es sind, immer auf die selbe Variable zugreifen, nämlich die aus dem Klasseninventar!

Kommen wir zurück auf das obige Beispiel, denn dieses funktioniert nur, da die text-Variable Statisch ist und somit im Klasseninventar liegt. Entfernt man das Wörtchen "static" vor der Definition, wird das Programm mit einer NullPointerException enden:


Ich bin die Methode
Exception in thread "main" java.lang.NullPointerException
	at Twitter.Demo.main(Demo.java:11)

Denn nun liegt die text-Variable im Objektinventar und da das Objekt mit null angegeben ist, also "leer" ist endet der Aufruf in einer Exception.

Soviel dazu, ich hoffe diese kleine Theoriestunde hat euch gefallen. Ich würde mich über Feedback (und evtl. Verbesserungen) freuen, wollt ihr zukünftig mehr solcher Themen hier haben?

VN:F [1.8.4_1055]
Rating: 10.0/10 (1 vote cast)
29Jan/101

Tutorial: Model-View-Controller (MVC) Struktur in Java Projekten nutzen

Die MVC-Architektur erfreut sich in letzter Zeit sehr hoher Beliebtheit, ständig hört man über neue Frameworks die auf diese Architektur schwören. (Beispiele: Spring Framework, ASP.NET MVC, Objective C...)

Bild 1: Der Aufbau - Quelle: Wikipedia

Doch was steckt eigentlich dahinter und warum sollte man seine Struktur überdenken?

Ich denke jeder, der eine Anwendung geschrieben hat schon die Erfahrungen gemacht, dass der Code immer unübersichtlicher wird, je größer ein Projekt wird.

Hier setzt MVC an, denn durch die strickte Teilung von Präsentation (View), der Programmlogik (Controller) und der Datenschicht (Model) erhöht sich die Lesbarkeit und Wartbarkeit des Codes. So kann man ein Projekt viel einfacher um weitere Funktionen ergänzen ohne den halben Sourcecode durchzugucken.

In diesem Artikel möchte ich diese Architektur an einem kleinem Java Beispiel demostrieren. Das Programm ist natürlich nicht wirklich zu gebrauchen, es soll lediglich das Vorgehen nahelegen.

Wie bereits oben erwähnt soll die Präsentation, also das was der Benutzer zu sehen bekommt (üblicherweise das Formular, also die GUI) von der Logik getrennt werden. Deshalb dürfen in der View-Klasse nur Sachen stehen, die der Darstellung der Elemente dienen. Das was passiert wenn man eins dieser Elemente anklickt wird dann über den Controller gesteuert.

Die Berechnungen an sich stehen dann im Model, wo auch alle Objekte verweilen, die unser Programm nutzt.

Unser Demo Programm ist sehr einfach aufgebaut, wir haben nur vier Klassen:

Bild 2: Der Aufbau des Projekts

Bevor ihr euch wundert, das Programm soll uns später die Quadratwurzel einer Zahl berechnen.

An Bild 1 könnt ihr erkennen, dass die Controller Klasse die wichtigste ist, da sie eigentlich das Ganze Programm steuert und dafür sorgt, dass die View und das Model sich verständigen können. Deswegen muss die Controller Klasse die anderen Klassen kennen, die anderen brauchen sich gegenseitig aber nicht!


public class Main {

    static WurzelController controller;

    /**
     * Diese Klasse wird nur dazu benutzt alle nötigen
     * Komponenten zu Initialisieren und die erste
     * View anzuzeigen
     */
    public static void main(String [] args){
        controller = new WurzelController();

        controller.showView();
    }
}

Die Klasse Main macht hier auch nichts anderes, als die anderen Klassen zu instanzieren und uns die View anzuzeigen.

An den Konstruktoren kann man auch die Abhängigkeiten erkennen. Eigentlich macht man der View auch das Model bekannt (siehe Bild 1), aber da unser Beispiel zu simpel ist können wir uns diese Bekanntschaft sparen.

Das Model ist ebenfalls sehr einfach aufgebaut, es enthält die nötigen Rechenoperationen die für unser Programm wichtig sind und bietet öffentliche Methoden mit denen der  Controller die Werte abfragen kann


/**
 * Das Model ist komplett unabhängig von den anderen
 * Klassen und weiß nicht was um ihn herum geschieht.
 * Es ist völlig egal ob man dieses Model aus einem
 * Fenster oder einer Konsolen Eingabe verwendet -
 * beiden würde funktionieren.
 */
public class WurzelModel {

    long _value;

    public WurzelModel(){
        zurückSetzen();
    }

    public void zurückSetzen(){
        this._value = 0;
    }

    public void berechneWurzel(long wert){
        this._value =  (wert * wert);
    }

    public long getWurzel(){
        return this._value;
    }
}

Wichtig hierbei ist, dass diese Klasse keine Beziehungen zu den anderen Klassen hat! Man muss sie so aufbauen, dass man sie ohne eine Änderung in einem anderen Projekt benutzen könnte.

Unsere View ist ganz typisch aufgebaut. Es werden die Steuerelemente initialisiert und auf der JForm plaziert, desweiteren muss die View Methoden bieten, mit denen man die ActionListener für die auf dem Formular liegenden Steuerelemente setzen kann. Über diese wird unser Controller mit der View kommunizieren. Dazu kommen noch die getter und setter Methoden für die Textfelder, diese werden benötigt, damit man aus dem Controller Zugriff darauf hat!


/* Die View-Klasse diese Enthält nur die Präsentation
 * hier sollte man keinerlei Programmlogik finden
 * alle Berechnungen und Reaktionen auf Benutzeraktionen
 * sollten allesammt im Controller stehen
 */
public class WurzelView extends JFrame{

    private JLabel lbl1 = new JLabel("Eingabe: ");
    private JTextField txtEingabe = new JTextField(3);
    private JButton cmdCalc = new JButton("Wurzen Berechnen >");
    private JTextField txtErg = new JTextField(5);
    private JButton cmdClear = new JButton("Zurüclsetzen");

    public WurzelView(){
        super("Wurzel Berechnen");

        initForm();
    }

    /**
     * Die JForm initialisieren und alle Steuerelemente
     * darauf positionieren
     */
    private void initForm(){
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(new FlowLayout());
        this.setBounds(200, 200, 500, 100);

        this.add(lbl1);
        this.add(txtEingabe);
        this.add(cmdCalc);
        this.add(txtErg);
        this.add(cmdClear);

    }

    public void resetView(){
        this.txtEingabe.setText("");
        this.txtErg.setText("");
    }

    public String getEingabe(){
        return this.txtEingabe.getText();
    }

    public void setErgebnis(String erg){
        this.txtErg.setText(erg);
    }

    /**
     * Funktionen bereitstellen, mit denen man später aus
     * dem Controller die nötigen Listener hinzufügen kann
     */
    public void setWurzelBerechnenListener(ActionListener l){
        this.cmdCalc.addActionListener(l);
    }

    public void setResetFormListener(ActionListener l){
        this.cmdClear.addActionListener(l);
    }
}

Bleibt nur noch der Controller. Dieser ist auch sehr simpel aufgebaut:


/**
 * Der Controller muss beide die View und das Model kennen
 * da dieser für die Kommunikation zwischen den Beiden sorgt
 */
public class WurzelController {

    private WurzelView _view;
    private WurzelModel _model;

    public WurzelController(){
        this._model = new WurzelModel();
        this._view = new WurzelView;

        addListener();
    }</pre>
<pre>
<div id="_mcePaste">    public void showView(){</div>
<div id="_mcePaste">        this._view.setVisible(true);</div>
<div id="_mcePaste">    }</div>
/**
     * Die Listener, die wir aus den Internen Klassen generieren
     * werden der View bekannt gemacht, sodass diese mit
     * uns (dem Controller) kommunizieren kann
     */
    private void addListener(){
        this._view.setWurzelBerechnenListener(new WurzelBerechnenListener());
        this._view.setResetFormListener(new ResetFormListener());
    }

    /**
     * Inneren Listener Klassen implementieren das Interface ActionListener
     *
     * 1: Hier wird erst der eingegebene Wert aus der View geholt
     * 2: Der Wert wird dem Model übergeben und die Wurzel berechnet
     * 3: Die berechnete Wurzel wird aus dem Model geladen und
     * 4: Wieder der View zum Darstellen übergeben
     *
     * ACHTUNG: Fehlerprüfung muss noch implementeirt werden
     */
    class WurzelBerechnenListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            long wert = Long.valueOf(_view.getEingabe());
            _model.berechneWurzel(wert);
            _view.setErgebnis(String.valueOf(_model.getWurzel()));
        }
    }

    /**
     * Hier wird dem View und dem Model gesagt ihre gespeicherten
     * Werte zu löschen.
     */
    class ResetFormListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            _view.resetView();
            _model.zurückSetzen();
        }
    }
}

Wie man sieht werden hier nur die dem Konstruktor übergebenen Referenzen auf die View und das Model gespeichert und dann die Erzeugten ActionListener der View übergeben.

Die ActionListener sind interne Klassen die nun automatisch von unserer View aufgerufen werden, wenn der Benutzer einen der Buttons klickt! Praktisch oder?

Wenn man die WurzelBerechnenListener Klasse anschaut kann man auch die Typische Kommunikation zwischen den Schichten sehen. Die View lößt ein Event aus, für dessen Abhandlung der Controller verantwortlich ist. Nun holt der Controller sich alle nötigen Daten aus der View (in diesem Fall ist es nur ein Wert, die Eingabe) und lässt das Model das Quadrat berechnen. Schließlich holt der Controller sich die berechneten Daten zurück und lässt die View diese anzeigen.

Im Grunde gar nicht kompliziert oder? So sieht das Fenster zur Laufzeit aus:

Bild 3: Das Programm zur Laufzeit

Wenn man ein neues Projekt anfängt sollte man sich natürlich erst einmal genau überlegen ob man die MVC Architektur benutzt, denn diese ist bei kleinen Projekten mit einem höheren Aufwand verbunden. Wenn man aber weiß, dass in absehbarer Zeit an dem Programm weitere Änderungen oder Erweiterungen vorgenommen werden sollte man MVC in Betracht ziehen!

ACHTUNG: Habe eben erst gemerkt, dass ich überall "Wurzel" geschrieben habe, das ergibt natürlich gar keinen Sinn! :D oh mann, muss wohl sehr verwirrt gewesen sein! Quadrat müsste es natürlich heißen. :) Keine Lust das nun alles nochmal zu ändern!

Die Sourcen könnt ihr hier laden: Download

VN:F [1.8.4_1055]
Rating: 10.0/10 (3 votes cast)
26Jan/100

Interessantes aus der Java Welt Teil 1

In diesem Beitrag möchte ich mal auf ein paar Besonderheiten eingehen, denen man wohl nicht jeden Tag beim Programmieren begegnet und die teilweise auch anders arbeiten, als wie man im Kopf denkt!

Ich möchte hier mal mit einem kleinem Beispiel anfangen. Guckt euch mal den kurzen Quellcode hier an und versucht zu berechnen was nach der Ausführung des Codes auf dem Bildschirm ausgegeben wird, bzw. lässt sich dieser Code überhaupt kompilieren?


public class Main {
    public static void main(String[] args) {
        int a = 2;
        int b = 3;
        int c = a+++b;

        System.out.println(c);
    }
}

Kompilieren lässt es sich aber was wird nun ausgegeben? Was passiert wenn man ein dreifaches Plus benutzt?

Naja das Ergebnis ist in jedem Fall 5. Doch um zu erfahren warum das so ist, muss man ersteinmal wissen, was der Kompiler da eigentlich macht, denn wir haben 2 Möglichkeiten die ausgeführt werden können:

int c = a_+_++b;   //1: Präfix Inkrement ( _ soll ein Leerzeichen darstellen)
int c = a++_+_b;   //2: Postfix Inkrement

Beide Rechenwege existieren doch nur einer wird ausgeführt, nämlich der 2te, denn nur der liefert als Ergebnis 5 zurück! Führt man den ersten Rechenweg aus erhält man 6 als Ergebnis!

Wenn der Kompiler diese Zeile liest und das Plus nach dem a gelesen hat erwartet er nun eigentlich den zweiten Summanden findet aber ein weiteres Plus, so wird erstmal die a++ Anweisung ausgeführt und anschließend + b dazu gerechnet.

Warum ist das so? Eigentlich wird doch in beiden Rechenwegen eine der Variablen um 1 (durch das ++ inkrement) erhöht, oder etwa nicht? Betrachten wir dazu dieses kleine Beispiel:


    public static void test(){
        int a = 2;
        a = ++a;

        int b = 2;
        b = b++;

        System.out.println(a);  //Ergibt 3
        System.out.println(b);  //Ergibt 2
    }

Im Falle von a wird der Wert von a erst erhöht und dann zugewiesen, dafür sorgt das so genannte Präfix (++a) da die Pluse auf der linken Seite stehen. Bei b wird erst der Wert von b zugewiesen und dann erhöht. Dadurch dass der Wert aber schon zugewiesen wurde verfällt diese Erhöhung  wieder. Wenn die Operatoren auf der rechten Seite stehen nennt man sie postfix.

Wie man sieht stecht doch einiges hinter einer solchen kleinen Anweisung. Deswegen sollte man hier (wenn man will, dass der erste Fall eintritt) immer Leerzeichen setzen und dem Kompiler so genau sagen, welche operation durchgeführt werden soll!

Benutzt beim Übergeben von Werten diese Schreibweise möglichst nicht, da es zu unerwarteten Werten kommen kann. Um hier sicher zu gehen und nichts zu reskieren kann man auch die etwas längere Schreibform benutzen:

starteFunktion(variable++); //stattdessen kann man auch das nutzen:
starteFunktion(variable + 1);

Die zweite Variante übergibt ebenfalls den Wert von variable um 1 erhöht, ändert aber nicht deren Wert!

Wer mehr zu diesem Thema wissen möchte kann hier gucken: Galileo Java ist auch eine Insel

VN:F [1.8.4_1055]
Rating: 10.0/10 (2 votes cast)
12Jan/100

Java: DB4O Anbindung in einer Client / Server umgebung

Da ich mich zur Zeit selbst mit diesem Thema auseinander setze, dachte ich, es könnte nicht schaden ein kleines Tutorial zu schreiben. Hier möchte ich die ersten Schritte erklären und an ein paar Beispielen demonstrieren.

DB4O steht für Database for Objects und ist, wie der Name es schon sagt eine Datenbank für Objekte. Im Unterschied zu einer SQL Datenbank (wie MySQL) werden hier keine Tabellen erstellt, in denen die Daten gesichert werden, sondern die Objekte "als Ganzes" in die Datenbank geschrieben.

Das bringt einen gewaltigen Vorteil, denn wenn man ein Objekt in einer herkömmlchen relationalen Datenbank sichern möchte muss man es in einzelne Teile zerlegen und diese Teile dann in die Tabellen schreiben. Beim Auslesen wieder das ganze Spielchen umgekehrt, man sucht sich alle Werte des Objekts wieder aus der Datenbank zusammen um dann eine neue Instanz davon zu erstellen und mit den alten Werten zu füllen.

All das ist mit DB4O nicht mehr nötig, da hier die Datenbank diese Aufgaben für uns übernimmt, somit ist es sehr einfach für uns auch sehr komplexe Objekte schnell und bequem zu sichern und zu laden.

Fangen wir aber ersteinmal mit der Installation an

Die Installation, wenn man diese so nennen kann ist bei DB4O sehr einfach, und besteht nur darin, die nötigen Librarys in das Projektverzeichnis zu kopieren. Somit ist keine Installation beim Client mehr nötig, das Programm bringt dann alles was nötig ist selbst mit!

Als erstes benötigt ihr die Librarys von der DB4O - Homepage

Nach dem ihr diese heruntergeladen habt erstellt ihr ein "lib" Verzeichnis in eurem Projektordner.

In diesen neu erstellten Ordner entpackt ihr die *.jar Dateien aus dem heruntergeladenen Archiv aus dem gleichnamigen Ordner.

Nun wechselt ihr in Eclipse (bzw. eure IDE) und aktualisiert mal die Ansicht, ihr solltet nun den neuen Ordner mit den ganzen *.jar Dateien sehen.

Der letzte Schritt besteht darin, die Librarys zu dem Klassenpfad hinzuzufügen. Dazu klickt ihr mit der Rechten Maustaste auf euer Projekt und wählt "Properties", dann wählt ihr rechts "Java Build path", und nun den Tab "Libraries".

Nun klickt ihr auf "Add JARs..." und wechselt in euren lib Ordner. hier wählt ihr alle *.jar Dateien aus und klickt 2x auf "OK". - Das wars auch schon!

Wenn das alles erledigt ist, sollte euer Projektbaum ca so auusehen wie auf dem Bild links.

Die Praxis

Wenn ihr die Installation schon einfach fandet, dann werdet ihr die Implementation lieben! Denn das ist genauso schnell erledigt, denn alle große Arbeiten übernehmen für uns die Libraries, die wir im letzten Schritt importiert haben!

Als erstes benätigen wir eine Klasse deren Objektinstanzen wir dann in die Datenbank schreiben können. Dazu habeich eine einfache kleine Klasse "Car" geschrieben:


import java.io.*;
import java.util.*;

public class Car implements Serializable{
	private static final long serialVersionUID = 1L;
	private String marke;
	private int ps;

	private LinkedList teile = new LinkedList();

	// Konstruktoren

	public Car(){}

	public Car (int ps){
		this.ps = ps;
	}
	public Car(String marke){
		this.marke = marke;
	}
	public Car(String marke, int ps){
		this.marke = marke;
		this.ps = ps;
	}
	public void addTeil(String teil){
		teile.add(teil);
	}

	// Getter &amp;amp; Setter

	public LinkedList getTeile(){
		return teile;
	}
	public void setMarke(String marke){
		this.marke = marke;
	}
	public void setPs(int ps){
		this.ps = ps;
	}
	public String getMarke(){
		return this.marke;
	}
	public int getPs(){
		return ps;
	}
}

Wichtig ist bei dieser kleinen Klasse ist, dass sie das Interface java.io.Serializable implementiert, damit es auch selialisiert werden kann!

Ok nun haben wir ein Objekt, wie krigen wir dieses in die Datenbank? Das geht sehr einfach, wie man an diesem Codeschnippsel sehen kann:


    ObjectContainer db = Db4o.openFile
        ("C:/test.yap");
    try{
        db.set(new Car("VW", 124));
    }
    catch(DatabaseFileLockedException e){
        e.printStackTrace();
    }
    finally{
        db.close();
    }

Erst wird ein ObjectContainer erstellt, in den wir die Datenbankdatei einlesen, die bei DB4O die Endung yap haben. Wenn diese Datei noch nicht vorhanden ist wird diese automatisch angelegt. Hier gibt es zu beachten, dass die Pfadangabe nicht mit dem Windowstypischen "\" (Backslash) angegebenwird, sondern mit einem normal Slash.

Im folgenden Try-Catch Block wird die Methode Set aufgerufen, der wir auch eine Instanz unseres Car-Objekts übergeben. Das wars auch schon, nun ist diese instanz der Car Klasse schon in der Datenbank gespeichert!

Genauso einfach geht es auch einene Wert aus der Datenbank wieder auszulesen:


bjectContainer db = Db4o.openFile
        ("C:/test.yap");
    try{
        ObjectSet result = db.get(new Car(null));
    }
    catch(Exception e){
        e.printStackTrace();
    }
    finally{
        db.close();
    }

Dazu wird die Methode Get benutzt, die als Rückgabewert ein ObjectSet mit allen Autos in der Datenbank liefert. Als Parameter wird der Get Methode eine leere Car Klasse übergeben, zu dem Warum komme ich nun.

Abfragesprachen / Abfragemethoden

Wie ihr an dem Letzten Codebeispiel gesehen habt wird hier keine SQL Sprache genutzt um die nötigen Werte aus der Datenbank zu ziehen, sondern es wird mit Objekten gearbeitet. DB4O bietet drei verschiedene Wege um an die gespeicherten Objekte zu kommen:

QBE - Query by Example
SODA - Simple Object Database Access
NQ - Native Queries

Ich werde hier aber nur auf die ersten zwei zusprechen kommen.

QBE bietet die einfachste Möglichkeit an unsere gespeicherten Objekte zu kommen, diese Methode habe ich auch bei dem Beispiel oben genutzt.

Bei dieser methode wird vom Benutzer ein Objekt erzeugt, nach dem dann in der Datenbank gesucht wird. Wenn wir den Fall von eben aufgreifen, wo nur eine leere Car Klasse übergeben wurde, werden wir sehen, dass der Wert null mit allen Werten übereinstimmt. Das heißt, will ich alle Werte aus der Datenbank haben, übergebe ich ihr eine "leere" Klasse.

Wenn ich die Get Abfrage etwas ändere und statt null, "VW" übergebe, würde uns die Datenbank alle Autos ausspucken, die als marke VW haben. Genauso können wir auch nach der PS Zahl suchen, indem wir statt null eine Zahl übergeben. Wir können aber auch, wenn wir genau wissen wonach wir suchen, auch beide Werte übergeben. db.get("VW",120);

Achting, wir können diese methoden nur anwenden, weil wir in der Car Klasse die einzelnen Konstruktoren definiert haben. So muss für jede Suchanfrage auch der passende Konstruktor vorhanden sein. Oder man erstellt einen Konstruktor, mit dem man alles abdecken kann. So könnte man in unserem Beispiel hier auch so vorgehen um alle VW Autos gelistet zu bekommen: db.get("VW", null);

Leider hat diese Vorgehensweise so simpel sie auch ist entscheidende Nachteile. So dürfen in den Konstruktoren keine Werte initialisiert werden, denn wenn ich im Konstruktor die Variable marke standardmäßig als marke = "VW"; initialisieren würde, würde mir die Datenbank bei jeder Suche nur Autos der marke VW ausspucken.

Desweiteren kann man keine Bedingungen benutzen, denn was ist wenn ich zB. alle Autos sehen will, die mehr als 100 PS haben?

Genau hier setzt SODA an. Hier sollte man wissen, dass alle Abfragen in DB4O unter der Haube in SODA laufen, so wird jede Anfrage automatisch konvertiert.

SODA bietet uns hier viele weitere Werkzeuge die man auch aus zB. SQL kennt. So können wir nun auch komplexere Abfragen erstellen. Setzen wir nun mal unser Problem von eben um und fragen alle Autos ab, die mehr als 100 PS haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		q.descend("ps").constrain(100).greater();
		listResult(q.execute());

Wie man sieht ist diese Abfragemethode etwas komplexer. Aber auf den zweiten Blick ist es aber doch ganz logisch. Zu erst wird ein Query Objekt erzeugt, dem wir dann ein "constrain" (zu Deut. Beschränkung, Begrenzung) übergeben. Diesem übergeben wir "Car.class", somit sagen wir dem Query, dass er nur Objekte vom Typ Car aus der Datenbank holt.

Die nächste Zeile ist eigentlich auch selbsterklärend, dort setzen wir fest, dass die Ergebnisse absteigend (descend) sortiert werden, undzwar nach der Eigenschaft "ps" und durch eine Weitere Beschränkung nur Werte über 100 akzeptiert.

Die letzte Zeile führt den Befehl aus.

Ähnlich kann man auch einen Maximalwert setzen, sodass nur Autos angezeigt werden die zwischen 100 und 130 PS haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		Constraint c1 = q.descend("ps").constrain(100).greater();
		q.descend("ps").constrain(130).smaller().and(c1);
		listResult(q.execute());

Natürlich kann man auch Autos abrufen, die einen bestimmten Wert enthalten. ZB alle Autos die die den Buchstaben w im Namen haben:


		Query q = aClient.query();
		q.constrain((Car.class));
		q.descend("marke").constrain("w").like();
		listResult(q.execute());

Client / Server umgebung

So, nach der Kurzen Einführung wollen wir nun das eigentlich Ziel umsetzen, nämlich die Datenbank in ein Client Server System integrieren. Zum Glück ist das ebenfalls nicht allzu kompleziert!

Malen wir uns erstmal ein kleines Diagramm um zu verbiltlichen, wie der Aufbau der Programme sein soll:

Bild 2: Der Aufbau

Das Tolle ist, dass die DB4O Libraries und die Arbeit abnehmen eine Verbindugn aufzubauen, die Sockets zu verwalten und die einzelnen Threads anzulegen. Somit müssen wir uns nur auf die Implementierung konzentrieren.

Unsere Server Applikation ist dementsprechend sehr einfach gestrickt:


import com.db4o.*;

public class Server {
	public static void main (String [] args){

		//Server starten mit lokaler Datei
		System.out.println("Starte Server...");
		ObjectServer server = Db4o.openServer("C:/test.yap",54321);

		server.grantAccess("test","pass");
		server.grantAccess("andererUser","pass2");
		try {
			while(true){
				//Leerlauf damit der Server anbleibt
				System.out.println("SERVER: [DB4O] läuft...");
				Thread.sleep(10000);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		finally {
			server.close();
		}
	}
}

Wie man sieht sieht man nicht viel. Was auffällt, ist dass man nun statt openFile openServer benutzt wird, und als zweiten Parameter nun einen Port mitgibt, auf dem der Server auf Verbindungen wartet.

Danach werden noch die Benutzer beschrieben, denen der Zugriff auf die Datenbank gewährt werden soll. Und eigentlich wars das auch schon! Die anschließende Schleife sorgt nur dafür, dass das Programm unendlich läuft (zumindest bis der Benutzer es terminiert) und Anfragen von Clients akzeptiert!

Gut nun schauen wir uns mal den Client an:


import com.db4o.*;

public class ClientAdd {

	static ObjectContainer aClient = null;

	public static void main(String [] args){
		aClient = Db4o.openClient("localhost", 54321, "test", "pass");

                //------------------------------------
		Car myCar1 = new Car("Mercedes", 98);
		Car myCar2 = new Car("VW", 158);

		myCar1.addTeil("Dieselmotor");
		myCar1.addTeil("Klimaanlage");
		myCar1.addTeil("ABS");

		myCar2.addTeil("ERP");
		myCar2.addTeil("Schiebedach");
		myCar2.addTeil("Automatik");

		//Sende Objekte
		System.out.println("Sende Objekte...");
		aClient.set(myCar1);
		aClient.set(myCar2);

		aClient.commit();
		System.out.println("Fertig");

		aClient.close();
	}
}

Wie man sieht ist die Verbindung zum Server noch einfacher gestrickt als der Server. Es wird wiedermal ein ObjectContainer instanziert und kann nun genauso benutzt werden wie in den Beispielen von oben.

Ich denke mal das dürfte als Einstieg reichen. Wenn ihr Probleme oder Fragen habt könnt ihr diese hier gern posten, vielleciht kommt dann irgendwann ein zweiter Teil.

Ein Lauffähiges Beispiel gibts hier: Download [Eclipse Project]

Wer mehr erfahren will guckt hier.
Eine wetere gute Anleitung gibts hier.

VN:F [1.8.4_1055]
Rating: 6.7/10 (3 votes cast)
30Dez/090

Tetris nachprogrammieren in Java

Ich hatte ja nun schon einige Java Tuts hier im Blog, auch zwei Spiele waren dabei (Fill IT und Snake). Nun bin ich letztens über mein erstes Java Spiel-Projekt gestolpert, da habe ich nämlich versucht Tetris nachzubasteln.

Das Ergebnis von diesem kleinen Projekt könnt ihr hier in dem Bild links betrachten.

Im Nachhinein weiß ich, dass ich lieber ein anderes (einfacheres) Spiel als erstes Projekt hätte wählen sollen, Snake oder so, denn ich habe knapp vier Wochen an diesem hier gesessen.

Natürlich hat es sich dennoch gelohnt und ich habe viel bei der Entwicklung gelernt, und dabei ist eine komplett spielbare Version bei herausgekommen!

Da dies mein erster Gehversuch war ist der Code natürlich nicht wirklich professionell und (wahrscheinlich) voll von irgendwelchen Anfängerdummheiten, desweiteren sind noch einige (meist kleinere) Bugs und Probleme vorhanden, die das Spielvergnügen trügen!

Die gute Nachricht ist aber, dass ich vieles kommentiert habe, und man sich somit relativ schnell in den Code einlesen kann!

Features:

  • Bekanntes Spielprinzip
  • Vier verschiedene Objekte (Zwei fehlen zum Original -> L-Blöcke)
  • Punkteanzeige
  • Anzeige der nächsten Figur
  • Vier verschiedene Schwierigkeitslevel

Bekannte Probleme:

  • Unterste Zeile ist fehlerhaft
  • Man kann Objekte nach rechts aus dem Bildschirm schieben
  • Die Oberfläche neigt zum "blinken"
  • Die Steuerung ist nicht "flüssig"
  • Letzter Schwierigkeitsgrad ist unspielbar, da zu schnell
  • Stürzt manchmal ab, sobald die erste Figur unten ankommt

Die oben gelisteten Bugs sind da, stören aber nur wenig. Da ich nicht die Absicht habe dieses Spiel weiterzuentwickeln, schreibt mir bitte keine Kommentare oder Mails mit weiteren Fehlern oder Bugs.

Falls ihr selber an einem solchen Spiel arbeitet und fragen zu dem Aufbau oder dem Quellcode habt könnt ihr mich natürlich gerne kontaktieren.

Folgendes solltet ihr schon mal gesehen haben um dem Quellcode zu verstehen:

  • Vererbung
  • Abstrakte Klassen
  • GUI Programmierung
  • Zeichnen (Java Graphics Klasse)
  • Allgemeine Objekt Orientierte Programmierung
  • Gute Kenntnisse der Basis Java Klassen

Die Downloads findet ihr hier:

Quell-text: Download | Vorkompiliert: Download

VN:F [1.8.4_1055]
Rating: 3.3/10 (3 votes cast)
1Sep/090

Java: Snake nachprogrammieren Demo

Letzte Woche habe ich versucht das iPhone Spiel Foold-it! in Java nach zu programmieren und heute habe ich eine weitere kleine Demojavaanwendung fertiggestellt, die sich an dem altbekannten Spiel Snake orientiert.

Irgendwie bin ich auf dem Geschmack gekommen Retro Games nach zu programmieren, das macht echt Spaß. heute will ich euch meine kleine Version von Snake "Sneaky" zeigen. Wie auch Fill IT ist dies ein reines Demospiel um das Prinzip deutlich zu machen. Deswegen habe ich auch hier nur die Basisfunktionalität eingebaut.

nokia_snake_thumbAls Vorbild habe ich mir hier das Snake genommen, das man von den ganz alten Nokia Handys kennt. Geniales und einfaches Spielprinzip mit Suchtfaktor!

 

sneaky1Meine Umsetzung unterscheidet sich deswegen kaum. Es ist lediglich etwas bunter geworden, der Kopf der Schlange ist Rot und der Punkt der eingesammelt werden soll ist grün! Desweiteren habe ich eine Punkte Anzeige implementiert und auch einen Zähler die die Spielzeit in Sekunden anzeigt!

Wie auch in dem letzten Projekt könnt ihr euch sowohl die fertig kompilierte Binary laden oder gleich den Quelltext!

Mit dem Kommentieren des Quelltextes habe ich mir diesemal mehr Mühe gegeben, und viel mehr geschrieben, sodass ihr euch ganz gut zurecht finden werdet!

An dieser Stelle sei auch hier gesagt, dass ich noch ein Java Anfänger bin und der Code deswegen sehr wahrscheinlich nicht perfekt seit wird aber zum Üben dennoch sehr geeignet!

Wenn ihr Bugs oder Fehler findet, könnt ihr diese Gerne als Kommentar oder per Mail an mich senden, gern auch mit Lösung (ihr habt ja den Quelltext! :D ) Aber auch Verbesserungsvorschläge, was den Code angeht sind gern gesehen!

Ihr könnt es natürlich auch gern weiter entwickeln, schickt mir dann einen Screenshot oder sowas :-)

Um den Quelltext verstehen zu können reichen auch Grundkenntnisse in Java und in diesen Themengebieten:

  • Objektorientierte Programmierung (OOP)
  • Grafische Programmierung (GUI)
  • Vererbung
  • Eventbehandlung

Download: Source         Download: Binary (WIN/MAC/LINUX)

VN:F [1.8.4_1055]
Rating: 0.0/10 (0 votes cast)
28Aug/099

Java: iPhone Spiel “Flood-it!” in Java nachprogrammieren

Erst vor ein paar Wochen bin ich im Apple Appstore über das kleine Spiel Flood-it! gestolpert. Das Spiel ist simpel, kostenlos und macht sehr süchtig!

Ziel des Spiels ist es den ganzen Bildschirm mit eine Farbe zu füllen. Dazu hat man 22 Versuche! Dabei fängt man als das Viereck oben links an und übernimmt die anliegenden Felder indem man die Passende Farbe unten anklickt!

flood-it-for-iphone-for-1

Schon damals habe ich mir gedacht: Mensch, das könnte man doch ganz einfach nachprogrammieren.

Heute habe ich mir dann die Zeit genommen und das ganze mal umgesetzt.

Für die Umsetzung habe ich mich für Java entschieden. In Java kenne ich mich zwar nur sehr begrenzt aus, aber das Projekt ist natürlich perfekt zum üben und lernen!

Geschlagene sechs Stunden später ist eine spielbare Version entstanden, die zwar nicht den kompletten Featureumfang des Originals besitzt, aber das Spielprinzip ganz gut adoptiert!

Ich muss sagen dass es sehr viel Spaß gemacht hat dieses kleine Game zu "kopieren" und ich viel über Java dazulernen konnte! Und ich kann jedem auch nur empfehlen es selber zu versuchen ein Spiel, das einem gut gefällt nachzumachen.

Ich werde den Quelltext meiner Version hier veröffentlichen, falls es jemanden interessiert, und er auch mal versuchen will so ein Projekt zu realisieren und an einer stelle nicht weiterkommt kann er meinen Code gerne zum nachschlagen benutzen!

fillit

Ich habe versucht meinen Code sehr einach zu halten und im Sinne der Polymorphie zu gestalten. Da ich alles andere als ein Java-Experte bin, kann es natürlich auch sein, dass manche Codestellen auch viel einfacher und oder besser gestaltet werden konnten, und auch dass es noch Bugs gibt will ich auch nicht ausschließen!

Wenn jemand einen Bug findet, oder Verbesserungsvorschläge zum Code hat kann er diese gerne als Comment oder per Mail an mich senden!

Der Quelltext ist sehr simpel gehalten, es würde aber helfen wenn man folgendes schon mal gehört hat:

  • Rekursivität
  • Klassen & Vererbung
  • GUI - Programmierung
  • Event-Behandlung
  • Java Drawing Klassen
  • Grundkenntnisse OOP

Ich habe den Code natürlich auch kommentiert, aber wenn man mit den obigen Begriffen nichts anfangen kann sollte man sich erstmal dort einlesen.

28.09.09 UPDATE: Danke an Daniel, habe den Bug mal behoben!
31.09.09 UPDATE: Nun ist der letzte (bekannte) Bug behoben, das Spiel müsste nun "perfekt" laufen!

Download: Source Download: Binary (MAC/WIN/LINUX)

VN:F [1.8.4_1055]
Rating: 10.0/10 (3 votes cast)
12Aug/092

Codeklau & wie man eigenen Code schützen kann Teil 2

In diesem zweiten Teil werde ich darauf eingehen, wie man seinen Code möglichst gut schützen kann. Natürlich gibt es keinen 100%igen Schutz aber man kann es jemandem durchaus schwer machen an seinen Code zu kommen.

Es gibt mehrere Ansätze wie man das Problem angehen kann. Zum Einen wäre da die Möglichkeit unseren .NET Code nicht in ein Assembly sondern in eine native Anwendung zu kompilieren. Das würde die Dekompilierung um einiges erschweren.

Dafür stellt und Microsoft mit dem .NET Framework bereits ein Tool zur Verfügung. Dieses kann eure Assemblys in Maschinencode compilieren, tut also das was der JIT-Compiler sonst erst bei der Ausführung machen würde! Dies hat den Vorteil, dass die Anwendung schneller arbeitet, da sie direkt ausgeführt werden kann, aber wird erheblich größer, da alle .NET Abhängigkeiten nun in das Programm integriert werden müssen. Es kann nun sogar ohne ein Installiertes .NET Framework ausgeführt werden!

Das Programm was das für uns erledigt nennt sich "ngen.exe" und ist ein Kommandozeilen Programm. Im MSDN gibts eine umfassende Anleitung für das Programm weswegen ich hier nicht auf die Funktionsweise eingehen werde!

Leider bringt diese Art der Kompilierung einen entscheidenden Nachteil mitsich, nämlich dass das Programm die Plattformunabhängigkeit verliert und meistens nurnoch auf dem eigenen Rechner (auf dem es erstellt wurde) benutzt werden kann! Also müssen wir uns etwas anderes einfallen lassen!

Und tatsächlich gibt es eine weitere Möglichkeit seinen Code zu schützen und dennoch keine Nachteile aufkommen zu lassen! Das Visual Studio bringt bereits ein Programm mitsich das uns da helfen kann! Das ist der sogenannte "Dotfuscator Community Edition" den man über das "Extras"-Menü in Visual Studio aufrufen kann!

Diese "Ofuscatoren" machen dann, simpel ausgedrückt, den Quelltext schlechter lesbar. Was sich so simpel anhört ist aber sehr effektiv! Wie ich an einem Beispiel demonstrieren möchte:

Hier ersteinmal der Quelltext den wir geschrieben haben in der Originalform:


private void CalcPayroll(SpecialList employeeGroup) {
   while (employeeGroup.HasMore()) {
        employee = employeeGroup.GetNext(true);
        employee.UpdateSalary();
        DistributeCheck(employee);
    }
}

Und nun das Ergebnis des Obfuscators:


private void a(a b) {
    while (b.a()) {
        a = b.a(true);
        a.a();
        a(a);
    }
}

Wie man sieht kann man nicht mehr so einfach darauf schließen was diese Funktion eigentlich machen soll, da alle für den Menschen logischen Bezeichner fehlen!

Was macht der Obfuscator also genau? Seine Hauptaufgabe ist natürlich den Quelltext nach dem Dekompilieren für den Menschen möglichst unleserlich zu machen. Das wird dadurch erreicht, dass alle Bezeichner und alle Texte die im Quelltext vorkommen durch zufallsgenerierte Bezeichner ersetzt werden. Denn dem Computer ist es egal, ob eine Variable "anzahlBenutzer" oder "3Fdg4§§s" heißt. Aber der Benutzer der den Quelltext liest ist natürlich sehr verwirrt da er nichts mit diesen Bezeichnungen anfangen kann.

Es gibt auch andere Dritthersteller, die Software anbieten die genau das machen soll, nur besser versteht sich! Ich habe zum testen mal den Phoenix Protector, teLock und den im Visual Studio integrierten Dotfuscator ausprobiert.

Wobei ich teLock nicht weiter behandeln werde, da alle daraus erzeugten Programm nicht lauffähig waren und immer abgestürzt sind!

Wie kann man selbst vorgehen um zu testen wie gut sein Programm geschützt ist? Ganz einfach, indem man so vorgeht als ob man ein fremdes Programm "knacken" würde. Dazu können wir uns dem Tool .NET Reflector bedienen, das ich im ersten Teil vorgestellt habe!

Ich werde hier als Beispiel mal das Tool aus dem Beitrag "Den Office Lizenzschlüssel aus der Registry auslesen" verwenden. Wenn wir das Programm dekompilieren dann sehen wir ca dieses Bild:

bild-32Bild 1: Office Key Finder im Dekompilierten Zustand

Wie man sieht konnte das Programm komplett dekompiliert werden und man kann in dem Quelltext wunderbar lesen (rechts)! Ich habe des weiteren die Stellen hervorgehoben, wo man die Original Namen der Variablen und Funktionen erkennen kann! - So sieht eine ungeschützte .NET Anwendung aus.

Nun habe ich den Phoenix Protector mal drüber laufen lassen und die selben stellen markiert.

bild-113Bild 2: Dieses Mal ist der Quelltext viel schlechter lesbar!

Wie man sieht haben nun alle Klassen, Funktionen und Variablen ganz andere unlogische Namen bekommen die in keinster weise deren Funktion wiederspiegeln!

bild-6Bild 3: Der Dotfuscator beschränkt sich auf das nötigste

Bild 3 zeigt das Ergebnis des im Visual Studio eingebauten Dotfuscators. Dieser hat sogar die Klassenstruktur geändert und auf das nötigste reduziert! (links)

Ich war aber mit dem Ergebnis des Phoenix Protectors besser zufrieden, denn nach etwas herumspielen mit den Einstellungen konnte ich den Quelltext so gut "tarnen", dass selbst der Disassambler diesen nicht mehr auflösen konnte und sich mit einem Fehler verabschiedete!

bild-26Bild 4: Das perfekte Ergebnis, der Quelltext lässt sich nicht mehr einsehen!

Das ist natürlich ein super Ergebnis! Ich habe es nicht an weiteren Programmen ausprobiert, aber ich denke nicht, dass man immer dieses Ergebnis haben wird und natürlich muss man im Hinterkopf behalten, dass auch das nicht sicher ist sondern auch mit genug Zeit ebenfalls geknackt wird!

Auch wenn diese Methode keinen perfekten Schutz bietet (den es eh nicht gibt!) ist sie trotzdem effektiv! Alle Tools die ich hier benutzt habe sind auch kostenlos und leisten dafür sehr viel! Ausprobieren lohnt sich! Als Alternative die aber auch was kostet kann ich euch .NET Reactor ans Herz legen. Dieses Tool arbeitet ebenfalls sehr gründlich sodass es teilweise nicht möglich ist zu dekompilieren kostet aber auch knappe 200 €!

Gut, kommen wir zu der dritten und auch sichersten und besten Lösung! .NET bietet uns die Möglichkeit auf fremdcode zuzugreifen zb. aus der Windows-API oder eigenen DLLs. Man kann auch Code aus dem Internet nachladen oder bestimmte Dienste in WebServices auslagern.

Und das Sollte man machen! So kann man das Hauptprogramm gerne in .NET schreiben, aber man sollte dann die besonders aufwendigen und geheimen Algorithmen und Prozeduren in eigene Native DLLs auslagern, die man dann wiederum ins Programm einbinden kann!

Das garantiert eine einfache Arbeit und höchste sicherheit! Denn auch wenn man dann das Hauptprogramm dekompiliert, ist der wichtige Teil immernoch sicher in der DLL verpackt!

Zum Schluss nochmal: hier habe ich nur .NET behandelt natürlich gibt es auch für Java und auch für Javascript Obfuscators die den selben Job machen, diese aber ebenfalls vorzustellen würde den Rahmen aber sprengen!

Ich hoffe dieser Zweiteiler hat euch gefallen und freue mich auf euer Feedback!

Interessiert dich dieses Thema? Möchtest du hier mehr Beiträge darüber lesen? Schreib ein Kommentar was dir gefallen hat oder was nicht.

VN:F [1.8.4_1055]
Rating: 10.0/10 (3 votes cast)
11Aug/091

Codeklau & wie man eigenen Code schützen kann Teil 1

In dieser Reihe möchte ich darüber schreiben welche Probleme die Arbeit mit modernen Programmiersprachen mit sich bringt und wie man diese möglichst eindämmen und gering halten kann.

In dem ersten Teil werde ich darauf eingehen, wie und warum es möglich ist Code aus fertigen Applikationen zu extrahieren.

Das größte Problem, das jeder Closed-Source Programmierer hat, ist es den erstellten Code nicht für andere einsichtbar zu halten damit dieser nicht geklaut oder ohne Einwilligung oder Lizenzzahlungen weiterverwendet werden kann!

Aber wie ist es überhaupt möglich Code zu klauen wenn mann nur die fertige Applikation hat?

Zuerst sollte man sagen, dass es keinen 100%igen Schutz gibt! Alles kann geknackt oder modifiziert werden. Alle Firmen, die das gegenteil versprechen sind einfach nur unprofessionell. Denn alle Schutzmechanismen die es gibt sorgen lediglich dafür, dass man mehr Aufwand aufbringen muss, um diese wieder zu umgehen.

Früher war Codeklau noch sehr harte Arbeit, denn früher waren alle Anwendungen kompiliert, wurden also beim Erstellen direkt in Maschinencode gewandelt! Diesen Maschinencode wieder in lesbaren Quelltext zu wandeln ist sogut wie unmöglich, was man aber machen kann ist es diesen zu "Deassimblieren" (engl. Disassamble). Bei diesem Vorgang wird der vorliegende Native Code (Maschinencode) in ein für den Menschen lesbaren Assembler Code umgewandelt.

Was man hier noch wissen sollte, ist dass früher (vor langer langer Zeit) alles in Assembler programmiert wurde. Assembler ist eine Sprache, die es dem Programmierer Erleichtert Code zu schreiben, dabei werden Befehle benutzt die der Prozessor, auf dem später der Code ausgeführt werden soll versteht. Somit muss man sich nicht mit Nullen und Einsen herumschlagen sondern hat was, was man auch einigermaßen lesen kann.

5623_2Grafik 1: So könnte ein Programm nach dem Disassamble-Vorgang aussehen

Hier findet ihr ein kleines Beispiel wie sich Assembler von einer Hochsprache wie zb. C++ unterscheidet!

Mit diesem erzeugten Assembler Code kann man nun weiter "arbeiten" und versuchen den Ablauf des Programms zu verstehen und diesen dann auch eventuell zu kopieren, zu klauen! Das ist natürlich je nach Größe extrem aufwendig und langwierig.

Übrigens: genauso gehen die so genannten "Cracker" vor wenn sie versuchen bestimmte Sicherheitmechanismen von Programmen oder Spielen zu deaktivieren. Sie suchen nach diesen Ereignissen in dem Assembler Code und versuchen diese umzuleiten oder zu löschen, sodass die "Gecrackte" Software dann anschließend zb. ohne gültige Lizenz oder ein Spiel ohne die Original CD im Laufwerk benutzt werden kann! Wer darüber mehr wissen will sollte einen Blick in die "Hacker-Bibel von Cyberdemon98" werfen.

Natürlich kann man mit diesem Vorgehen nicht wirklich Code "klauen" sondern mehr versuchen zu verstehen wie ein Programm arbeitet um dann zb. andere Programme darauf abzustimmen ("Schnittstellen" schafften). Diesen Vorgang nennt man dann reverse Engeneering (deut. Umgekehrtes entwickeln).

Ein berühmtes Beispiel dafür ist der Instant Messaging Dienst ICQ, dieses Unternehmen hat keinerlei Spezifikationen über den Dienst oder das Protokoll veröffentlicht. Alle ICQ-Clients wie QIP, Miranda oder andere basieren komplett auf Reverse Engeneering! Deswegen kommt es auch oft, dass man sich gegenseitig keine Dateien schicken kann oder die Status-Nachricht eines Freundes lesen kann weil er einen anderen Client verwendet. Dies liegt einfach daran dass die Entwickler "raten" müssen wie diese Funktionen funktionieren und Kompabilitäten nur schwer geschaffen werden können! Dadurch geht ICQ sicher, dass nur ihr eigener Client perfekt funktioniert und alle Features beherrscht!

c-flow

Grafik 2: Diese Schritte durchläuft C++ Code während der Compilierung

Diese Codevariante, die so genannten unmanaged-code hervorbringt, der aus Maschinencode besteht erstellen die "alten" Programmiersprachen wie Delphi, C, VB6 und C++. Die Obere Grafik zeigt, dass nach der Kompilierung des Quelltextes, dieser noch weitere Schritte durchläuft und schließlich als Binäre Datei vorliegt, die nur auf einem bestimmten CPU-Typ und Betriebssystem läuft!

Die neueren Programmiersprachen wie etwa Microsofts .NET (Worunter auch C#, VB.NET fällt) oder Java verfolgen einen komplett anderen Ansatz der managed code erzeugt und förmlich zum Codeklauen einlädt!

20011114_1xGrafik 3: Compilierung von Managedcode Programmen (.NET & Java)

Die Sprachen, die managed Code erzeugen, erzeugen nämlich keinen Maschinencode sondern so genannte Assemblys welche eine Art Vorkompilierte Programme sind die nicht lauffähig sind.

Grafik 3 beschreibt im Oberen Teil, den Kompilierungs Prozess, der ziemlich einfach gestrickt ist. Der Code wird garnicht "Richtig" kompiliert sondern nur für die spätere Ausführung vorbereitet.

Warum ist das so? Nun heutzutage müssen Programme laufen, und nicht nur auf dem PC auf dem Sie geschrieben wurden sondern auch auf anderen und sogar auf anderen Betriebssystemen. Das ganze nennt sich dann Plattformunabhängigkeit!

Java Programm zb. können auf allen gängigen Betriebssystemen ausgeführt werden. Egal ob Windows, Solaris, Mac OS oder Linux Java Programme funktionieren! Und damit das möglich ist dürfen die Programme nicht kompiliert werden!

Denn wenn ein Programm kompiliert wird wird es in Maschinencode umgewandelt. Und da jede Prozessorarchitektur einen eigenen Maschinencode hat und jedes Betriebssystem auch anders aufgebaut ist kann ein kompiliertes Programm immer nur auf dem System ausgeführt werden für das es kompiliert wurde!

Um dieses Problem zu umgehen werden Java Programme nur Vorkompiliert (zu so genannten Assemblys) und dann wenn die gestartet werden (egal auf welchem Betriebssystem) werden sie erst "richtig" kompiliert, für das System auf dem sie gerade gestartet werden!

Warum ist das gut? Naja, so kann man ein Programm schreiben, und es ohne daran viel zu machen auf allen Systemen benutzen. Leider hat das System auch viele Schattenseiten, denn um ein solches vorkompiliertes Programm ausführen zu können muss eine Virtuelle Maschine installiert sein, die unser vorkompiliertes Programm "zu ende kompiliert" im Falle von Java ist es die JVM (Java Virtual Machine) diese kompiliert das Programm passend zum Betriebssystem und sorgt für dessen korrekte Ausführung.

Desweiteren sind diese Programme natürlich auch langsamer als die Nativen, denn zb. die Java Programme müssen vor deren Start erst kompiliert werden und laufen in einer Virtuellen Maschine, was natürlich auch Geschwindigkeitseinbußen. (vergleichbar mit einem Virtuell ausgeführten Betriebssystem durch VMWare und co)

Aber nun schweife ich schon wieder ab! Was wichtig ist, ist dass die Managedcode Programme nicht in Maschinencode kompiliert werden. Der Zustand in dem sie dann verweilen macht es uns sehr leicht das Programm wieder zurück in Quelltext zu verwandeln und diesen dann zu Klauen!

Dieser Vorgang, bei dem eine Kompilierte Datei wieder in lesbaren Quelltext verwandelt wird nennt man Dekompilierung. Anders als beim Disassambling wird hierbei der Original Quelltext den der Programmierer geschrieben hat fast perfekt wiederhergestellt!

Davon sind alle managedcode Sprachen betroffen, da sie alle nach dem Prinzip aus Bild 3 arbeiten! (zb. Java, C#, VB.NET)

Für jede Sprache gibt es auch den passenden Decompiler, der den Original Quellentext in Sekunden herausspuckt! Viele dieser Decompiler sind hochkomplexe Programme und deswegen auch nicht kostenlos! Besonders wenns an das dekompilieren von Nativen Programmen geht wird man keine kostenlosen Programme finden die brauchbare Ergebnisse liefern!

Um Java Programme (.jar-Dateien) zu dekompilieren empfehle ich den JD Java Compiler, dieser ist kostenlos und arbeitet sehr zuverlässig!

screenshot1Bild 4: Der Java Decompiler in Action

Natürlich muss man auf so etwas wie Kommentare und Ordnungen verzichten, da diese bei dem Kompilierungs Prozess automatisch aus dem Quelltext gelöscht werden, da der Prozessor damit nix anfangen kann!

Um .NET Programme oder DLLs zu dekompilieren, wobei hier disassimbliren besser passt kann ich das Tool .NET Reflector von Redgate empfehlen, es ist einfach zu benutzen und funktioniert perfekt!

Bild 5: Arbeiten mit dem .NET Reflector

Das Gute hierdran ist, dass man sogar in die .NET System Prozesse schauen kann um diese besser zu verstehen! Dieses Programm ist ebenfalls kostenlos nach einer Registrierung zu haben!

Wer das ganze mal ausprobieren will kann das gerne ja mal machen. Sucht euch einfach mal ein einfaches Java Programm, dabei ist es egal ob es ein Desktop Java Programm ist ober ein Mobile Programm aus dem J2ME. zb. den Opera Mini und schaut euch mal dessen Innereien an.

Wenn ihr mal ein .NET Programm dekompilieren wollt kann ich für den Einstieg eins der Programme empfehlen, die ich hier im Blog veröffentlicht habe. (Schaut zb. in dem Letzten Beitrag. Ladet die Beispiel Datei herunter und entpackt den Ordner und geht in den Ordner TreeViewDemo\bin\debug\ dort müsstet ihr eine .exe Datei finden an der ihr euch austoben könnt!)

Als Ergänzung will ich noch sagen, dass ein Programm immer in managedcode UND in unmanagedcode kompiliert werden kann und somit manchmal nicht (so einfach) dekompiliert werden kann!

Wie man seinen Code schützen kann werde ich dann in dem zweiten Teil behandeln, da der hier ja schon ein bissl zuu lang geworden ist!

Interessiert dich dieses Thema? Möchtest du hier mehr Beiträge darüber lesen? Schreib ein Kommentar was dir gefallen hat oder was nicht.

VN:F [1.8.4_1055]
Rating: 9.7/10 (3 votes cast)
18Mai/090

Eigenes Programm mit Parametern starten

Heute gehts darum das eigene Programm um eine sehr sinnvolle Funktion zu erweitern, nämlich um die Nutzung der übergebenen Parameter.

Dies ist besonders nützlich wenn eine Anwendung Informationen verarbeiten soll, die erst nach dem Öffnen des Programms eingegeben werden sollen oder wenn das Programm (ähnlich wie paint oder notepad) ein Dokument duech ainen Doppelklick öffnen soll.

Wenn ihr auf eine Datei doppeltklickt sucht das Betriebssystem erstmal nach einem Programm, das diesen Dateityp verarbeiten kann, das sogenannte Standardprogramm. Ist das gefunden, wird es gestartet und der Pfad zu der von euch doppeltgeklickten Datei als Parameter übergeben.

Dabei wird ein Befehl erzeugt, der ca so ausschaut: notepad.exe "C:\Ordner\datei.txt" dadurch wird notepad.exe gestartet und der Parameter, der hier aus dem Dateipfad besteht übergeben. So weis Notepad, dass eine Datei geöffnet werden soll und versucht dies zu tun.

Also besteht unsere Aufgabe darin, diesen Parameter auszulesen, und dafür stellen die Programmiersprachen freundlicherweise Funktionen bereit. Ich werde hier auf ein Beispiel in Java und VB.NET eingehen.

Fangen wir mit Visual Basic an. Alle Parameter, die dem Programm übergeben werden werden in der Variable Command in Anführungszeichen gespeichert. Diese muss weder importiert noch deklariert werden und ist sofort verfügbar!


Public Class Form1
    Private Sub Form1_Loaed() Handles MyBase.Load
        TextBox1.Text = Command()
    End Sub
End Class

Mit dem obenstehenden Code werden alle übergebenen Parameter in Textbox1 Dargestellt. Wenn wir zB. wollen, dass wenn man eine Datei auf das Symbol unserer Anwendung zieht, diese dann in der TextBox2 der Anwendung geöffnet wird müssen wir so vorgehen:


Public Class Form1
    Private Sub Form1_Loaed() Handles MyBase.Load
        TextBox1.Text = Command()

        'Prüfen, ob überhaupt etwas übergeben wird
        If Command() <> "" Then
            Try
                'Inhalt der Übergebenen Datei auslesen
                'und die Anführungszeichen aus dem Parameter filtern
                TextBox2.Lines = System.IO.File.ReadAllLines( _
                    Command().Replace("""", "") _
                )
            Catch ex As Exception
                'Einen Fehler anzeigen falls die übergebenen Parameter kein Dateipfad sind
                MessageBox.Show("Die übergebenen Parameter verweisen auf keine Datei!" _
                                & vbNewLine & ex.Message)
            End Try
        End If
    End Sub
End Class

Es kann natürlich auch vorkommen, dass man mehr als eine Datei öffnen will (zB. ein ganzen Haufen mp3s) man markiert also mehrere Dateien und zieht sie dann auf unsere Anwendung. In diesem Fall ahben wir pech gehabt, denn Windows sendet die Parameter dann so: notepad.exe "C:\Ordner\datei1.txt" "C:\Ordner\datei2.txt" "C:\Ordner\datei3.txt"

Nun haben wir 3 Dateinamen im Parameter die wir erst einmal isolieren müssen:


Public Class Form1
    Private Sub Form1_Loaed() Handles MyBase.Load
        TextBox1.Text = Command()

        'Prüfen, ob überhaupt etwas übergeben wird
        If Command() <> "" Then
            Try
                'Die Parameter Splitten nach jedem >" "<
                Dim Dateien() As String = Command().Split(""" """)
                For Each s As String In Dateien
                    'Alle Parameter in Textbox2 Schreiben
                    TextBox2.Text = TextBox2.Text & vbNewLine & s
                Next
            Catch ex As Exception
                'Einen Fehler anzeigen falls die übergebenen Parameter kein Dateipfad sind
                MessageBox.Show("Die übergebenen Parameter verweisen auf keine Datei!" _
                                & vbNewLine & ex.Message)
            End Try
        End If
    End Sub
End Class

Falls ihr euch fragt, warum überhaupt die Anführungszeichen mitgesendet werden und nicht nur ein Leerzeichen verwendet wird, so gibts dafür eine sehr einfache Erklärung. Denn nur so lässt sich feststellen wann ein Befehlt anfängt und wann er aufhört!

Übrigens bietet das .NET Framework auch eine sehr mächtige Klasse extra für das auslesen der Parameter an: My.Application.CommandLineArgs hier gibt es ein paar Funktionen die für euch ein paar Dinge übernehmen, wie zb das Zählen der übergebenen Parameter! Command() ist eigentlich ein Überbleibsel aus der VB6 Zeit, der wegen der Abwärtskomperbilität noch übernommen wurde. Auf die My.Application.CommandLineArgs Klasse werde ich später noch zu Sprechen kommen!

Sehen wir uns doch mal an wie Java die Parameter handelt:


package commandtest;

/**
 *
 * @author Sebastian Gross || http://blog.bogbasti.com
 */
public class Main {

    public static void main(String[] args) {

    }

}

In Java wird die Variable, die die Start Parameter die Command Line Arguments enthällt vom Benutzer mit deklarert! Hier trägt Sie den namen args und ist immer im der Main-Methode einer jeden Klasse deklariert.

Desweteren können wir erkennen, dass diese Varibale ein Array ist, das heißt wir können uns hier das Splitten der Befehle sparen! Alles andere Funktioniert aber genauso wie bei VB:


package commandtest;

/**
 *
 * @author Sebastian Gross || http://blog.bogbasti.com
 */
public class Main {

    public static void main(String[] args) {
        System.out.println("Die Parameter:");
        for(String s : args){
            System.out.println(s);
        }
    }
}

Man bedarf also nur einer simplen For-Each Schleife um alle Übergebenen Parameter auszulesen und dann gegebenenfalls weiter zuverarbeiten.

VN:F [1.8.4_1055]
Rating: 0.0/10 (0 votes cast)
Get Adobe Flash playerPlugin by wpburn.com wordpress themes