Hallo zusammen!
Ich möchte für eine Anwendung ein einfaches Plugin-System realisieren. Die Applikation durchsucht nach dem Start ein definiertes Unterverzeichniss nach *.jar Dateien, öffnet diese und sucht via Reflection nach allen nicht abstrakten Klassen die von der abstrakten Klasse PluginFactory erben. Von diesen Klassen erwarte ich eine parameterlose statische öffentliche Methode createFactory, die mir eine Factory für dieses jeweilige Plugin liefert.
Soweit so gut, das funktioniert bisher einwandfrei. Mich stört an dieser Lösung nur, dass es (scheinbar) keine Möglichkeit gibt die statische Methode zu erzwingen, so dass es zu einem Compilerfehler o.ä. kommt wenn eine entsprechende Klasse nicht eine eigene Version der factory-Methode implementiert. Einfach gesagt
[JAVA=1337]
public abstract class PluginFactory {
public abstract static PluginFactory createFactory() ;
}
[/code]
wäre genau das was ich gerne hätte. Leider steht das ja etwas im wiederspruch mit OO-Denke und dem was Java bietet.
Meine Idee war nun eine annotation zu erstellen, die von einem eigenen annotation processor ausgewertet wird. Der annotation gebe ich den Namen der statischen Factory Methode mit. Annotiert durch @Inherits wird die Annotation ja auch an die ableitungen weiter vererbt.
Der annotation processor muss nun alle gekennzeichneten Klassen durchlaufen und prüfen ob diese eine statische öffentliche Methode des gesetzten Namens imlementiert.
Hier die annotation:
und hier das wichtigste aus dem annotation Processor
Der Code oben ist etwas abgewandelt um den Fehler leichter zu finden, also die prüfung auf abstract in der class und den Rückgabetyp der Methode habe ich mal aussen vorgelassen.
Der Code wirft ja viele Warnings, welche mir eclipse auch schön brav auswirft.
Auf folgende Beispielklassen wird das ganze nun angewandt
Die Warnings die der Compiler wirft sind im anhang zu finden.
Die erste Warnung kann ignoriert werden, da der Check für die Classe SuperClass ja ausgeführt wurde.
Die beiden nächsten sind interessanter: found occurence für SuperClass und SubClass. Die vererbung der annotation hat also geklappt.
Die 4te Warnung method accepted ist auch sehr schön, da die gesuchte Methode in der Klasse SuperClass gefunden wurde.
Die 5te Warnung gibt den Namen der gesuchten methode an, der passt auch.
Allerdings wird scheinbar in der Klasse SubClass keinerlei Prüfung durchgeführt, obwohl die Klasse vom Annotation processor gefunden wurde.
Ich hab jetzt etliche Stunden google nach diesem Thema gelöchert aber nichts brauchbares gefunden. Hat von euch jemand eine Idee was falsch sein könnte, oder wie mein Problem gelöst werden könnte?
(Und bitte keine Belehrungen darüber, dass es mehr als einen Grund gibt, warum statische methoden nicht vererbbar sind. Sowohl die Tatsache als auch die Gründe sind mir hinreichend bekannt.
)
Gruß
Adalbert
Ich möchte für eine Anwendung ein einfaches Plugin-System realisieren. Die Applikation durchsucht nach dem Start ein definiertes Unterverzeichniss nach *.jar Dateien, öffnet diese und sucht via Reflection nach allen nicht abstrakten Klassen die von der abstrakten Klasse PluginFactory erben. Von diesen Klassen erwarte ich eine parameterlose statische öffentliche Methode createFactory, die mir eine Factory für dieses jeweilige Plugin liefert.
Soweit so gut, das funktioniert bisher einwandfrei. Mich stört an dieser Lösung nur, dass es (scheinbar) keine Möglichkeit gibt die statische Methode zu erzwingen, so dass es zu einem Compilerfehler o.ä. kommt wenn eine entsprechende Klasse nicht eine eigene Version der factory-Methode implementiert. Einfach gesagt
[JAVA=1337]
public abstract class PluginFactory {
public abstract static PluginFactory createFactory() ;
}
[/code]
wäre genau das was ich gerne hätte. Leider steht das ja etwas im wiederspruch mit OO-Denke und dem was Java bietet.
Meine Idee war nun eine annotation zu erstellen, die von einem eigenen annotation processor ausgewertet wird. Der annotation gebe ich den Namen der statischen Factory Methode mit. Annotiert durch @Inherits wird die Annotation ja auch an die ableitungen weiter vererbt.
Der annotation processor muss nun alle gekennzeichneten Klassen durchlaufen und prüfen ob diese eine statische öffentliche Methode des gesetzten Namens imlementiert.
Hier die annotation:
Java:
package test;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Documented
@Retention( RetentionPolicy.RUNTIME)
@Target( ElementType.TYPE )
public @interface StaticInherit {
public String value();
}
und hier das wichtigste aus dem annotation Processor
Java:
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// for (Element element : roundEnv.getRootElements()) {
// checkClass(element);
//
// }
for (TypeElement type : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(type)) {
checkClass(element);
}
}
return true;
}
private void checkClass(Element element) {
boolean found = false;
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "found occurence: " + element.toString(), element);
StaticInherit inherit = element.getAnnotation(StaticInherit.class);
String name = inherit.value();
if (name == null || "".equals(name))
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "no value: " + element.toString(), element);
else
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "value: " + name + " " + element.toString(), element);
for (ExecutableElement execelement : ElementFilter.methodsIn(element.getEnclosedElements())) {
if (execelement.getSimpleName().toString().equals(name)) {
if (execelement.getModifiers().contains(Modifier.PUBLIC)
&& execelement.getModifiers().contains(Modifier.STATIC)) {
found = true;
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "method accepted: " + execelement.toString() + " "
+ System.currentTimeMillis(), execelement);
break;
}
}
}
if (!found) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "public static method called " + name + "() not implemented!", element);
} else {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "check not performed!", element);
}
}
Der Code oben ist etwas abgewandelt um den Fehler leichter zu finden, also die prüfung auf abstract in der class und den Rückgabetyp der Methode habe ich mal aussen vorgelassen.
Der Code wirft ja viele Warnings, welche mir eclipse auch schön brav auswirft.
Auf folgende Beispielklassen wird das ganze nun angewandt
Java:
package test.banane;
import test.StaticInherit;
@StaticInherit("createFactory")
public abstract class SuperClass {
public static void createFactory() {
}
}
Java:
package test.banane;
public class SubClass extends SuperClass{
}
Die Warnings die der Compiler wirft sind im anhang zu finden.
Die erste Warnung kann ignoriert werden, da der Check für die Classe SuperClass ja ausgeführt wurde.
Die beiden nächsten sind interessanter: found occurence für SuperClass und SubClass. Die vererbung der annotation hat also geklappt.
Die 4te Warnung method accepted ist auch sehr schön, da die gesuchte Methode in der Klasse SuperClass gefunden wurde.
Die 5te Warnung gibt den Namen der gesuchten methode an, der passt auch.
Allerdings wird scheinbar in der Klasse SubClass keinerlei Prüfung durchgeführt, obwohl die Klasse vom Annotation processor gefunden wurde.
Ich hab jetzt etliche Stunden google nach diesem Thema gelöchert aber nichts brauchbares gefunden. Hat von euch jemand eine Idee was falsch sein könnte, oder wie mein Problem gelöst werden könnte?
(Und bitte keine Belehrungen darüber, dass es mehr als einen Grund gibt, warum statische methoden nicht vererbbar sind. Sowohl die Tatsache als auch die Gründe sind mir hinreichend bekannt.
Gruß
Adalbert