Java kaputt :]

  • Themenstarter Themenstarter Gast2
  • Beginndatum Beginndatum
G

Gast2

Gast
2 Tage !! :autsch:

seit 2 Tagen suche ich einen Fehler - der machte sich damit bemerkbar das ich irgendwie 2 Benutzerlisten in einem VPNServer habe ... zuerst zweifelt man ja an sich selber - vor allem wenn es unter Eclipse läuft :toll: ... nur unter Java nicht - egal ob Linux oder Windows ... anschließend fiel mir ein das Eclipse & Java Remote-Debugging beherrschen ... das Ergebnis habe ich mal aufgenommen

mal ein nettes kleines Video

ich brauche nur eine Instanz des VPNServers ... wie auf dem Video zu erkennen - ist der Konstruktor private ... dennoch springt er beim zweiten Aufruf erneut in den Konstruktor ... vor allem bitte im zweiten Teil auf die Meldung achten die fürs Logfile generiert wird und was lt. Eclipse da noch immer für ein Object ist ???:L ... im übrigen wird da das neue Object zurück geliefert

hand, mogel
 
Zuletzt bearbeitet von einem Moderator:
Nachtrag,

selbst folgendes funktioniert nicht ;(

Java:
public class Helper {

	public final static VPNServer instance = new VPNServer();

}

das Blöde ist - ist kann das Problem nicht reproduzieren in einem TestProjekt

😡
 
Ist ein bisschen, ein Video der verschwommenen Art. Zumindest bei mir.

Wenn ich das richtig deute soll VPNServer ein Singelton sein.
Bei Singeltons gibt es noch die möglichkeit synchronized zu nutzen.

also
Java:
private static VPNServer getInstance(){
if(instance == null){
synchronize{
if(instance == null)
instance = new VPNServer();
}
}
return instance;
}

Vielleicht hilft das ja.

Erstellt ein Thread gerade eine Instanz ist instance immer noch null, so daß sich auch ein weiterer Thread dranmachenkann eine Instanz zu erstellen, Erst wenn die Erstellung einer Instanz abgeschlossen ist, ist die Instance nicht mehr null.
 
@Johannis

Dein synchronized ist auf dem falschen Lock, IMHO sollte dort synchronized(VPNServer.class) stehen. Besser wäre es die ganze Methode zu synchronisieren.

"Double-checked locking" ist IMHO heutzutage in Java nicht mehr notwendig, es war schon damals keine gute Alternative und besser is es auch nicht geworden. Mogels zweite Idee ist da schon besser.

Was mich wundert ist dass das Zweite aus Mogels Post nicht funktioniert. Liegt vielleicht am public?

Java:
public class Helper {
     private final static VPNServer instance = new VPNServer();
     public static VPNServer  getVPNServer(){return instance;}
}

Eine andere Alternative (lt. Effective Java, basierend auf Annahmen über das "lazy"-class Loading und dem Verhalten statischer Initialisierer) wäre Folgende:

Java:
public class VPNServerFactory{
    private static class VPNServerHolder{
        public static VPNServer server = new VPNServer();
    }
    public static VPNServer getVPNServer(){
        return VPNServerHolder.server;
    }
}
 
Moin,

ich kann das Problem beliebig reproduzieren - allerdings nur innerhalb dieses Projektes ... ich habe mal in den Anhang mein Beispiel gepackt wie ungefähr das Programm funktioniert ... nur leider ohne diesen Bug

ich werde mal alles direkt von Java kompilieren lassen

hand, mogel
 
@ThreadPool und Johannis

Beides "oft so gemacht, aber dennoch nicht sauber". Bei Singletons synchronisiert man sich zu tode. Egal wie man's dreht und wendet, es ist nicht sauber thread-safe etc.
Am besten das ganze dem Classloader überlassen (so wie es Mogel schon gemacht hat mit dem private final static, zusammen mit einem privaten Konstruktur), oder eine Enum-Klasse zu Hilfe nehmen (Design Patterns in Java - Singleton).

- Alex
 
Zuletzt bearbeitet von einem Moderator:
Ick werde bekloppt,

Code:
java.lang.ClassCastException: de.VPNServer cannot be cast to de.VPNServer

ich habe jetzt im Hauptprogramm entsprechend einen Container vorgesehen um den VPNServer da rein zu packen ... da das Hauptprogramm keine Ahnung hat was das Plugin macht geht natürlich nur Object ... beim Rausholen kann der Java das Ding nicht casten :cry: ... und nein - VPNServer erbt definitiv nur von Object

Nachtrag: ich habe jetzt noch etwas weiter rumgespielt ... er packt wirklich nur ein Object in den Container ... ich erhalte jetzt jedes mal eine ClassCastException - dadurch wird immer wieder ein neuer VPNServer erzeugt der in den Container gesteckt wird ... wenn ich auf den VPNServer zugreife vom letzten Object - dann klappt das ... nur nicht wenn ich zwischen durch über ein anderes Object zugreifen ... das ist als wenn im Plugin jedes einzelne Object einen eigenen Heap hat
 
Zuletzt bearbeitet von einem Moderator:
Zwei Klasseninstanzen sind definitiv nicht gleich, wenn die Klassen dazu von zwei Classloadern geladen wurden (auch wenn das von der selben Ressource, z.B. JAR geschieht). Somit kann eine Instanz von Klasse A nicht auf Klasse B (also eigentlich die gleiche Klasse aber von einem anderen Classloader geladen) gecastet werden.
Das kommt vermutlich durch unsauberes Deploymentment in deinem Container bzw. (bei einem Plugin-Mechanismus) durch einen unsauberen Umgang mit Classloadern.
 
Zwei Klasseninstanzen sind definitiv nicht gleich, wenn die Klassen dazu von zwei Classloadern geladen wurden (auch wenn das von der selben Ressource, z.B. JAR geschieht). Somit kann eine Instanz von Klasse A nicht auf Klasse B (also eigentlich die gleiche Klasse aber von einem anderen Classloader geladen) gecastet werden.
ah ja ... die Vermutung hatte ich auch - nachdem ich alle Dateien zusammen gepackt habe und das Programm funktionierte

Das kommt vermutlich durch unsauberes Deploymentment in deinem Container bzw. (bei einem Plugin-Mechanismus) durch einen unsauberen Umgang mit Classloadern.
dann hätte ich gerne an der Stelle etwas Hilfe

Java:
	private static boolean LoadBefehlURL(String befehl)
	{
		logger.info(" - Plugin: '" + befehl + "'");

		File dir = new File("plugins");
		if (!dir.exists())
		{
			logger.warn("kein Plugin-Verzeichnis angelegt");
			return false;
		}
		
		for(File file : dir.listFiles())
		{
			try
			{
				final URL extLibUrl = new URL("file", "localhost", file.getAbsolutePath());
				URLClassLoader loader = new URLClassLoader(new URL[]{extLibUrl});			
				final Class<?> c = loader.loadClass(befehl);
				befehle.add((ACommand) c.newInstance());
				return true;
			} catch(ClassNotFoundException ex)
			{
				// nüschts - try again ... nicht im aktuellen JAR vorhanden
				logger.error("", ex);
			} catch(Exception ex)
			{
				logger.error("", ex);
			}
		}
		
		return false;
	}

entsprechend dem geposteten Beispiel habe ich nur die einzelnen Pakete auf die verschiedenen JARs verteilt ... base ist das Hauptprogramm, welche das JAR aus library referenziert ... in der Realität sind da noch verschiedene Exceptions definiert ... dann fehlt nur noch plugin welches ebenfalls library referenziert ... viel falsch kann man an der Stelle nicht machen

was alles aus dem Plugin instanziert werden muss wird einmal am Start instanziert ... ähnlich wie im Beispiel

hand, mogel
 
... viel falsch kann man an der Stelle nicht machen

Das ist eine grobe Fehleinschätzung, bei einem Plugin-Mechanismus, einer "Container-Architektur" kann man (alleine bzgl. Classloading) eine Menge falsch machen... bei der Implementierung und bei der Verwendung (z.B. über setzen des Klassenpfades).

Konkret zu hier:
der URLClassLoader ist hier problematisch, da er (wie alle Standard-Classloader) zuerst an den Parent-Classloader delegiert, bevor er selber nach einer Ressource sucht. Gerade bei Plugins wäre es umgekehrt besser (erst selber suchen, dann delegieren).

Das "Mischen" von Ressourcen über mehrere Classloader ist auch problematisch, da kann man sich schnell Memoryleaks bauen kann (wenn z.B. eine Ressource des Parents noch auch eine Ressource des (bereits beendeten) Plugins referenziert, wird u.U. der gesamte Classloader nicht collected... mit allem was dazu gehört.
 
der URLClassLoader ist hier problematisch, da er (wie alle Standard-Classloader) zuerst an den Parent-Classloader delegiert, bevor er selber nach einer Ressource sucht. Gerade bei Plugins wäre es umgekehrt besser (erst selber suchen, dann delegieren).
kommt ja zurück - weil die Default-CL nix finden ... aber das Problem liegt daran das der URLClassLoder mehrfach instanziert wird ... ich habe mich jetzt darauf beschränkt das der URLClassLoader nur einmal Instaziert wird und dabei gleich die URL von allen Plugins im Verzeichnis mitbekommt

Java:
	private static URLClassLoader pluginloader = null;
	
	private static void InitPluginLoader()
	{
		if (pluginloader != null) return;
		File dir = new File("plugins");
		if (!dir.exists())
		{
			logger.warn("kein Plugin-Verzeichnis angelegt"); 
			return; 
		}
		
		List<URL> urls = new ArrayList<URL>();
		
		for(File file : dir.listFiles())
		{
			try
			{
				URL extLibUrl = new URL("file", "localhost", file.getAbsolutePath());
				urls.add(extLibUrl);
			} catch (MalformedURLException e)
			{
				logger.error("", e);
			}
		}
		
		pluginloader = new URLClassLoader(urls.toArray(new URL[urls.size()]));
	}

Java:
	private static boolean LoadBefehlURL(String befehl)
	{
		logger.info(" - Plugin: '" + befehl + "'");
	
		try
		{
			final Class<?> c = pluginloader.loadClass(befehl);
			befehle.add((ACommand) c.newInstance());
			return true;
		} catch(Exception ex)
		{
			logger.error("", ex);
		}
		
		return false;
	}

Nachtrag: sonst habe ich immer nur ein(e) Plugin(-Klasse) in einem JAR - da funktioniert das auch so ... diesmal sind aber pro JAR mehrere enthalten 😳

danke, mogel
 
Zuletzt bearbeitet von einem Moderator:
Das ist auch nicht wasserdicht... oder nicht sinnvoll...

Kommen zur Laufzeit Plugins dazu oder werden entfernt? Dann kommst du mit einem CL zu Problemen (nämlich wenn Ressourcen plötzlich fehlen oder ausgetauscht werden, z.B. gegen eine neuere Version).
Wenn das nicht der Fall ist, ist der ganze Heckmeck umsonst: es reicht die Ressourcen (JARs) beim Programmstart in den regulären Klassenpfad aufzunehmen.

[EDIT]
java plugin classloader - Google-Suche
 
Zuletzt bearbeitet:
Kommen zur Laufzeit Plugins dazu oder werden entfernt? Dann kommst du mit einem CL zu Problemen (nämlich wenn Ressourcen plötzlich fehlen oder ausgetauscht werden, z.B. gegen eine neuere Version).
nein ... wird einmal am Programmstart alles geladen

Wenn das nicht der Fall ist, ist der ganze Heckmeck umsonst: es reicht die Ressourcen (JARs) beim Programmstart in den regulären Klassenpfad aufzunehmen.
da ich keine Ahnung habe welche Plugins irgend wann mal da mit reinkommen und ich gelernt habe das auch V011N00bs meine Programme nutzen fällt die Angabe des Classpath zum Programmstart definitiv aus

das habe ich mir auch überlegt ... dabei viel mir aber auf das ich den eigenen ClassLoader auch nur einmal verwenden darf ... dann kann ich gleich den URLClassLoader verwenden - da ich keine besonderen Anforderungen habe

falls doch irgendwann mal noch irgendwelche Plugins später geladen werden sollen/müssen/wollen ... kann auch der URLClassLoader erweitert - addURL()

hand, mogel
 
*grml*

ach das mit den Classloadern ist doch Mist ... jetzt habe ich zwei Plugins die den gleichen Klassennamen importieren :toll:
 

Zurück
Oben