Problem mit Threads

mysticado

Aktives Mitglied
Hallo Leute,
ich habe folgendes Problem - schaut euch bitte mal den angehängten Screenshot an! Ich möchte, dass wenn ich auf den button "simulate" drücke, verschiedene Befehle durchlaufen werden und der derzeitige Status des Durchlaufs per output.append("text"); in der Output-Textarea angezeigt wird.

Nun habe ich aber in der Mitte meines Durchlaufs folgenden Code:

Java:
// Starting the VM-Utilize
                Thread t = new Thread();
                     output.append("\n >> Utilizing");
                     t.sleep(1000);
                     t.interrupt();
                     output.append(".");
                     t.sleep(1000);
                     t.interrupt();
                     output.append(".");
                     t.sleep(1000);
                     t.interrupt();
                     output.append(".");
                     utilizeVM();

Die Idee ist also die, im Outputfeld den Text "Utilize" anzuzeigen und nach jeder Sekunde jeweils einen Punkt dranhängen, so dass ich dadurch einen Fortschritt vortäuschen kann und am Ende nach 3 Sekunden den Text "Utilizing..." stehen habe.
Mein Problem ist aber, dass nachdem ich auf den Button Simulate gedrückt habe, erstmal meine ganze GUI für 3 Sekunden einfriert und dann auf einmal den kompletten Text "Utilizing..." anzeigt, ohne eben den oben genannten gewollten Effekt.
Ausserdem habe ich bemerkt, dass mein Simulate-Button in den 3 Sekunden auch einfriert, d.h. gedrückt bleibt.


Was kann ich also machen, um diese Animation der Punkte korrekt darzustellen und den Simulate-Button zu "entfrieren"? Wo mache ich den Fehler??

Danke schon mal für die Antworten :)
 

Anhänge

  • GUI.PNG
    GUI.PNG
    69 KB · Aufrufe: 52

mysticado

Aktives Mitglied
Wow geile Sache!
Ich habs nun mit dem SwingWorker probiert und habe zumindest schon mal nicht mehr das Problem das der Button einfriert.
Leider funktioniert es aber trotzdem noch nicht so wie ich es will. Es bleibt leider nach dem ersten Punkt stehen (oder bleibt in einer Endlosschleife, oder wasweissich), d.h. ich erhalte als Ausgabe nur "Utilizing."

Hier mein neuer Code:

Java:
 // Starting the VM-Utilize
                output.append("\n >> Utilizing");
                SwingWorker worker = new SwingWorker() {
          protected String doInBackground() throws InterruptedException {
            Thread.sleep(1000);
            return null;
          }
          protected void done() {
            output.append(".");
          }
        };

        worker.execute();
        worker.execute();
        worker.execute();

Ausserdem meldet mir mein Debbuger das drei Threads erzeugt werden - also anscheinend einer für jedes execute(). Sie hätten aber nach einander laufen sollen, und nicht gleichzeitig - wo ist denn nun mein Fehler???
 

hdi

Top Contributor
Aus der API-Doc:
public final void execute()

Schedules this SwingWorker for execution on a worker thread. There are a number of worker threads available. In the event all worker threads are busy handling other SwingWorkers this SwingWorker is placed in a waiting queue.

Note: SwingWorker is only designed to be executed once. Executing a SwingWorker more than once will not result in invoking the doInBackground method twice.


Der gesamte Prozess muss in den Worker, und der wird dann nur einmal gestartet. In deinem Fall reicht es wohl nicht, nur doInBackground() und done() zu implementieren. Du brauchst wohl noch process() und publish().

Lies dir die Dokumentation durch, da ist ein Beispiel:
SwingWorker (Java Platform SE 7 )

Ich hoffe, du hast die API-Doc gebookmarked. Ist heiliger als die Bibel, gerade am Anfang!
 

hdi

Top Contributor
...und als Nachtrag:

Bevor du demnächst in das nächste Problem mit Threading läufst (in meiner Kristallkugel sehe ich eine ConcurrentModificationException :D), lies dir am besten auch mal die ersten paar Artikel zu "Concurrent Programming" auf dieser Seite durch. Du weißt ja jetzt nach dem Lesen des o.g. Artikels, dass du bei Programmen mit einer GUI immer Multi-Threading hast, oder?
 

mysticado

Aktives Mitglied
Das ist echt nett von dir, dass du mir so gründlich hilfst. Ich hatte es natürlich auch zuerst selber versucht, doch google wollte mir einfach nicht das anzeigen, was ich eigentlich gebräucht hätte :)

Ich muss aber leider zugeben, die ganze SwingWorker Geschichte übersteigt so langsam schon mein (recht überschaubares) Wissen. Das Beispiel zu publish() versteh ich ja nicht mal, haha!

Könntest du also evtl. eine Ausnahme machen, und mit konkret sagen wie ich denn process() und publish() für meinen Fall einbauen könnte? Okay, publish() muss ins doInBackground()....doch wie genau?! Irgendwie versteh ich nicht wirklich wo mein Thread.sleep() Befehl hin muss? Puh...

Danke! :)


Edit: Ja, ich habe nun gelernt, dass es bei einer GUI immer um's Multithreading geht und ob du's glaubst oder nicht, ich habe durch deine Hilfe bisher tatsächlich was dazugelernt :) Danke !!!
 
Zuletzt bearbeitet:

hdi

Top Contributor
Über publish() schiebst du "Chunks", d.h. Zwischenergebnisse deiner Berechnung auf den EDT. Über publish() wird process() aufgerufen, und dieser Methode werden alle bisher noch nicht verarbeiteten Chunks mitgegeben. In deinem Fall sind die Zwischenergebnisse die Strings, die du appendest. D.h. in deiner doInBackground hast du eine Schleife, mit:

publish(text);
sleep(1000);

Und in der process fügst du diese Chunks in deine TextArea ein per Append.

Das ganze ist generisch. Davon schon mal was gehört? Das ist dieses <V,K> in der Doc, das du in deinem Code so schön ignoriert hast ;) Damit kannst du den Datentyp deiner Chunks festlegen. In deinem Falle String. Ob du das für V oder K einsetzen musst, kannst du nachlesen in der Doc. Ist in diesem Beispiel zwar nicht unbedingt nötig, da du auf deinen Chunks auch die toString()-Methode aufrufen kannst.. Aber du solltest es dir gleich angewöhnen, das generisch zu machen.

Versuch's noch mal. Und zeig mir dein Ergebnis. Dann bekommst du von mir die Lösung, sofern du sie nicht gefunden hast. Aber beeil dich, in 10 Minuten läuft Quatsch Comedy Club xD
 

hdi

Top Contributor
So ich poste das jetzt mal, weil QCC fängt an. Wenn du die API-Doc aufmerksam gelesen hättest, wäre dir aufgefallen dass das Beispiel dort eigentlich 1:1 das ist, was du brauchst...

Java:
SwingWorker<Void, String> job = new SwingWorker<Void, String>(){

     @Override
     public Void doInBackground() {
         for(int i = 0; i<10; i++){
             publish(".");
             try{
                 Thread.sleep(1000);
             } catch(InterruptedException e){}
         }
         return null;
     }

     @Override
     protected void process(List<String> chunks) {
         for (String s : chunks){
             output.append(s);
         }
     }

     @Override
     public void done(){
         utilizeVM(); 
     }
 }

job.execute();

Wenn du dazu noch Fragen hast, nur zu. Ich werd sie aber vllt erst morgen beantworten (wenn mir kein anderer zuvor kommt)

Gute Nacht!
 

mysticado

Aktives Mitglied
Mist, ich bin doch schon soooo nah! Es geht aber net anders, ich brauch deine Hilfe :-/

Java:
                   // Starting the VM-Utilize
                output.append("\n >> Utilizing");
                SwingWorker worker = new SwingWorker() {
          protected String doInBackground() throws InterruptedException {
              for(int i=0;i<3;i++){
                  Thread.sleep(1000);
                  publish(".");
              }
            return null;
          }

          protected void process(String a) {
          output.append(a);
                    }
          protected void done() {
            
          }
          
        };

Das mit dem Generalisieren habe ich verstanden und normalerweise mache ich das auch, doch es geht mir hierbei nur um 3 Punkte, und eigtl. wollte ich das ohne größere Komplikationen bewerkstelligen, doch am Ende hat es mich die meisten Nerven gekostet, haha.
Wie auch immer, ich hoffe ich kriege nun die Lösung von dir und dann wünsche ich dir auch viel Spaß beim QQC ! Ich denke, ich geh ihn mir nun auch reinziehen, hahaha....
Schönen Abend noch! Ciao!
 

mysticado

Aktives Mitglied
Hat geklappt! Danke für die Hilfe!
Fragen habe ich vorerst nicht...ich werd mir nochmal die javadoc angucken und dazu noch diesen Beispielcode und dann erstmal versuchen selber draus schlau zu werden. Falls nicht, weiss ich ja wo ich hilfsbereite Menschen finden kann :)

Danke für die tolle Unterstützung so spät am Abend und gute Nacht!
 

mysticado

Aktives Mitglied
Okay, da wäre ich wieder :)
Keine Sorge, das Problem von oben haben wir ja gestern erfolgreich gelöst, doch nun habe ich ein weiteres Problem und da es sich (meiner Meinung nach) wieder um ein Thread-Problem handelt, dachte ich hänge ich es hier hinten dran.
Um mein Problem aber zu erklären werde ich ein bisschen weiter ausholen müssen...

Ich muss für ein Praktikum ein Programm schreiben, welches sich alle Rechner im internen Netz anguckt und protokolliert wie viele und welche virtuellen Maschinen auf ihnen laufen. Die Funktionsweise ist in diesem Moment nicht wichtig - es ist nur wichtig zu wissen, dass es klappt an diese Infos ranzukommen ;) Diese einzelnen virtuellen Maschinen sollen von mir nun unterschiedlich ausgelastet werden, z.B.
- VM1= 99% cpu , 100% RAM
- VM2= 10% cpu , 50% RAM
- usw.
Wenn man sich den neu angehängten Screenshot anschaut, sollte es vielleicht etwas klarer sein - links ist der Baum aller Rechner und den darauf laufenden VMs.

Das Problem ist nun die rechte Seite meiner GUI...

Ich soll es so programmieren, dass ich gleichzeitig mehrere VMs auslasten kann, sprich ich soll es möglich machen links im Baum eine VM auszuwählen, rechts individuelle Werte eingeben und die Simulation starten. Wenn ich nun eine andere VM auswähle, soll ich wieder die gleichen Möglichkeiten haben.
Leider bin ich halt absoluter Noob was Swing und GUI betrifft und weiss in diesem Moment nicht mal von wo aus ich starten soll. Ich habe z.Z. noch die Funktionalitäten (die rechte Seite der GUI) und den Baum komplett in einer Klasse - doch das ist wahrscheinlich falsch, oder? Die rechte Seite wird wahrscheinlich eine eigenständige Klasse sein müssen (bzw. ein JFrame?) damit sie jedes Mal neu instanziiert werden kann...
Und wie instanziiere ich sie dann? Jeweils ein Thread? Auch mit SwingWorker???

Puh, viel Text, viele Fragen...viel Spaß damit :)
 

Anhänge

  • gui2.PNG
    gui2.PNG
    92,5 KB · Aufrufe: 34

hdi

Top Contributor
Hello again,

Was mir jetzt noch nicht ganz klar ist: Wie funktioniert das Auslasten der VM's? Ist das eine pseudo-mäßige Simulation, in der du einfach irgendwelche Auslastungen in deiner GUI anzeigen lassen sollst (ohne dass da was dahintersteckt), oder geht es wirklich darum, den VM's über's Netzwerk irgendwelche Datenpakete zu schicken, sodass es am betroffenen Rechner tatsächlich zu einer echten Auslastung kommt?

Ich frage deshalb, da Multi-Threading in dem Fall, dass das alles nur gefaked ist, gar nicht nötig ist. Es sei denn, du hast die explizite Vorgabe, auch in diesem Fall trotzdem Multi-Threading zu betreiben.

Falls da wirklich etwas sinnvolles passiert, dann wäre es gut zu wissen was da genau passiert, und wie es passiert. D.h. was machen die Threads? Inwiefern gibt es eine Verbindung zwischen den internen Daten und der GUI?
 

mysticado

Aktives Mitglied
Also, um mich kurz zu fassen - das alles ist kein Fake sondern erzeugt tatsächlich Last. Um da jetzt jedochnicht zu tief reinzugehen lass mich nur soviel sagen - die GUI versendet die Befehle eigentlich nur, sie selbst muss aber nichts loggen, auf irgendwelche Antworten warten oder was auch immer.
Die Befehle werden per ssh gesendet und dann übernimmt die VM selbst die ganze Auslastungsgeschichte. Die Textfields dienen in meinem Programm daher nur, dass ich weiss welche Werte ich der VM rüberschicken muss.

Ich denke aber trotzdem das multi-threading nötig sein wird (falls ich das umgehen kann, dann würde ich das sehr gerne machen - die vorgabe wurde mir nicht gestellt ;) ), da es noch diese lästige Timer-Funktion gibt - Duration of Simulation - welche nach der vorgegebenen Zeitspanne den VM's die kill-Befehle zuschicken soll.
Ich weiss deswegen nicht wirklich wie ich denn einfach zwischen den ganzen VM's im Baum hin und her switchen kann und dabei all die eingegebenen Werte UND die verbliebene Zeit des Timers jeder VM behalten kann?

Puh! Schwere Sache! :)
 

hdi

Top Contributor
Okay.. Also es würde durchaus ohne Multi-Threading gehen. Aber es ist in diesem Fall wirklich besser (heißt auch: leichter) das über mehrere Threads zu machen.

Wegen der GUI: Du brauchst das alles, d.h. die Textfelder usw., nur ein mal. Immerhin werden ja immer nur die Daten von einer VM angezeigt, aber niemals Daten von mehreren VM's gleichzeitig, oder?

Die eigentlichen Daten, d.h. Auslastung, Timer usw sind in einer internen Struktur gespeichert, zB Klasse "VMData". Und nicht direkt in deinen Textfeldern. Deinem Tree verpasst du ein entsprechendes Model mit diesen internen VMData-Daten. Wenn man jetzt auf eine Node klickt, dann befüllst du deine rechte Seite der GUI mit den Daten aus der VMData-Instanz. Und wenn man auf Simulation starten klickt, startest du einen neuen Thread, dem diese VMData-Instanz übergeben wird. Damit hat er alle Infos, die er braucht. Und ja, wenn man jetzt während der Simulation Werte ändern können soll, geht's los mit Synchronisierung.

Aber - fangen wir am besten mal bei dieser VMData an. Hast du denn im Moment irgendeine Klasse, die alle Infos über eine VM und ihre Simulationsdaten speichert?
 

mysticado

Aktives Mitglied
Also, ich merke, du bist so nett und möchtest mir tatsächlich helfen und deswegen hier nun auch mein Quellcode - es ist denke ich auch viel einfacher mit einander zu kommunizieren wenn du auch wirklich verstehst, was ich meine, bzw. du es vor dir liegen hast :)
Doch keine Sorge ich erwarte jetzt nicht, dass du komplett alles für mich machst. Ich bin dir schon dafür dankbar, dass ich mit dir ein bisschen Brainstormen kann :)

Zurück zu meinem Problem - das was mich noch ein bisschen stutzig macht ist die Tatsache, dass nachdem ich auf "Simulate" klicke, der Simulate-Button selbst erstmal ausgegraut wird, in Hintergrund wird dann auch der Timer gestartet, der nach "Duration of Simulation"-Zeiteinheiten die kill-Befehle an die VM schickt (also die Funktion des Stop-Buttons übernimmt, nur eben nach einer vorgegebenen Zeit).
Wenn ich da nun für jede VM einen eigenen Thread erstelle, wie mache ich es denn dann mit dem Ausgrauen des Simulate-Buttons? Geht das genauso mit einer internen Struktur?

Apropos: Eine interne Struktur habe ich bisher noch nicht. Ich habe die übergebenen Werte immer nur in die dementsprechenden Globalen Variablen gespeichert. Mein ganzes Programm ist ja, wie du sehen kannst, in einer einzigen Klasse untergebracht :)

Also, erster Schritt...eine neue Klasse für die Werte? Eine Methode reicht dafür nicht?
 

Anhänge

  • WebserverUtilizer.java
    43,6 KB · Aufrufe: 4

hdi

Top Contributor
Wenn du schon sagst, dass alles in einer einzigen Klasse steckt.. Ne du, das tu ich mir nicht an, sorry. Würd eh darauf hinauslaufen dass ich sage "Wegschmeißen, neu machen".

Vergiss erstmal die GUI.. Du brauchst eine Datenstruktur zur Verwaltung deiner Daten. Wenn ich mir deinen Screenshot ansehe würde ich sagen:

Java:
class VirtualMachine{

   private String ip;
   private long stressDuration;
   private int stressCpuLast;
   private int stressRamLast;
}

und

Java:
class NetworkEntity{

   private String ip;
   private List<VirtualMachine> virtualMachines;
}

Solche VirutalMachines bzw NetworkEntities baust du dir zusammen im Zuge der Durchsuchung des Netzwerks. Und die ganzen NetworkEntity-Instanzen (die ja die VM's enthalten) sind dann dein interner Datenbestand. Dafür machste dir am besten nochmal ne eigene Klasse. Die kann der Einfachheit halber meinetwegen die Daten auch statisch anbieten:

Java:
class Database{

   public static List<NetworkEntity> networkEntities = new ArrayList<NetworkEntitiy>();
}

Diese Database zu befüllen ist das erste, was du tust. in deiner main-Methode, da ist mit GUI oder Threads etc. noch gar nix!

So, wenn du das hast, dann sag Bescheid.

(Das ganze wird noch etwas länger dauern.. Aber dafür ist es am Ende sauber, du verstehst es, und kriegst ne 1)
 

mysticado

Aktives Mitglied
Ohje, ich trau mich das hier gar nicht zu schreiben, sonst verscheuche ich dich womöglich noch...
Ich verstehe deinen Ansatz, habe die Klassen auch so erstellt wie du es mir gesagt hast - die Idee ist also jeweils eine Database-Instanz zu erzeugen, bzw. die Variable networkEntities zu befüllen, nachdem die Werte ausgelesen wurden.
In der main-Methode habe ich die Auslesefunktion schon drin, sprich ich hab Zugriff auf die Daten, doch.......ich komme einfach nicht drauf, wie ich die Daten nun intern speichern soll :(
Geht das einfach so mit:
Code:
Database db = new Database();
db.networkEntities("192.168.0.1", ...);

Ich muss ehrlich zugeben, ich habe noch nie mit solch verschachtelten Klassen rumhantiert und bin deswegen etwas durcheinander :( Bitte verlier nicht die Geduld mit mir - es wäre toll, wenn du mir das in 3 Sätzen erklären könntest :)
Ich bin bereit zu lernen und willl unbedingt diese 1 ;)

P.S. Das ist falsch so, oder?

VMDatabase.networkEntities.add(new NetworkEntity("192.168.0.1",new List<VirtualMachine>()))
 
Zuletzt bearbeitet:

hdi

Top Contributor
Java:
VMDatabase.networkEntities.add(new NetworkEntity("192.168.0.1",new List<VirtualMachine>()))

Fast ;)
Code:
List
ist ein Interface, du kannst davon keine Instanz erzeugen. Du musst eine Klasse wählen, die das Interface implementiert. z.B. ArrayList:

Java:
VMDatabase.networkEntities.add(new NetworkEntity("192.168.0.1",new ArrayList<VirtualMachine>()))

Das wäre schon mal korrekt, sofern du natürlich einen entsprechenden Konstruktor definiert hast. Wenn du eine NetworkEntitiy so erzeugst, brauchst du halt eine Methode á la addVirtualMachine. Alternativ liest du erst alle VM's aus, erstellst eine Liste davon, und erzeugst dann die NetworkEntitiy, und übergibst diese Liste (an statt eine leere Liste).
 

mysticado

Aktives Mitglied
Also, als erstes - NEIN, es klappt noch net :D
Ich muss schon sagen, Du hast mich ganz schön durcheinander gebracht mit all den Entitys und VirtualMachines :) Aber ich seh schon ein Licht am Horizont...am Ende des Tages werde ich um einiges schlauer sein (was Java anbelangt zumindest :) ).
Also, ich bin so weit gekommen:

Java:
public class VMDatabase {
    public static List<NetworkEntity> networkEntities = new ArrayList<NetworkEntity>();


}

public class NetworkEntity {
	   private String ip;
	   private List<VirtualMachine> virtualMachines;
	   
	   public void addVM(VirtualMachine vm){
		   virtualMachines.add(vm);
	   }

	}

public class VirtualMachine {
	private String ip;
	private long stressDuration;
	private int stressCpuLast;
	private int stressRamLast;
	private boolean webTraffic;
	private int activeUsers;
	private String templatePath;

	public VirtualMachine(String ip, long stressDuration, int stressCpuLast,
			int stressRamLast, boolean webTraffic, int activeUsers,
			String templatePath) {
		this.ip = ip;
		this.stressDuration = stressDuration;
		this.stressCpuLast = stressCpuLast;
		this.stressRamLast = stressRamLast;
		this.webTraffic = webTraffic;
		this.activeUsers = activeUsers;
		this.templatePath = templatePath;
	}
}

Meine Klasse mit main-Methode versucht nun folgendes auszuführen:

Java:
VMDatabase.networkEntities.add(new NetworkEntity("192.168.0.1",new ArrayList<VirtualMachine>().addVM(new VirtualMachine("192.168.0.5", (long) 1000, 10, 128, true, 5))));
was natürlich wieder falsch ist. Mensch, da hab ich mir aber was eingehandelt :D
 

hdi

Top Contributor
In deiner Network-Entitiy Klasse hast du die Liste noch nicht instantiiert. Das gibt eine NullPointerException wenn du da was per add() einfügen willst. Du musst der Variablen für die Liste natürlich auch eine Liste zuweisen. Das geht so wie auch bei der Database. Außerdem fehlt noch ein Konstruktor, um zB die ip festzulegen. Am besten so:

Java:
public class NetworkEntity {
       private String ip;
       private List<VirtualMachine> virtualMachines = new ArrayList<VirtualMachine>();

       public NetworkEntity(String ip){
           this.ip = ip;
       }
       
       public void addVM(VirtualMachine vm){
           virtualMachines.add(vm);
       }
}

Hier die Reihenfolge wie das ablaufen muss:

1) Du suchst die IP eines Rechner im Netzwerk, und erstellt eine NetworkEntity
2) Du suchst alle VM's auf diesem Rechner mit deren IP usw, und erstellst jeweils eine VirtualMachine, die du mit addVM() der obigen NetworkEntitiy hinzufügst
3) Wenn alle VM's zu der NetworkEntitiy geaddet wurden, fügst du die NetworkEntity in die Datbase ein. Das sieht dann nur so aus:

Java:
VMDatabase.networkEntities.add(entity);

Übrigens musst du ein Zahlen-Literal im Code nicht explizit nach long casten. Das Casting von int nach long kann implizit vorgenommen werden. Wenn du betonen willst, dass es sich um ein long handelt, hänge ein kleines oder großes "L" hinter die Zahl:
Code:
1000L

PS: Gewöhn dir an, Fehlermeldungen zu lesen! Egal ob Compile- oder Laufzeitfehler, die sind meist recht klar ausgedrückt.
 

mysticado

Aktives Mitglied
Aaah, nun ist es mir viel klarer. Ich dachte ich muss komplett die Methoden so übernehmen wie du sie oben beschrieben hattest. Natürlich weiss ich, dass man Konstruktoren usw. braucht, doch da du hier definitiv der bessere Java-Checker bist wollte ich einfach nichts sagen und nahm alles so hin wie es da stand :) Sorry, für meine Beschränktheit!

Alles klar, es hat geklappt - alle Rechner konnten so in die Database eingespeichert werden. Mein nächster Schritt ist dann das Erzeugen des JTrees, oder?
Eine weitere Frage - die Database ist ja nun in der Memory gespeichert - bleibt sie denn so drin so lange die GUI läuft? Sprich, werde ich dann mit den jeweiligen Threads immer die selben Werte aus der DB rauslesen können?
 

hdi

Top Contributor
Alles, was irgendwie referenziert ist, bleibt auch für die gesamte Laufzeit des Programms im Speicher, ja.

Mein nächster Schritt ist dann das Erzeugen des JTrees, oder?

Genau:

Java:
public class NetworkEntitiyTree extends JTree{

    public NetworkEntitiyTree(){
        setModel(new MyModel());
    }

    private static class MyModel extends DefaultTreeModel{

        // ...
    }  

}

Weißt du, wie man ein TreeModel implementiert? Du musst in der inneren Klasse diverse Methoden überschreiben (aus der Klasse DefaultTreeModel bzw dem Interface TreeModel), und dabei auf deine Database zugreifen, sodass der JTree weiß, wie er aussehen muss.
 

mysticado

Aktives Mitglied
Ist vielleicht ein wenig unschön, aber es klappt:

Java:
        javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("Cluster");
        javax.swing.tree.DefaultMutableTreeNode treeNode2;
        javax.swing.tree.DefaultMutableTreeNode treeNode3;

        for(int i=0;i<VMDatabase.networkEntities.size();i++){
            treeNode2 = new javax.swing.tree.DefaultMutableTreeNode("Node #"+(i+1)+" ["+VMDatabase.networkEntities.get(i).getIP()+"]");
            treeNode1.add(treeNode2);
                for (int j=0;j<VMDatabase.networkEntities.get(i).virtualMachines.size();j++) {
                    if(VMDatabase.networkEntities.get(i).virtualMachines.get(j).getHostIP().equals(VMDatabase.networkEntities.get(i).getIP())){
                              treeNode3 = new javax.swing.tree.DefaultMutableTreeNode("VM ["+VMDatabase.networkEntities.get(i).virtualMachines.get(j).getIP()+"]");
                              treeNode2.add(treeNode3);
                    }
                }
        }
        tree.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));

        expandAll(tree); // Tree wird expandiert!

        tree.addTreeSelectionListener(new TreeSelectionListener() {
                 public void valueChanged(TreeSelectionEvent evt) {
                     String tempIp = evt.getPath().toString().split("VM")[1];
                     String endIp = tempIp.substring(tempIp.indexOf("[")+1, tempIp.length()-2);
                     ipAddr = endIp;
                     ueberschrift.setText("// VM: "+endIp);
                  }
          });

Da ich meine GUI mit Netbeans mache, wurde mir solch eine Vorgabe für das JTree erstellt und daher habe ich es diesmal nicht so gemacht wie du es mir im letzten Kommentar geschrieben hattest...aber wie gesagt, es läuft ja und das ist erstmal das Wichtigste, hihi.
Was sagst du zum SelectionListener? Ist es so wie du es machen würdest, oder es dir vorgestellt hast? Mein nächster Schritt ist ja nun, dass ich per Selection jeweils einen neuen Thread starte, oder?

p.s. mehr als die 3 treenodes werde ich ja nicht brauchen, da ich ja nur rechner und deren vms anzeigen lassen muss.
 
Zuletzt bearbeitet:

hdi

Top Contributor
Nein, das ist Quatsch!

Dein Tree hat jetzt wieder keine Verknüpfung zu den eigentlichen Daten. Deine Nodes sind nur Strings (IP-Adresse), das bringt dir nix. Deine Nodes müssen die tatsächlichen NetworkEntity bzw VirtualMachines kapseln, damit du ordentlich damit arbeiten kannst.

Meinetwegen benutze ein DefaultTreeModel, aber steck in die Nodes nicht nur die IP, sondern die gesamte NetworkEntity bzw die VM's. Und nimm diese Variablen "treeNode1,2,3" usw raus! Sonst funktioniert das nur wenn genau 3 Rechner gefunden wurden. Was machts du, wenn 4 gefunden werden? Lösch die Variablen, du brauchst nur einen Root-Knoten, und in den fügst du dynamisch über eine Schleife alle NetworkEntities aus denier Datenbank ein:

Java:
new DefaultMutableTreeNode(VMDatabase.networkEntities.get(i));

Dann überschreibst du in der NI- und VM-Klasse jeweils die to-String Methode, damit er dir die IP anzeigt:

Java:
@Override
public String toString(){
    return "[" + getClass().getSimpleName() + "]" + " " + ip;
}

Die Anzeige ist dann fast gleich, aber der Unterschied ist dass du über die Nodes tatsächlich an die internen Daten kommst.
 

mysticado

Aktives Mitglied
ja warte mal, ich dachte meine Treenodes1,2,3 seien nur variablen für die Tiefenlevels, sprich treenode1 = root, treenode2 = rechner, treenode3 = vms....so dass ich dann dadurch die hierarchie erhalten würde. ich wusste nicht, dass sich die treenode variablen in meinem fall auf konkret einen rechner beziehen?

ja und wie mach ich das dann mit den VMs die jeweils den networkentitys zugeordnet werden sollen? oben meintest du ja ich solle eine schleife machen und alles an root hängen - dann hätte ich aber diese hierarchie nicht, oder?
 

hdi

Top Contributor
Die Hierarchie musst du halt zusammenbauen:

Java:
DefaultMutableTreeNode root = new DefaultMutableTreeNode();

for(NetworkEntity networkEntitiy : VMDatabase.getNetworkEntitites()){
   DefaultMutableTreeNode entityNode = new DefaultMutableTreeNode (networkEntitiy);
   for(VirtualMachine vm : networkEntitiy.getVirtualMachines()){
      entityNode.add(new DefaultMutalbeTreeNode(vm));
   }
   root.add(entityNode);
}

Zu den Variablen: Das ist falsch, du brauchst wie gesagt nur eine für den root-Knoten. An alles andere kommst du ja über diesen Root, da ja da alles drin steckt. Wenn du für einzelne Knoten im Tree nochmal Variablen abspeicherst ist das redundant, d.h. umständlich und fehleranfällig.

... und in deinem SelectionListener prüfst du nun mit
Code:
instanceof
, ob der ausgewählte Knoten eine NetworkEntitiy ist oder eine VirtualMachine. Wenn ersteres, musst/kannst du ja rechts in der GUI gar nix anzeigen, außer ne Meldung "Bitte eine VM auswählen". Und wenn es eine VM ist, dann kannst du den Node casten, alle Infos da raus holen und deine Textfelder usw mit den Werten befüllen:

Java:
Object data = node.getUserObject();
if(data instanceof NetworkEntitiy){
   // ...
}
else{
   // DAtena nzeigen
}
 
Zuletzt bearbeitet:

mysticado

Aktives Mitglied
Du wirst es nicht glauben, aber die Aha-Effekte dauern bei mir schon seit gestern an! Ich wäre nieeee auf die Idee gekommen einige Sachen so zu machen wie Du es mir hier beschrieben hattest. Das Überschreiben der toString(), oder das instanceOf() lassen mich immer noch über all' die verschiedenen Möglichkeiten in Java staunen :)

Eine Frage hätte ich jedoch noch zum Listener, dann können wir eeeendlich auf die eigentliche Frage (Threads!) übergehen: da mir das node.getUserObject() nicht wirklich klar war (ich bekomme ja einen Klickevent zurück und keinen node) und ich ja theoretisch nur wissen muss welches der Knoten angeklickt wurde, um da draus dann die Infos rauszuholen ob es ein NE, oder eine VM war, könnte ich es doch auch so machen oder:

Java:
        tree.addTreeSelectionListener(new TreeSelectionListener() {
                 public void valueChanged(TreeSelectionEvent evt) {
                     Object data = evt.getSource();
                            if(data instanceof NetworkEntity){
                                 ueberschrift.setText("Bitte VM auswählen");
                            }
                            else{
                                ueberschrift.setText("Korrekt");
                            }
                  }
          });

Leider bekomme ich hierbei aber immer die Antwort "Korrekt" zurück, was natürlich nicht richtig ist. Ist es denn falsch es mit .getSource() zu machen?!
Ich würde es auch gerne anhand deiner Methode lösen, nur musst du mir wieder einen klitzekleinen Stoß geben, damit ich auch verstehe aus welchem Knoten du deinen UserObject rausholen möchtest :)
 

hdi

Top Contributor
API-Doc lesen!

source - The object on which the Event initially occurred.

Das ist nicht der Knoten, sondern der eigentliche JTree. Und in der API-Doc von TreeSelectionEvent sind auch einige Methoden gelistet, u.a.:
Code:
getNewLeadSelectionPath()
Darüber musst du dir den angeklickten Node holen. Und wenn du den Node hast, dann machst du das was ich gepostet hab: über getUserData() die internen Daten rausholen, casten, und schon hast du deine VirtualMachine.

Es ist klar, dass du die API nicht ausnwendig kannst. Aber dazu gibt es die Doc. Wenn du mit einer Klasse arbeitest, dann lies dir die Doc durch, schau dir alleMEthoden an usw. Genau dann kommst du nämlich auch darauf, wie du es am besten machen kannst.
 

mysticado

Aktives Mitglied
Okay, passt! Die Daten bekomme ich daher nun, und es ist auch nicht wirklich schwer sie nun in die TextFields einzulesen...
Das Problem, das ich nun habe ist eben für jede VM einen Thread zu starten.

Wie muss der Thread aufgebaut werden? Genauso wie oben, per SwingWorker?

Edit:
Okay, nach all' den Schönheitskorrekturen hab ich mir meinen Code nochmals angeguckt und bin jetzt völlig durcheinander. Okay, ich kann jetzt alle im Netz befindlichen Rechner und VMs auslesen und sie zu einem Baum zusammenfügen - die jeweiligen VMs haben in dem Moment aber noch keine Werte.
Das ist ja klar, denn die Werte kommen ja erst mit der Veränderung der JTextfields!

Jetzt stellt sich mir die Frage - wie kann ich das denn machen das eventuelle Änderungen in den TextFields in der VM gespeichert werden beim Klicken auf einen anderen Node im Tree???? Und wie kombiniere ich das alles mit der Thread-Geschichte?

Jetzt erst kommt wohl der Hammer, oder? Denn alleine komm ich hier echt nicht mehr weiter, jetzt wo meine (damals noch laufende) GUI komplett zerschossen ist :D
 
Zuletzt bearbeitet:

hdi

Top Contributor
wie kann ich das denn machen das eventuelle Änderungen in den TextFields in der VM gespeichert werden beim Klicken auf einen anderen Node im Tree????
Du weißt ja, wie du auf das Anwählen von Nodes im Tree reagieren kannst: Mit einem SelectionListener. Also wo genau ist nun das Problem? Deine Frame-Klasse enthält eine Referenz auf den Tree, sowie auf die Textfelder, oder? Also fügst du dort einen neuen SelectionListener an den Tree, in dem du die Werte der Felder ausliest, und in der Node änderst. Welche Node ausgewählt ist kannst du ja auch über den Tree abfragen.
 

mysticado

Aktives Mitglied
Tja, manchmal sieht man den Wald vor lauter Bäumen nicht. Ich hab jetzt einfach einen weiteren SelectionListener implementiert, der die Werte aus den Feldern holt und....so, jetzt brauch ich wieder Hilfe :)
Der ActionListener reagiert ja nur auf einen Klick, nachdem man auf einen anderen Knoten geklickt hat. Wie aber behalte ich am Besten die Info welcher Knoten als letzter aktiv war (ich hätte jetzt sonst eine globale Variable "String letzteAuswahl" erstellt und dort immer den aktuellen Knoten reingespeichert)?
Oder vielleicht so: evt.getOldLeadSelectionPath().toString() ?

Und ausserdem hab ich noch ein kleines Problem mit dem Abspeichern der neuen Werte der VirtualMachine. Ich weiss leider nicht mal genau wie ich jetzt an die VirtualMachine selbst rankomme? DefaultMutableTree oder Object liefern mir nicht wirklich Methoden mit welchen ich konkret die VM-Werte ansprechen und abändern könnte, oder? getLastSelectedPathComponent sagt mir wohl welche VM ausgewählt war...doch wie ich konkret die Werte nun ändern kann weiss ich net :(
Ich habe schon Setter-Methoden in die VM Klasse reingeschrieben, jetzt muss ich nur noch wissen wie ich da drauf komme :)
 
Zuletzt bearbeitet:

hdi

Top Contributor
Oder vielleicht so: evt.getOldLeadSelectionPath().toString() ?
Das mit dem getOldLeadSelectionPath() stimmt, steht ja unmissverständlich in der API-Doc. Das mit dem toString() ist allerdings Blödsinn. Was willst du mit der String-Repräsentation dieses Objekts anfangen? Über den Path kommst du an die eigentliche Node. Siehe Methoden von TreePath.

Ich weiss leider nicht mal genau wie ich jetzt an die VirtualMachine selbst rankomme? DefaultMutableTree oder Object liefern mir nicht wirklich Methoden mit welchen ich konkret die VM-Werte ansprechen und abändern könnte, oder?
Richtig. getUserData() liefert dir zwar durchaus die VM, allerdings weiß das der Compiler nicht. Für ihn ist es lediglich Object. Liiegt daran, dass du dich ja gegen meinen Ratschlag für ein eigenes TreeModel entschieden hast. Ist halt jetzt etwas umständlicher.. Wie schon gesagt musst du nun casten. Dabei sagst du dem Compiler explizit, dass dieses "Object" in Wahrheit eine VM ist, und er seine "Sicht" auf dieses Objekt entsprechend umwandeln soll, sodass dir die Methoden aus der Klasse VM verfügbar gemacht werden. Casten geht so:

Code:
VirtualMachine vm = (VirtualMachine) object;

Und nun hast du über die Variable vm Zugriff auf deine ganzen Setter und Getter.
 

mysticado

Aktives Mitglied
Ach wie schön, nun habe ich mein Programm wieder auf den gleichen Punkt gebracht wie zu Anfang dieses Beitrages, nur dass meine Werte nun viel besser gespeichert sind und der Zugriff auf sie effizienter verläuft!
Ich muss echt noch einmal an dieser Stelle meinen Dank aussprechen für die bisherige Mühe - wir sind auch schon fast am Ziel :)

Es bleibt nur noch die Frage der Threads, bzw. wie ich zu jedem Node aus dem Tree jeweils einen Thread starten kann, nachdem auf "Simulate" geklickt wurde.

Ich habe eine [c]startButtonActionPerformed()[/c] Methode in meiner GUI, welche zuerst prüft ob bei "WebTraffic" das Häkchen dran gemacht wurde und ob in der Browse-Zeile auch eine Datei reingeladen ist.
Danach wird die Methode [c]utilizeVM()[/c] aufgerufen, welche eine VM-bezogene Prozedur durchführt.
Am Ende läuft dann noch der [c]javax.swing.Timer[/c], welcher den ganzen Thread abbrechen sollte entweder nachdem die Zeitspanne abgelaufen ist, oder der User selbst auf "Stop" geklickt hat.

Theoretisch müssten ja alle drei Befehle aus dem jeweiligen Thread gestartet werden können, da sie ja eigentlich alle direkt bezogen sind auf die VM.
Daher müsste ich jetzt nur noch wissen wie es denn am geschicktesten wäre den Thread auch wirklich zu starten? Muss der SwingWorker wieder herhalten? :)
 

hdi

Top Contributor
Also mit SwingWorker hat das jetzt nichts zu tun. Einen SwingWorker brauchst du nur, wenn der Thread Zugriffe auf die GUI macht. Aber ist ja nicht so, oder?

Wie und wo wir die Threads integrieren hängt jetzt davon ab, was die denn so benötigen um ihre Augabe erledigen zu können. Von dieser "Datei" die du da hast wusste ich bislang nichts. Was steht da drin, was hat die mit der Simulation zu tun?
 

mysticado

Aktives Mitglied
Aber ich dachte, die GUI muss verfügbar sein, denn ich möchte ja, dass ich z.B. die eine VM starte (was ja länger dauern kann, abhängig von der übergebenen Länge der Simulation), und während diese eine VM simuliert möchte ich auf eine andere switchen können, um auch sie starten zu können.
Das sind doch GUI-Zugriffe und daher dachte ich, brauche ich wieder den SwingWorker, oder?

Und was diese Datei anbelangt ist die ganze Geschichte eigentlich halb so wild. Bei der Datei handelt es sich um eine template Datei die für die Netzwerkauslastung wichtig ist. Die Netzauslastung stoße ich nämlich mit einem externen Programm an und dieses braucht in der Kommandozeile nunmal diese template Datei, welche durch eine XML-Struktur aufgebaut ist. Im Großen und Ganzen kann ich das alles aber checken noch bevor der Thread gestartet wird, also können wir das einfach mal vergessen.

Der Thread selbst führt wie gesagt nur die Methode utilizeVM() aus, welche im Endeffekt nichts anderes macht als über [c]Runtime.getRuntime().exec[/c] sich per ssh auf der VM einzuloggen und sie auszulasten. Sprich, mit der GUI selbst hat das ja nichts mehr zu tun.
Die zweite Sache die der Thread dann noch machen müsste ist eben für jede VM den Timer ablaufen zu lassen. Nachdem der Timer dann abgelaufen ist, logge ich mich wieder per ssh in die VM und stoppe alle Auslastungsbefehle.

Das ist alles was ich brauche...jede VM soll für sich nach dem Drücken auf den "Simulate"-Button einen Timer starten und die Auslastung anstoßen und nichts mehr. Eigentlich sollte das ja dann nicht mehr so schwer zu bewerkstelligen sein, oder? :)
 

hdi

Top Contributor
Aber ich dachte, die GUI muss verfügbar sein, denn ich möchte ja, dass ich z.B. die eine VM starte (was ja länger dauern kann, abhängig von der übergebenen Länge der Simulation), und während diese eine VM simuliert möchte ich auf eine andere switchen können, um auch sie starten zu können.
Das sind doch GUI-Zugriffe und daher dachte ich, brauche ich wieder den SwingWorker, oder?
SwingWorker brauchst du nur, wenn du GUI-Zugriffe aus einem externen Thread hast. Wenn du in deiner GUI rumklickst dann macht das der EDT. Also sowieso schon der richtige Thread. SwingWorker bräuchtest du nur an den Stellen, wo der neu gestartete Simulations-thread etwas an der GUI verändert. Aber das ist nicht vorgesehen, oder?
Und was diese Datei anbelangt ist die ganze Geschichte eigentlich halb so wild. Bei der Datei handelt es sich um eine template Datei die für die Netzwerkauslastung wichtig ist. Die Netzauslastung stoße ich nämlich mit einem externen Programm an und dieses braucht in der Kommandozeile nunmal diese template Datei, welche durch eine XML-Struktur aufgebaut ist. Im Großen und Ganzen kann ich das alles aber checken noch bevor der Thread gestartet wird, also können wir das einfach mal vergessen.
Vergessen können wir das nicht. Pack das auch noch in deine VM-Klasse rein als Instanz-Variable mit Getter & Setter.

Der Thread selbst führt wie gesagt nur die Methode utilizeVM() aus, welche im Endeffekt nichts anderes macht als über Runtime.getRuntime().exec sich per ssh auf der VM einzuloggen und sie auszulasten. Sprich, mit der GUI selbst hat das ja nichts mehr zu tun.
Die zweite Sache die der Thread dann noch machen müsste ist eben für jede VM den Timer ablaufen zu lassen. Nachdem der Timer dann abgelaufen ist, logge ich mich wieder per ssh in die VM und stoppe alle Auslastungsbefehle.
Das ist alles was ich brauche...jede VM soll für sich nach dem Drücken auf den "Simulate"-Button einen Timer starten und die Auslastung anstoßen und nichts mehr. Eigentlich sollte das ja dann nicht mehr so schwer zu bewerkstelligen sein, oder?

Nein, das ist noch recht easy. Aber vorhin meintest du noch:
welcher den ganzen Thread abbrechen sollte entweder nachdem die Zeitspanne abgelaufen ist, oder der User selbst auf "Stop" geklickt hat.
Das wiederum macht das ganze deutlich komplizierter.

Du... ich schreib an diesem Post seit über 30 Minuten. Es ist sau kompliziert, dir das zu erklären.. Wenn du nix dagegen hast, würde ichd ich bitten mir deinen gesamten Code zu geben. (Lad ihn irgendwo hoch). Ich msus das direkt da reincoden, sonst reden wir noch stundenlang weiter und ich weiß nie ob du das jetzt so eingebaut hast wie ich meinte..

Am Ende werde ich dir alle Fragen dazu beantworten, aber ich glaub das kriegen wir jetzt wesentlich schneller hin wenn ich dir dieses Programm einfach runterschreib...Schau mal wie viel Text hier schon in dem Topic steht.. Das geht noch über 10 Seiten so weiter wenn wir so weitermachen.
 

mysticado

Aktives Mitglied
Also,
ich habe es währenddessen ein bisschen selber probiert und meine, dass es so jetzt evtl. sogar klappen könnte - belehre mich bitte des Besseren :)
Ich habe einfach noch einen SwingWorker drum rum gemacht (ja, ich weiss, gerade hattest du mir geschrieben, dass das so nicht gedacht ist - ich wollt's einfach mal probieren :) ) und irgendwie benimmt sich das Programm jetzt so wie ich es mir dachte. Oder bilde ich es mir ein?

Ja, die Idee, dass du dir am Besten mal selber den Code anguckst ist gar nicht mal so schlecht, nur wollte ich dich nicht um sowas bitten. Jedenfalls schick ich dir jetzt gleich ne pn....und bitte bitte, sei nicht zu kritisch wenn du dann siehst was ich da so alles fabriziert habe :)
Der Code braucht ja auch nicht schön sein - es soll einfach nur funktionieren :D

Die wichtigsten Sachen findest du im [c]startButtonActionPerformed[/c] , [c]stopButtonActionPerformed[/c] und [c]createTree[/c] .

Wenn du die GUI dann live siehst, wird dir vielleicht einiges klarer, allein was die Funktionalität der einzelnen Dinger betrifft...

Also, danke nochmals!!!!!!!!!!!!!!!!
 
S

SlaterB

Gast
muss man alles bisherige wissen/ lesen oder kannst du nochmal neu zusammenfassen was das Thema/ noch offene Punkte sind? ;)
 

mysticado

Aktives Mitglied
Haha, ja es sollte machbar sein, dass du quer mit einspringst :)
Die ersten zwei Seiten haben eigentlich nicht wirklich was mit meinem eigentlichen Problem zu tun, sondern behandelten ein paar hässliche Programmierfehler von mir. Nun da wir sie in den ersten 2 Seiten des Beitrag bearbeitet haben, können wir uns auch endlich meinem echten Problem widmen - den von mir benötigten Swing-Threads :)
Im Großen und Ganzen geht es darum, dass ich eine GUI habe, welche aus dem LAN alle Rechner scannt, und die auf ihnen befindlichen VM's. An dieser Stelle ist es unwichtig zu sagen wie das geht - es funktioniert jedenfalls, was bedeutet, dass wir die Daten für meine GUI haben.
Nun habe ich auf der linken Seite meiner GUI einen JTree erstellt welcher die Rechner + die darauf befindlichen VMs anzeigt.
Das Ziel ist es nun, auf einer der VMs klicken zu können, ein paar Werte auf der rechten Seite der GUI zu verändern und dann per "Simulate"-JButton einen Befehl an die jeweilige VM zu schicken. Das sollte nun in einen Thread verpackt werden, um es mir zu ermöglichen gleichzeitig noch weitere VMs auszuwählen, um auch denen diese Befehle schicken zu können.
Das hört sich vielleicht simpel an, doch alleine kam ich da leider nicht weiter :(
hdi war jedenfalls so nett und hat mir mühselig Schritt für Schritt erklärt gehabt bisher, und ich muss sagen - es hat gefruchtet :) Wenn ihr mir noch mit dem letzten Schritt (Threads) helfen könntet wäre ich super zufrieden und auch mit der 1 müsste es dann klappen ;)
Gruß!
 

hdi

Top Contributor
@mysticado

Ich hab kurz reingekuckt. Was mir sofort aufgefallen ist, ist dass du in der NetworkEntity Klasse die Liste der VMs static deklarierst hast. Ich weiß nicht wie du darauf gekommen bist, denn ich hab dir den Code ja ganz konkret gegeben. Also das ist falsch... Die Liste darf nicht static sein! Sonst hast du in jedem Netwerk-Knoten ALLE VM's drin, und nicht nur die zu der jeweiligen NetworkEntity gehörigen VMs.

Ansonsten muss ich ehrlich sagen hab ich dann das ganze schnell wieder geschlossen als ich deine GUI Klasse gesehen habe.. Wann begreifen die Leute endlich, dass diese GUI Builder vorallem am Anfang kontraproduktiv sind. Und anschauen will sich sowas keiner.

Deswegen kann ich dir jetzt auch keinen konkreten Code mehr geben, zumindest nicht an bestimmte Zeilen in deinem Programm einbauen. Denn wie was wo genau gemacht wird hängt davon ab wie deine GUI im gesamten funktioniert. Da gibt's allerhand zu beachten. zB inwiefern müssen sich die Werte die in der GUI angezeigt werden mit der Simulation synchronisieren. Was passiert, wenn du einfach 5x hintereinander auf Start drückst? Was passiert, wenn du auf Stopp drückst obwohl gar keine Simulation läuft? Über solche Dinge solltest du dir Gedanken machen.

Wie es jetzt im Groben mit den Threads weitergeht:
Du musst beim Start einen neuen Thread starten, der mit der VM-Instanz verknüpft ist. zB kannst du dafür in der VM-Klasse das Interface Runnable implementieren,und starten kannst du die Simulation dann über

Java:
new Thread(vm).start();

In die run-Methode der VM-Klasse kommt dann die Simulation rein. Du haust deinen Befehl raus, und betrittst dann eine Schleife die die Zeit runterzählt.Sobald sie abgelaufen ist sendest du den Befehl um die Auslastung zu beendne. Da du das ganze jederzeit über "Stopp" beenden können willst, musst du eine entsprechende Variable anlegen, zB "running". Diese Variable wird in o.g. Schleife jedes mal geprüft. Bei Klick auf Stopp setzt du sie auf flase, das bekommt dann deine Simulation mit und verlässt die run-Methode. Also so in etwa:
Java:
@Override
public void run(){
    running = true;
    // start auslastung über dein externes programm
    while(running && restZeit > 0){
         // restzeit verringern um sleepWert (evtl zu sekunden runterrechnen, denn sleepWert ist in ms)
         // Kurze Pause via Thread.sleep(sleepWert);
    }
    // stop auslastung über programm
    running = false;
}

D.h. das Programm läuft solange bis die eingestellte Zeit vorbei ist oder bis der User running über die GUI auf false setzt. Für das Running machst du dir halt einen Getter und Setter, damit du in der GUI weißt ob grad ne Simulation läuft, und damit ob man auf Start oder Stopp drücken kann. Also zB

Java:
// Start drücken
if(vm.isRunning() == false){
    new Thread(vm).start(); 
}
else{
   // läuft bereits
}

Java:
// Stopp drücken
if(vm.isRunning() == true){
    vm.setRunning(false);
}
else{
    // simulation läuft gar nicht
}

wenn du das so machst ist esnur wichtig dass die oben genannte "sleeptime" für den Thread-Loop nicht zu lang ist.. Das ganze ist etwas schlampig gemacht. Aber ich glaub das reicht dir erstmal..

Wichtig ist dann noch, dass du synchronisieren musst. Wenn du's so machst wie grad beschrieben reicht es wenn du das running einfach volatile machst:

Java:
private volatile boolean running;

Ich hoffe du hast verstanden was ich meine. Die Simulation ziehst du halt in die VM-Klasse rein, also in die run-Methode. und über dieses "running" kannst du ermitteln ob grad ne Simulation läuft oder nicht und die jederzeit beenden.

edit: Wie du siehst hat das alles nix mit SwingWorker zu tun. Du startest einen ganz normalen neuen Thread, siehe die Zeile oben. Ich hab dir jetzt schon zwei mal ausführlich beschrieben, wann du SwingWorker brauchst und wann nicht.. Du musst die Antworten etwas aufmerksamer lesen.. Dann passiert es dir auch nicht dass du einfach irgendwo ein "static" einbaust, wo es nix zu suchen hat.
 
Zuletzt bearbeitet:

mysticado

Aktives Mitglied
Okay, all die Änderungen habe ich eingeführt (andere waren schon drin, ob Du's glaubst oder nicht :D Z.B. die Variable "running" hatte ich schon, und auch die Getter und Setter usw. auch) und nun funktioniert das auch so wie ich mir das gedacht habe.

Eine Sache wäre da aber noch...ich starte ja nun jede VirtualMachine-Instanz als eigenen Thread in dem dann auch der Timer läuft usw. Das ist auch schön und gut so, nur habe ich jetzt folgendes Problem - ich habe in meiner GUI ein JTextField "output" in welchem der jeweils aktuellste Schritt rausgeschrieben wird.
Nun würde ich wollen, dass nachdem der Timer abgelaufen ist, das output-field auch upgedated wird. Dieses müsste ich nun aber aus der VirtualMachine aus aufrufen. Mit [c]new WebserverUtilizer().setOutput("bla")[/c] kann ich das aber nicht bewerkstelligen, da dies ja eine neue Instanz der GUI-Klasse erzeugen würde, oder?
Eventuelle Ideen?
 

hdi

Top Contributor
ich habe in meiner GUI ein JTextField "output" in welchem der jeweils aktuellste Schritt rausgeschrieben wird. Nun würde ich wollen, dass nachdem der Timer abgelaufen ist, das output-field auch upgedated wird.
Wie jetzt.. soll die VM einmalig was reinschreiben, wenn die Simulation fertig ist, oder soll sie während der Simulation, d.h. öfters, etwas da reinschreiben?

Im Prinzip gibt es wei Ansätze:
1) Die VM hat eine Referenz auf das Textfeld und schreibst da driekt was rein
2) Die GUI hängst sich als Listener an die VM und schreibt das Textfeld selbst.

Ich würd Variante 2 empfehlen.

D.h. mach ein Interface SimulationListener mit einer Methode outputFired(...), und spendier der VM-Klasse eine Variable zum Speichern solch eines Listeners. Deine GUI implementiert dieses Interface, d.h. diese outputFired-Methode, und hängst sich beim Start der Simulation als Listener an die VM. Und die VM ruft in ihrer run-Methode diese outputFired() auf.

Jetzt ist nur blöd wenn mehrere Simulationen laufen.. Weil ich schätze du willst ja nur den output anzeigen von der VM die grad in der GUI ausgewählt ist. D.h. diese outputFired() braucht wohl als Parameter schon mal die referenz auf die VM, die die Methode aufruft. Aber selbst dann funktioniert das ganze mit dem Textfeld-Update nur wenn zu diesem Zeitpunkt die richtige VM ausgewählt ist..

Das hätte man von vornherein berücksichtigen müssen Ich hatte das Thema öfters angesprochen:

zB inwiefern müssen sich die Werte die in der GUI angezeigt werden mit der Simulation synchronisieren.

[...] wo der neu gestartete Simulations-thread etwas an der GUI verändert. Aber das ist nicht vorgesehen, oder?

Du hast leider nie irgendetwas von Output gesagt...
 

mysticado

Aktives Mitglied
Ja, ich bin mir bewusst, dass du es schon öfters angesprochen hast, aber ich muss leider zugeben, dass ich nicht dachte, dass sich das kleine Projekt so weit entwickeln würde. Anfangs lief ja auch alles für eine VM, doch als mir gesagt wurde, dass auf einmal mehrere solcher VMs gleichzeitig bedienbar sein sollen, stieß ich an meine Grenzen. Es wäre wohl doch besser gewesen, ich hätte dann alles von Anfang an angefangen zu programmieren, statt den damals bestehenden Code abzuändern, doch aus Fehlern lernt man ja bekanntlich. Und du merkst, so viele Programmierprojekte hatte ich in meinem Leben noch nicht ;)

Übrigens, jede VM soll den Output ausgeben, wie z.B. "VM Nr. X - Simulation beendet".
Über die StopButtonActionPerformed geht sowas ja leicht. Das einzige Problem ist eben, sowas aus der VirtualMachine Klasse zu triggern nachdem der Timer abgelaufen ist. Aber ich probiers mal mit dem interface-Ansatz! Drück mir die Daumen :)
Für weitere Tipps bin ich immer offen ;)

Edit: Vielleicht ist es doch einfacher es mit der Referenz zu machen, da jede VM ihren Text eingibt...
Was genau hast du denn gemeint mit Referenz?

Edit2: Also ich weiss schon was Referenzen sind (nur um Missverständnisse aus der Welt zu schaffen ;) ), nur war's mir unklar wie ich das denn mit diesem JTextField machen kann, da ich den Inhalt immer abändere mit [c]textfield.append("bla")[/c] ...und da weiss ich leider nicht wirklich, wie ich das aus einer anderen Klasse per Referenz machen kann??? :(
 
Zuletzt bearbeitet:

mysticado

Aktives Mitglied
Einmal brauch ich noch Hilfe...dann bist du mich (höchstwahrscheinlich) los :)

Also, ich habe nun in meiner WebserverUtilizer-Klasse noch "implements SimulationListener" hinzugefügt. Das hat dazu geführt, dass in dieser Klasse nun auch die Methode "outputFired()" erschienen ist.
Ich habe ausserdem in meiner VirtualMachine-Klasse eine Variable "String output" welche den rauszuschreibenden String enthalten soll, erstellt. Dieser String wird gesetzt nachdem der Timer abgelaufen ist.
Jetzt fehlt mir nur noch der Schritt wie meine GUI diesen String erhalten kann? Wie kann ich mich denn als Listener dran hängen? Die API liefert ja Millionen an Infos und Beispielen wie ActionListener erstellt werden, doch so konkret wie bei mir hab ich leider nichts finden können :(

...dieses eine Mal noch bitte :)
 

hdi

Top Contributor
Java:
class VM{

    private SimulationListener listener;

    public void setListener(SimulationListener l){
         listener = l;
    }

    // wenn du den Listener benachrichtigen willst:
    listener.outputFired(this, toString() +" has completed");
}

Java:
class GUI implements SimulationListener{

    // beim Starten der Simulation:
    vm.setListener(this);
    new Thread(vm).start();

    @Override
    public void outputFired(VM vm, String output){
          VM selected = (VM) tree.getSelectedValue();
          if(selected == vm){
               textfield.setText(output);
          }
    }
}

Ist eigentlich nichts magisches dran, oder? ;) Du übergibst der vm halt die GUI über setListener, und in der VM wird ja auf genau dieser Referenz die outputFired-Methode aufgerufen, also landest du in der GUI.
 
Zuletzt bearbeitet:

mysticado

Aktives Mitglied
Hi, da wär ich wieder :)
also erst nochmal danke für die Erklärung bzgl. der Listener. Ich bin immer noch fasziniert wie einfach das doch geht :)
Ich wollte nun erstmal abwarten, um zu sehen was mein Betreuer zur GUI sagt, bevor ich mich wieder hier melde. So, und nun hat er sie sich angeguckt und war überaus zufrieden mit ihr!
Eine Frage hat er mir aber noch gestellt und zwar fragt er sich, ob meine GUI noch folgendes anzeigen könnte: Wenn die Virtuellen Maschinen von einem Rechner auf der anderen migriert werden, müsste theoretisch auch der JTree angepasst werden. Er fragte mich daher, ob ich nicht noch einen Thread einbauen könnte, welcher nach einer bestimmten Zeit den Tree einfach aktualisieren könnte?
Im Großen und Ganzen wäre das ja nicht schwer. Das einzige Problem, dass ich dabei habe ist, dass ja in den einzelnen Nodes meines JTrees die ganzen VM-Infos drin stehen. Sprich, wenn ich die einzelnen Rechner und VM's neu einlesen würde, würde ich alle gesetzten Werte verlieren oder?
Was mich daher (rein theoretisch) interessieren würde: Könnte man irgendwie die Hierarchie neu einlesen und die alten Werte wieder übernehmen?
 

hdi

Top Contributor
Du kannst dir vor dem Aktualisieren des Trees alle VM's in einer Liste abspeichern, und nach dem Aktualisieren die Einträge des neuen Trees gegen diese abgleichen. Dafür kannst du die equals()-Methode in der Klasse VM entsprechend implementieren. Wenn dann die neu eingelesne VM einer der alten aus der "Backup-Liste" entspricht (wie gesagt: per equals()), dann setzt du halt die Werte.

Code:
neueVm.setX(alteVm.getX());

Du musst jetzt nur überlegen wie du equals() implementierst. Du musst irgendwie rausfinden wann zwei VMs semantisch gleich sind. Selbe IP?
 

mysticado

Aktives Mitglied
Okay, nun habe ich das auch implementiert, doch wie das so ist will natürlich etwas wieder nicht so wie ich es gern hätte.
Mein Code sieht ungefähr so aus:

Java:
public void refreshTree(){

while(true){
Thread.sleep(60000);
System.out.println("Refreshing tree");

saveOldVMsInList();

deleteOldDatabase();

readInNewDatabase(){
compareVMsFromListWithVMsFromDB};

createTreeFromNewDatabase();
System.err.println("Show number of children: "+node.getChildCount());

tree.validate();
tree.setModel(new javax.swing.tree.DefaultTreeModel(root))
}
}

Die Idee war also die, alle 60 Sekunden die alten VMs in die Liste zu speichern und beim erzeugen der neuen Database die alten Werte zu berücksichtigen. Soweit klappt das auch ganz gut (so wie es aussieht - ich bekomme jedenfalls keine errors zurück), nur habe ich jetzt ein paar Probleme!
Zum Einen wird der Baum einfach nicht "refresht", obwohl die validate()-Methode das ja eigentlich sofort initiieren müsste, oder? Aber das könnte auch ein Bug von meiner Seite sein, denn ausserdem habe ich bemerkt, dass wenn ich meine GUI starte und nichts an ihr verändere, eine VM aber migriere, dass dann der Tree nach der Migration (und nach dem Update) korrekt angezeigt wird.
Wähle ich aber ein Element im Tree aus, oder verändere es - dann klappt das Neuzeichnen des Trees nicht mehr. Zudem ist die Ausgabe meines System.err auch ganz komisch.
Zuerst wird alles korrekt angezeigt - Knoten 1 hat 2 Kinder, Knoten 2 hat ein Kind. Dann migriere ich eine VM und bekomme prompt den neuen Wert auch korrekt angezeigt (also, Knoten 1: 2, Knoten 2: 1), doch gleich hintendran wird noch ein Wert ausgegeben, der dann anzeigt Knoten1: 0, Knoten 2: 0. Hä??? Ich verstehe überhaupt nicht wieso auf einmal 2 Werte ausgegeben wurden, zumal dieser Thread doch erst alle 60 Sekunden Sachen rausschreiben müsste?
Hier meine Ausgabe:
Refreshing tree: 14:10:21
Refreshing tree: 14:10:22
Show number of children: 1
Show number of children: 2
Show number of children: 0
Show number of children: 0

any ideas?
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
T hallo, habe ein Problem mit dem pro. eines Threads AWT, Swing, JavaFX & SWT 4
G problem mit threads/repaint ! AWT, Swing, JavaFX & SWT 2
F Verständis Problem zu Threads AWT, Swing, JavaFX & SWT 2
V Problem mit Aktualisieren von JList mit Threads AWT, Swing, JavaFX & SWT 3
C Schweres Problem mit JDialog und Threads! Anzeige blockiert! AWT, Swing, JavaFX & SWT 5
D Problem mit JProgressBar und Threads AWT, Swing, JavaFX & SWT 7
G Problem mit der Anzeige von jLabel. Unlesbar wenn der Text geändert wird. AWT, Swing, JavaFX & SWT 28
H 2D-Grafik Problem mit Paint AWT, Swing, JavaFX & SWT 1
S Layout - Problem AWT, Swing, JavaFX & SWT 1
Tassos JavaFX/Problem mit der Maussteuerung in Stackpane AWT, Swing, JavaFX & SWT 7
sserio Java Fx - Problem AWT, Swing, JavaFX & SWT 3
A Problem Spiel auf Panel der GUI zu bringen AWT, Swing, JavaFX & SWT 1
A JavaFX Controller Problem AWT, Swing, JavaFX & SWT 1
TheWhiteShadow JavaFX ListView Problem beim Entfernen von Elementen AWT, Swing, JavaFX & SWT 1
E LayoutManager Welcher Layout-Mix löst mein Problem? AWT, Swing, JavaFX & SWT 3
Umb3rus JavaFX Problem mit PropertyValueFactory: can not read from unreadable property AWT, Swing, JavaFX & SWT 1
T Problem mit paintComponent() AWT, Swing, JavaFX & SWT 17
AmsananKING Java Menü-Problem AWT, Swing, JavaFX & SWT 1
K JavaFX Resizing-Problem beim BorderLayout (Center Component) beim Arbeiten mit mehreren FXMLs AWT, Swing, JavaFX & SWT 2
G Instance OF Problem AWT, Swing, JavaFX & SWT 9
FrittenFritze Ein Problem mit der CSSBox, die Größe wird nicht angepasst AWT, Swing, JavaFX & SWT 5
M Problem mit dem Anzeigen von Frames im Vordergrund AWT, Swing, JavaFX & SWT 5
Badebay Problem mit JButton AWT, Swing, JavaFX & SWT 2
newJavaGeek Grid-Layout problem AWT, Swing, JavaFX & SWT 7
J JavaFX Löschen im Tabelview macht Problem AWT, Swing, JavaFX & SWT 15
JavaTalksToMe JavaFx ExekutorService Problem AWT, Swing, JavaFX & SWT 2
Zrebna Problem bei Eventhandling (Value soll nach jedem erneutem Klick gelöscht werden) AWT, Swing, JavaFX & SWT 4
B Problem mit JavaFX AWT, Swing, JavaFX & SWT 5
J css Problem AWT, Swing, JavaFX & SWT 5
B JavaFX habe mein Problem fett markiert AWT, Swing, JavaFX & SWT 2
A Swing Filter-Problem AWT, Swing, JavaFX & SWT 1
temi JavaFX Problem mit IntelliJ und JavaFx 11 unter XUbuntu AWT, Swing, JavaFX & SWT 3
L Java FX Problem mit Ubuntu 18 und JavaFx AWT, Swing, JavaFX & SWT 27
H JTable TableCellEditor-Problem AWT, Swing, JavaFX & SWT 0
kodela Swing Problem mit Warten-Dialog AWT, Swing, JavaFX & SWT 16
B JavaFx Scene Builder Problem AWT, Swing, JavaFX & SWT 2
B [Problem] Java öffnet Word-Datein nicht AWT, Swing, JavaFX & SWT 14
T DataBinding Problem AWT, Swing, JavaFX & SWT 5
Blender3D Problem mit € Symbol Font Gotham Windows 10 Swing AWT, Swing, JavaFX & SWT 11
T Problem mit JTable Sortierung AWT, Swing, JavaFX & SWT 2
J Problem mit Platfrom run later AWT, Swing, JavaFX & SWT 15
J Problem mit Platfrom run later AWT, Swing, JavaFX & SWT 0
D Swing SwingUtils / Thread Problem AWT, Swing, JavaFX & SWT 3
L JavaFX Problem beim Aufrufen einer Methode AWT, Swing, JavaFX & SWT 5
T Swing Problem mit Datum und FormattedTextField AWT, Swing, JavaFX & SWT 2
S AWT Java print dialog Problem AWT, Swing, JavaFX & SWT 0
olfibits JavaFX Problem mit HTMLEditor AWT, Swing, JavaFX & SWT 0
W SWT hover-background-problem with first column in TreeViewer AWT, Swing, JavaFX & SWT 0
M Problem mit Add JScrollPane AWT, Swing, JavaFX & SWT 25
Mario1409 Swing JTextArea scroll Problem AWT, Swing, JavaFX & SWT 0
N Swing Problem mit loop AWT, Swing, JavaFX & SWT 2
S Swing Problem mit Button und ActionListener AWT, Swing, JavaFX & SWT 5
S Swing & Clean und build Problem AWT, Swing, JavaFX & SWT 12
S JLabel setText() Problem AWT, Swing, JavaFX & SWT 6
I 2D-Grafik Problem beim Ändern der Farbe eine 2d Objekts AWT, Swing, JavaFX & SWT 3
G Swing Splitpane Problem AWT, Swing, JavaFX & SWT 1
F Problem mit der FXML Rectangle Shape AWT, Swing, JavaFX & SWT 2
N JavaFX Stranges Problem mit der Autoscroll-Eigenschaft von Textareas AWT, Swing, JavaFX & SWT 0
E Java FX FXML Problem mit html Scriptausführung AWT, Swing, JavaFX & SWT 2
J JavaFX Intersect Problem mit Shapes AWT, Swing, JavaFX & SWT 10
R JavaFX MediaPlayer AVI-Problem AWT, Swing, JavaFX & SWT 1
M Swing Problem mit ListCellRenderer AWT, Swing, JavaFX & SWT 7
D Problem mit JTable AWT, Swing, JavaFX & SWT 1
F GUI Auflösung ändern - Koordianten und Proportions Problem AWT, Swing, JavaFX & SWT 21
J Problem mit Button darstellung AWT, Swing, JavaFX & SWT 23
M Problem mit Layoutmanagern... Hilfe wäre sehr nett. AWT, Swing, JavaFX & SWT 2
S 2D-Grafik Problem mit Variablen AWT, Swing, JavaFX & SWT 4
7 JavaFX Problem beim Zeichnen eines Dreiecks in einem GUI AWT, Swing, JavaFX & SWT 6
M Swing AttributiveCellTableModel addRow() Problem AWT, Swing, JavaFX & SWT 1
J Swing Problem mit Graphics Methode AWT, Swing, JavaFX & SWT 4
N JavaFX Problem mit table multiple selection AWT, Swing, JavaFX & SWT 5
K CheckBox Problem AWT, Swing, JavaFX & SWT 5
Grevak DisplayMode Problem seit Windows 10 AWT, Swing, JavaFX & SWT 2
S Swing Eigene JComboBox Problem! AWT, Swing, JavaFX & SWT 1
B Swing Problem mit Bildpfad AWT, Swing, JavaFX & SWT 4
N Swing Problem beim Scrollen mit JScrollPane AWT, Swing, JavaFX & SWT 6
V Graphics g - drawOval problem mit background AWT, Swing, JavaFX & SWT 1
C AWT Problem mit Protokol Fenster AWT, Swing, JavaFX & SWT 0
M Swing pack() Problem mit Taskleiste AWT, Swing, JavaFX & SWT 4
N Swing Choice- Problem! AWT, Swing, JavaFX & SWT 8
Q "AWT-EventQueue-0" Exception Problem AWT, Swing, JavaFX & SWT 4
D jButton Problem, ein Rieser Button bedeckt das ganze frame AWT, Swing, JavaFX & SWT 1
A Problem: repaint() - Schleife AWT, Swing, JavaFX & SWT 3
J Anfänger GUI Problem bei der Ausführung eines sehr einfachen Programms AWT, Swing, JavaFX & SWT 2
P AWT Problem mit Platzierung (GridBagLayout) AWT, Swing, JavaFX & SWT 2
N Swing JTree Problem beim erstellen der Knoten AWT, Swing, JavaFX & SWT 0
N Swing CardLayout: Problem beim Wechsel zwischen den JPanels AWT, Swing, JavaFX & SWT 3
A Mini-Menu-Schriften. Ein Problem bei hohen DPI Zahlen AWT, Swing, JavaFX & SWT 2
Z Canvas in Frame einfügen. Problem mit 4-Gewinnt AWT, Swing, JavaFX & SWT 1
C Thread-/ Simulations- Problem AWT, Swing, JavaFX & SWT 18
G Swing Setvisible problem AWT, Swing, JavaFX & SWT 1
J JTabbedPane: close Button Problem AWT, Swing, JavaFX & SWT 2
Tom299 JavaFX -> fxmlLoader -> getResourceAsStream Problem AWT, Swing, JavaFX & SWT 1
T Problem: ComboBox und addItem AWT, Swing, JavaFX & SWT 5
M JTextArea wird nicht aktualisiert (ActionListener-Problem) AWT, Swing, JavaFX & SWT 1
T LayoutManager LookAndFeel-Problem AWT, Swing, JavaFX & SWT 4
F Problem mit Implementierung von Kollisionsabfrage AWT, Swing, JavaFX & SWT 5
vodkaz (javafx) Image Problem AWT, Swing, JavaFX & SWT 2
T Problem beim Zeichnen von Rechteck AWT, Swing, JavaFX & SWT 3
B JavaFX Problem bei Kamera / Group, gesamte Scene bewegt sich mit AWT, Swing, JavaFX & SWT 0

Ähnliche Java Themen

Neue Themen


Oben