2009-03-09 2 views
2

Während ich Hudson für kontinuierliche Integrationstests (auf einem JeOS-Server) eingerichtet habe, bin ich auf ein merkwürdiges Verhalten gestoßen, auf das ich hoffe, dass die netten Leute von SO mir das erklären können.Ist die Java-Reflektion über vererbte Methoden in Windows und Linux anders?

Unsere Komponententests hängen stark von der Verwendung von Domänenobjekten mit vielen Eigenschaften ab, die festgelegt werden müssen (aufgrund von Nullbedingungen in der Datenbank). Um unsere Tests lesbar zu halten, haben wir eine Klasse InstantiationUtils erstellt, die ein Objekt instanziiert und eine Reihe von Eigenschaften durch Reflexion gesetzt:

public static <T> T newInstance(final Class<T> type, final KeyValuePair<?>... propertyValues) { 

    return ReflectionUtils.reflectionOperation(new ReflectionOperation<T>() { 

     @Override 
     public T perform() throws Exception { 

      T object = type.newInstance(); 
      for (KeyValuePair<?> propertyValue : propertyValues) { 

       String propertyName = propertyValue.getKey(); 
       Object value = propertyValue.getValue(); 
       String setterName = "set" + StringUtils.capitalize(propertyName); 
       ReflectionUtils.invoke(object, setterName, value); 
      } 
      return object; 
     } 
    }); 
} 

public static void invoke(final Object target, final String methodName, final Object... params) { 

    List<Class<?>> parameterTypes = ListUtils.map(asList(params), "class"); 
    Class<?> targetClass = target.getClass(); 
    Method method = MethodUtils.getMatchingAccessibleMethod(targetClass, methodName, 
     parameterTypes.toArray(new Class<?>[] {})); 
    invoke(target, method, params); 
} 

public class Foo { 
    private String foo; 

    public void setFoo(final String foo) { 
     this.foo = foo; 
    } 
} 

public class Bar extends Foo { 
    private String bar; 

    public void setBar(final String bar) { 
     this.bar = bar; 
    } 
} 

Die Person, die leider dieser Code nicht mehr funktioniert für uns geschrieben hat, aber als Soweit ich sehen kann, ist nichts falsch daran. Das gilt auch für Windows - wir verwenden InstantiationUtils während unserer Komponententests ohne Probleme.

Linux ist jedoch anders. Es stellt sich heraus, dass die Methode newInstance() in Linux nur für direkte (d. H. Nicht geerbte) Elemente der Klasse, die wir instanziieren möchten, funktioniert.

InstantiationUtils.newInstance (Bar.class, "bar", "12345"); wird funktionieren, während InstantiationUtils.newInstance (Bar.class, "foo", "98765");

xxx.xxx.xxx.ReflectionUtils $ Reflection: java.lang.NoSuchMethodException: Eigenschaft 'foo' hat keine Setter-Methode

Unter Windows wird sowohl mit folgenden Ausnahme wird auf Linux scheitern Aufrufe funktionieren (ich weiß, dass die newInstance-Signatur nicht übereinstimmt; wir haben mehrere überladene newInstance() -Methoden, die die Parameter in KeyValuePairs konvertieren).

Ich hatte eine schwere Zeit zu akzeptieren, dass vererbte öffentliche Methoden anders behandelt werden, also habe ich dies auf alle möglichen Arten getestet. Und es endet immer mit der Schlussfolgerung, dass wir unter Linux, zumindest mit der obigen Verwendung von Reflection, nicht auf öffentliche vererbte Methoden zugreifen können.

Unter Windows verwende ich Suns JRE 1.6.0.11, in Linux ist es auch Sun, aber Version 1.6.0.7.

Kann jemand bestätigen, ob das richtig ist? Oder ist die Reflection-Verwendung fehlerhaft?

+0

Verwenden Sie die gleiche Version der JRE auf beiden Betriebssystemen? Das klingt nach einer viel wahrscheinlichen Ursache für Diskrepanzen als das Betriebssystem. Wenn Sie Ihren Code zu einem einzigen vollständigen Beispiel zusammenfassen könnten, wäre es einfacher zu überprüfen und zu erkunden. –

+0

"Linux ist jedoch anders. Es stellt sich heraus, dass die Methode newInstance() in Linux nur für direkte (d. H. Nicht vererbte) Mitglieder der Klasse funktioniert, die wir instanziieren möchten." 23 Tage früher. – phihag

Antwort

1

Rätsel teilweise gelöst:

MethodUtils.getMatchingAccessibleMethod() funktioniert anscheinend anders auf Linux und Windows.

Wenn Sie stattdessen MethodUtils.getAccessibleMethod() verwenden, funktioniert es. Ich weiß es nicht, aber ich vermute, dass MethodUtils die Parameterliste irgendwie falsch interpretiert, wenn es darum geht herauszufinden, welche Signatur die Methode haben sollte.

Ich mag würde mehr Zeit zu untersuchen, dies zu verbringen, aber wie immer gibt es Dinge zu tun und Projekte zu liefern, also muss ich einfach akzeptieren, dass getAccessibleMethod arbeitet, und ziehen weiter :-)

Dank jeder für ihre Eingabe!

2

Könnte es sein, dass die SecurityManager Einstellungen zwischen den verschiedenen Java-Laufzeiten unterschiedlich sind?

Sicherlich bezweifle ich, dass dies ein Plattform Problem - es ist fast sicher etwas mit der JRE-Version/Setup zwischen den beiden Umgebungen zu tun

Sie wirklich brauchen, um den Quellcode MethodUtils.getMatchingAccessibleMethod

posten
+0

Ich habe darüber nachgedacht, aber normalerweise, wenn es ein SecurityManager-Problem gibt, bekomme ich eine sehr spezifische Ausnahme, die genau das angibt. In diesem Fall handelt es sich um NoSuchMethodException und keine Anzeichen von SecurityManager im Stack-Trace. –

+0

Das ist ein guter Punkt –

0

Welche JVM verwenden Sie unter Linux, Sun, GCJ usw.? Wenn Sie etwas anderes als Sun JVM verwenden, können Sie versuchen, es zu installieren und sehen, ob das einen Unterschied macht.

+0

Windows ist Sun 1.6.0.11, Linux ist Sun 1.6.0.7. Ich werde versuchen, beide auf 1.6.0.12 zu aktualisieren und zu sehen, was passiert. –

+0

Das gleiche Verhalten mit 1.6.0.12, schlägt immer noch unter Linux fehl. –

0

Haben Sie unterschiedliche Sprachumgebungen? StringUtils.capitalize(propertyName) kann unterschiedliche Ausgabe produzieren.

+0

Die Schauplätze sind eigentlich anders.Aber ich testete die Ausgabe von Großbuchstaben(), und es scheint richtig zu sein. –

+0

Nun, was haben Sie getestet? Zugegeben, es ist unwahrscheinlich, aber es könnte eine Eigenschaft mit einem Nicht-ASCII-Buchstaben mit seltsamen Großschreibung Regeln, wie die türkische dotless i –

3

Sie verwenden MethodUtils, und es hat einig limitations:

Bekannten Einschränkungen

Zugriff auf öffentliche Methoden in einem Standard-Zugriffssuperklasse

Es ist ein Problem beim Aufruf von öffentlichen Methoden in einem enthaltenes Standardzugriffs-Superklasse. Reflection lokalisiert diese Methoden und ordnet sie korrekt als öffentlich zu. Wenn die Methode aufgerufen wird, wird jedoch eine IllegalAccessException ausgelöst.

Eine andere Sache ist zu prüfen, ob die setFoo() -Methode überlastet ist, kann dies auch das Problem verursachen kann ...

+0

Alle guten Punkte :-) Aber setFoo() ist nicht überlastet, und alle Klassen in der Hierarchie sind öffentlich . –

0

Haben Sie schon Ihre CLASSPATH? Nimmst du verschiedene Versionen der Klasse auf, die du instanziieren willst, abhängig davon, auf welcher Plattform du bist? (? ZB alte Codebases um usw. liegen)

1

Ein paar Dinge zu versuchen ...

Unter Linux versuchen, den Code ohne reflektierende Aufruf getFoo comping() - wenn es nicht dann Reflexion kompilieren hat keine Hoffnung zu arbeiten (gut, es hängt davon ab, wie Sie die CLASSAPTH zur Laufzeit einrichten ...)

Versuchen Sie den folgenden Code hinzufügen und führen Sie es auf Linux und Windows.

final Properties properties; 

properties = System.getProperties(); 

for(final Entry<Object, Object> entry : properties.entrySet()) 
{ 
    System.out.println(entry.getKey() + " " + entry.getValue()); 
} 

Überprüfen Sie die Ausgabe, um sicherzustellen, dass Sie die smae JDK/JRE verwenden. Überprüfen Sie außerdem, ob der Klassenpfad korrekt ist, damit Sie tatsächlich laden, was Sie vermutlich laden.