# Java kaputt :]



## Gast2 (16. Jan 2010)

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


----------



## Gast2 (16. Jan 2010)

Nachtrag,

selbst folgendes funktioniert nicht ;(


```
public class Helper {

	public final static VPNServer instance = new VPNServer();

}
```

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


----------



## JohannisderKaeufer (16. Jan 2010)

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

```
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.


----------



## ThreadPool (16. Jan 2010)

@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?   


```
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:


```
public class VPNServerFactory{
    private static class VPNServerHolder{
        public static VPNServer server = new VPNServer();
    }
    public static VPNServer getVPNServer(){
        return VPNServerHolder.server;
    }
}
```


----------



## Gast2 (18. Jan 2010)

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


----------



## tuxedo (18. Jan 2010)

@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


----------



## Gast2 (18. Jan 2010)

Ick werde bekloppt,


```
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


----------



## FArt (18. Jan 2010)

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.


----------



## maki (18. Jan 2010)

Ist es schon wieder Zeit für eine "Singletons sind böse!" Diskussion?


----------



## Gast2 (18. Jan 2010)

FArt hat gesagt.:


> 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


```
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


----------



## FArt (18. Jan 2010)

mogel hat gesagt.:


> ... 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.


----------



## Gast2 (19. Jan 2010)

FArt hat gesagt.:


> 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


```
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()]));
	}
```


```
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


----------



## FArt (19. Jan 2010)

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


----------



## Gast2 (19. Jan 2010)

FArt hat gesagt.:


> 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



> java plugin classloader - Google-Suche


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


----------



## Gast2 (31. Mai 2010)

*grml*

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


----------



## FArt (1. Jun 2010)

mogel hat gesagt.:


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



k. K.


----------

