Durchsuchen des Classpath

Das JDK bringt von Haus aus bereits ein mächtiges Framework mit sich, mit dem sich zur Runtime Informationen über die Applikation gewinnen lassen, wie auch schon in einem früheren Beitrag beschrieben. Dies funktioniert solange einwandfrei als ich weiss auf welche Klasse ich die Reflection los lasse.
Mit Annotation lässt sich eine Vielzahl von Dingen bewerkstelligen, so kann man auch auf den Gedanken verfallen, dass man Bestimmte Aufgaben definiert (beispielsweise in einer Enumeration). Die Ausführung dieser Aufgaben übernehmen Methoden mit einer nicht näher bekannten Signatur. Was aber bekannt ist, ist dass die Methode mit einer Annotation gekennzeichnet ist, die besagt, dass diese Methode jene Aufgabe erfüllt.
Wie gelange ich nun an die Klasse in der die Methode definiert ist? Nur mit dem JDK ist dies nicht möglich. Dazu müssen aus dem Classpath alle Klassen ausgelesen werden. Dann kann jede einzelne Klasse nach der Methode untersucht werden, die die spezifizierte Annotation besitzt.
Wem dies alles etwas zu abstrakt war hier das Beispiel:

public class ClassFinder {
        /**
         * Defined classpath
         */
	private static final String CLASSPATH = System.getProperty("java.class.path");
	/**
	 * List with the jar files on the classpath
	 */
	private static String[] jarFiles;
	/**
	 * List with the directories on the classpath (containing .class files)
	 */
	private static String[] binDirs;
	/**
	 * All Classpath elements
	 */
	private static File[] classPathDirs = null;
	/**
	 * Default constructur initializes the directories indicated by the
	 * CLASSPATH, if they are not yet initialized.
	 */
	public ClassFinder() {
		if (classPathDirs == null) {
			initClassPathDir();
		}
	}
	/**
	 * Initialize the directories based on the classpath
	 */
	private void initClassPathDir() {
		StringTokenizer st = new StringTokenizer(CLASSPATH, File.pathSeparator);
		int count = st.countTokens();
		classPathDirs = new File[count];
		Vector jar = new Vector();
		Vector bin = new Vector();
		for (int i = 0; i < count; i++) {
			classPathDirs[i] = new File(st.nextToken());
			if (classPathDirs[i].isDirectory()) {
				bin.add(classPathDirs[i].getAbsolutePath());
			} else {
				jar.add(classPathDirs[i].getAbsolutePath());
			}
		}
		jarFiles = new String[jar.size()];
		binDirs = new String[bin.size()];
		jar.copyInto(jarFiles);
		bin.copyInto(binDirs);
	}

	/**
	 * Retrive all classes of the indicated package. The package is searched in
	 * all classpath directories that are directories
	 * 
	 * @param packageName
	 *            name of the package as 'ch.sahits.civ'
	 * @return Array of found classes
	 * @throws ClassNotFoundException
	 */
	public Class[] getAll(String packageName) throws ClassNotFoundException {
		String packageDir = convertPackege(packageName);
		Vector classes = new Vector();
		for (int i = 0; i < binDirs.length; i++) {
			packageDir = binDirs[i] + File.separator + packageDir;
			File dir = new File(packageDir);
			classes.addAll(extractClasses(packageName, dir));
		}
		Class[] result = new Class[classes.size()];
		classes.copyInto(result);
		return result;
	}
	/**
	 * Extract all the classes from a directory
	 * @param packageName name of the package as 'ch.sahits.civ'
	 * @param dir Package as directory
	 * @return Vector with all found directories
	 * @throws ClassNotFoundException
	 */
	private Vector extractClasses(String packageName, File dir) throws ClassNotFoundException {
		Vector classes = new Vector();
		File[] files = dir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String filename) {
				return filename.endsWith(".class");
			}
		});
		if (files!=null) {	// directories without .class files may exist
			for (int j = 0; j < files.length; j++) {
				String className = packageName + "." + files[j].getName();
				className = className.substring(0, className
						.lastIndexOf(".class"));
				classes.add(Class.forName(className));
			}
		}
		return classes;
	}
	/**
	 * Convert the package name into a relative directory path
	 * @param packageName name of the package as 'ch.sahits.civ'
	 * @return relativ directory to the package
	 */
	private String convertPackege(String packageName) {
		String sep = File.separator;
		return packageName.replace(".", sep);
	}
	/**
	 * Retrive all classes of the indicated package and all subpackages. The package is searched in
	 * all classpath directories that are directories
	 * 
	 * @param packageName
	 *            name of the package as 'ch.sahits.civ'
	 * @return Array of found classes
	 * @throws ClassNotFoundException
	 */
	public Class[] getAllRecursive(String packageName) throws ClassNotFoundException {
		String packageDir = convertPackege(packageName);
		Vector classes = new Vector();
		for (int i = 0; i < binDirs.length; i++) {
			packageDir = binDirs[i] + File.separator + packageDir;
			File dir = new File(packageDir);
			classes.addAll(extractClasses(packageName, dir));
			if (dir.isDirectory()) {
				File[] sub = dir.listFiles();
				for (int j = 0; j < sub.length; j++) {
					if (sub[j].isDirectory()) {
						Class[] rec = getAllRecursive(packageName + "."
								+ sub[j].getName());
						Vector temp = new Vector(rec.length);
						for (int k = 0; k < rec.length; k++) {
							temp.add(rec[k]);
						}
						classes.addAll(temp);
					}
				}
			}
		}
		Class[] result = new Class[classes.size()];
		classes.copyInto(result);
		return result;
	}
}

Vielleicht sollte an dieser Stelle noch bemerkt werden, dass ein solches Vorgehen wohl überlegt sein soll, denn nur mit diesen Informationen ist es nicht nicht möglich, die Methode auszuführen, den dazu wird noch ein Objekt benötigt, auf welchem die Methode ausgeführt werden kann und wenn man dieses hat, kann man sich den Umweg über den Classpath sparen.

Datenstrukturen in Java

Datenstrukturen und Java klingt im ersten Moment nach einem Widerspruch: Java hat Objekte und keine Strukturen. Bei näherer Betrachtung sieht man jedoch, dass die vermeintlichen Datenstrukturen zumindest in C++ als Klassen und somit als Objekte realisiert sind – alleine die Bezeichnung ist noch ein Überbleibsel aus prozeduralen C-Zeiten.
Wir können also festhalten, dass eine Datenstruktur Attribute und Funktionen hat (um die Sprache möglichst neutral zu halten). Unterschiedliche Datenstrukturen haben unterschiedliche Attribute und Funktionen. Dies steht im Gegensatz zum objektorientierten Ansatz der Wiederverwendung von Objekten.
Java bietet von Haus aus bereits ein grosses Repertoire (JDK) wo man mit C++ die STL bemühen muss. So lässt sich in Java eine FIFO Queue bereits durch die Klassendefinition und dem Überschreiben von remove vollständig implementieren:

public class FIFOList extends LinkedList implements Queue{
    public Object remove() {
        return remove(0);
    }
}

Beispiel von: Simple Thoughts
Wenn ich jedoch den durch die Struktur beschriebenen Funktionsumfang anschaue muss ich feststellen, dass die Funktionalität zwar der einer FIFO Queue entspricht, aber bereits die Methodennamen leicht verwirrlich sind. Zudem gibt es zahlreiche Funktionen, die mit einer FiFo Queue nichts zu tun haben. Ein C++ Entwickler, der nur die Signaturen sieht kann nicht erkennen, worum es sich handelt. Zudem weiss ich mit einer solchen Umsetzung nicht wie die Queue funktioniert.
Aus diesem Grund habe ich die C++ Implementation genommen, sie für Java angepasst und mit einigen convenient Methoden ergänzt:

public class FiFoQueue {
	private FiFoNode first=null;
	private FiFoNode last=first;
	public FiFoQueue(){		
	}
	// Put a new elemnt in the queue
	public void enqueue(T element){
		FiFoNode temp = new FiFoNode();
		temp.value=element;
		temp.next=last;
		last=temp;
	}
	// Get the element from the queue that is in front
	public T dequeue(){
		FiFoNode temp = first;
		first=first.next;
		return temp.value;
	}
	// Check if the queue is empty
	public boolean isEmpty(){
		return first==last && first==null;
	}
	// Empty the queue
	public void empty(){
		first=last=null;
	}
	class FiFoNode{
		FiFoNode next;
		T value;
		FiFoNode(){
		}
	}
}

Mehrfachvererbung in Java

Im allgemeinen ist wahrscheinlich so, dass das Unterbinden von Mehrfachvererbung eher ein Segen als ein Fluch ist. Doch wie immer gibt es auch hier Fälle, wo dies unumgänglich ist. Dabei müchte ich nicht auf die Gründe eingehen. Es sei lediglich der Hinweis erlaubt: Es ist in jedem Fall weniger aufwändig, ohne Mehrfachvererbung aus zukommen. Somit ist der Aspekt ob Mehrfachvererbung tatsächlich notwendig ist zu überprüfen. Wie der Umstand von Single Inheritance umgangen werden kann soll hier am Beispiel der Programmiersprache Java erläutert werden.

  1. Beide Klassen, die Abzuleiten sind liegen im eigenen Namensraum, d.h. beide Klassen gehören zum selben Projekt wie die zu erstellende Ableitung. Hier wählt man eine Klasse aus von der man ableitet. Von der anderen Klasse erstellt man ein Interface, das man implementiert. Dieses vorgehen legt nahe, dass man für das Interface die Klasse mit weniger Implementationsaufwand verwendet. Wenn möglich sollte hier auch die erwartete API Stabilität einfliessen. D.h. eine Klasse deren API, oder zu einem geringeren Grad die Implementation, noch ändern kann sollte wo möglich nicht als Interface verwendet werden, da hier aufgrund der Anpassung der Klasse, das Interface und dann die „abgeleitete“ Klasse angepasst werden muss.
  2. Ähnlicher Fall wie oben, nur dass hier die eine Klasse ausserhalb des Projekts liegt. Liegt diese Klasse jedoch noch innerhalb des eigenen Einflussbereichs, z.B. anderes Projekt des Teams, so kann selbes vorgehen wie oben angewandt werden; andernfalls ist die externe Klasse prädestiniert für ein Interface zumal hier davon ausgegangen werden kann, dass die API stabil ist.
  3. Beide Klassen liegen Ausserhalb des Einflussbereichs. Eine der beiden Klassen implementiert ein Interface, das die essentiellen Methoden enthält. In diesem Fall kann dieses Interface implementiert werden und die andere Klasse implementiert werden.
  4. Trifft keiner der obigen Punkte zu so muss eine komplexere Lösung gefunden werden. Komplexer ist sie weil sie mehr Aufwand bedeutet, aber auch weil sie einer echten Mehrfachvererbung am nächsten kommt: Man wählt den Ansatz, dass man eine der beiden Klassen ableitet und von der anderen eine Reimplementation vornimmt. Handelt es sich hierbei um eine Klasse von Sun, so kann sie dem src.zip entnommen werden. Zum Beispiel:
    public class ObservableThread extends Thread {
        private boolean changed = false;
        private Vector obs;
        public ObservableThread() {
    	obs = new Vector();
        }
        public synchronized void addObserver(Observer o) {
            if (o == null)
                throw new NullPointerException();
    	if (!obs.contains(o)) {
    	    obs.addElement(o);
    	}
        }
        public synchronized void deleteObserver(Observer o) {
            obs.removeElement(o);
        }
        public void notifyObservers() {
    	notifyObservers(null);
        }
       public void notifyObservers(Object arg) {
            Object[] arrLocal;
    
    	synchronized (this) {
    	    if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(new HiddenObservable(this), arg);
        }
        public synchronized void deleteObservers() {
    	obs.removeAllElements();
        }
        protected synchronized void setChanged() {
    	changed = true;
        }
        protected synchronized void clearChanged() {
    	changed = false;
        }
        public synchronized boolean hasChanged() {
    	return changed;
        }
        public synchronized int countObservers() {
    	return obs.size();
        }
    

    Hierbei handelt es sich jedoch noch nicht um eine echte Mehrfachvererbung. In vielen Fällen sollte dies jedoch schon genügen. Wo nicht kann der Mehrfachvererbung mehr Authentizität verliehen werden indem von der ursprünglichen Klasse die reimplementiert wird ein Interface gebildet wird und dieses implementiert wird. Nun hat die Klasse beide Typen (Thread und Observable). Eine andere oder weitere Möglichkeit – insbesondere wenn this semantisch mit der ursprünglichen Klasse verwendet wird – besteht darin eine versteckte Inner Class zu bilden, die die reimplementierte Klasse ableitet und alle Methoden, die von dieser Klasse geerbt werden, überschreibt. Die Implementation der Methoden reicht die Ausführung an die selbe Methode in der äusseren Klasse weiter:

        private class HiddenObservable extends Observable{
        	        private ObservableThread ot;
         	        public HiddenObservable(ObservableThread obersevable){
        		       ot = obersevable;
        	        }
    		public synchronized void addObserver(Observer o) {
    			ot.addObserver(o);
    		}
    		protected synchronized void clearChanged() {
    			ot.clearChanged();
    		}
    		public synchronized int countObservers() {
    			return ot.countObservers();
    		}
    		public synchronized void deleteObserver(Observer o) {
    			ot.deleteObserver(o);
    		}
    		public synchronized void deleteObservers() {
    			ot.deleteObservers();
    		}
    		public synchronized boolean hasChanged() {
    			return ot.hasChanged();
    		}
    		public void notifyObservers() {
    			ot.notifyObservers();
    		}
    		public void notifyObservers(Object arg) {
    			ot.notifyObservers(arg);
    		}
    		protected synchronized void setChanged() {
    			ot.setChanged();
    		}
        }
    

    Zu beachten: Dies ist nötig, da im ersten Code-Teil (bereits angepasst) ein Aufruf mit this gemacht wird und this nun mal vom Typ Observable und nicht vom Typ Thread oder ObservableThread ist.
    Einziger Nachteil dieser Lösung: instanceof Observable wird immer false zurückgeben. Solange diese Abfrage jedoch im eigenen Code ist, spielt dies keine weitere Rolle.

The Mathias Prophecy by Esined

The Mathias Prophecy is a trilogy of three books that describe the occurrences during Harry’s sixth and seventh school year.
Already in the first chapter Esined drops a bombshell: Harry is a girl. Also surprising can be that Sirius is not dead. As to why that is the reader has to wait until the third book where it is revealed, that Harry’s and his friends memory were modified concerning their fifth year.
To the extraordinary powers of Harry count: Magical animagus (Phoenix), Healing-Power, Telekinesis and Empathy.
There is also the second prophecy that states that Harry has the power to destroy Voldemort. On Halloween Harry is sent back in time when her parents die, but they decide the same way and therefore insure, that Harry survives. Before Easter Hermione turns the snitch into a portkey that sent Harry to him. She escapes with the help of Draco.
Based on the revelation Snape changes his attitude towards Harry. Until the end of term Harry develops deep feelings for her protectors Sirius, Remus and Snape.
During the summer Harry stays at the Dursleys with frequent trip’s to Snapes town-house. The Dursley are under Malfoys control and concoote a potion that allows him access to the house. After her birthday she undergoes a formal protector ship with her protectors, Dumbledore as the Trustee and Ron with the Right of Vision. While at Snape’s she becomes like a sister to Circe’s daughter Phaedra, Serverus‘ niece.
On Christmas Harry and Phaedra are kidnapped but can escape.
In may Volemort attracts the school after Harry is visited by an old women who gives her the confidence to win. Harry let herself being whisked away by Voldemort, who tries to rape her. Coming this close Harry manages to kill him with her mothers wand that has been found on the Dursleys‘ attic.
The stories can be read online at Schnoogle: The Mathias Prophecy Book I, The Mathias Prophecy II and The Mathias Prophecy III.
There are also PDFs of the books: The Mathias Prophesy Book I, The Mathias Prophesy Book II and The Mathias Prophesy Book III

The Real Epilogue

Because DrT was not happy with Rowlings epilogue he wrote his own version. Key point is that Ginny and Harry and Ron and Hermione bonded and Ron became bonded to Harry as vasall which meas Harry has the higher rights over Hermione. But only Ginny was aware of this as the others thought the bonding to be a dream. This fact messing the possible future thoroughly up.
On an other note Harry and Hermione achieve to take influence on re rebuilding of wizard society in Britain.
This fic is certainly not one of DrTs best, but considering the material he ha to work with …
The story can be read online at Ficwad.
There is also a PDF version available.