# IndexOutOfBoundsException bei neuen Daten in JTable



## Ollek (6. Jan 2012)

Hallo,

ich erhalte wie im Titel des Themas beschrieben eine IndexOutOfBoundsException, wenn ich neue Daten in eine JTable lade.

Folgende Anwendung:

Es ist eine Server/Client Anwendung.
Die Anwendung dient zum verwalten von so genannten Jobs. 
Aktuell besteht das Problem beim löschen eines Jobs. Vom Client wird ein Command an den Server geschickt, der das löschen dieses Jobs veranlasst. Der Server gibt einen Command an alleine mit dem Server verbundenen Clients zurück, dass die ihre JobListe, diese wird in der JTable dargestellt, neuladen.

Und genau bei diesem neuladen tritt die o.g. Fehlermeldung auf.
Ich weiß nicht, wo ich den Bug habe, werde hier die passenden relevanten Klassen posten. Ich verzweifle an diesem Problem, bin seit vorgestern morgen dran und komme nicht weiter.. Viele SystemOuts gemacht, aber der Fehler tritt immer und immer wieder auf.

Meine Vermutung ist, dass der Ordner vom Server nocht nicht gelöscht ist, wenn der Client die liste neu zeichnen soll. Aber das ist nur eien Vermutung.. Aber eigentlich sollte der Server doch erst die Message an den Client senden, wenn dieser Job auch wirklich gelöscht ist. Vielleicht habe ich dort auch den Fehler gemacht. Ist meine erste Server/Client Anwendung.
habt Nachsicht, wenn der Code nicht gut ist.. ;(

Folgende Fehlermeldung

```
Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Invalid index
	at javax.swing.DefaultRowSorter.convertRowIndexToModel(Unknown Source)
	at javax.swing.JTable.convertRowIndexToModel(Unknown Source)
	at javax.swing.JTable.getValueAt(Unknown Source)
	at javax.swing.JTable.prepareRenderer(Unknown Source)
	at javax.swing.plaf.basic.BasicTableUI.paintCell(Unknown Source)
	at javax.swing.plaf.basic.BasicTableUI.paintCells(Unknown Source)
	at javax.swing.plaf.basic.BasicTableUI.paint(Unknown Source)
	at javax.swing.plaf.ComponentUI.update(Unknown Source)
	at javax.swing.JComponent.paintComponent(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintToOffscreen(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
	at javax.swing.RepaintManager.paint(Unknown Source)
	at javax.swing.JComponent._paintImmediately(Unknown Source)
	at javax.swing.JComponent.paintImmediately(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$000(Unknown Source)
	at java.awt.EventQueue$1.run(Unknown Source)
	at java.awt.EventQueue$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
```

ServerSocket

```
package de.ernsting.network;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Hashtable;

import org.apache.log4j.Level;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

import de.ernsting.command.JobCommand;
import de.ernsting.util.Config;
import de.ernsting.util.Log4JLogger;

public class Server {

	private Config config = Config.getInstance();
	private Log4JLogger logger = Log4JLogger.getInstance();
	private Hashtable outputStreams = new Hashtable();
	private ServerSocket serverSocket;
	private Scheduler sched;
	
	public void listen(){
		try {
			serverSocket = new ServerSocket(Integer.parseInt(config.getProperty("network.socket.port")));
			System.out.println("Server gestartet auf " + serverSocket.getInetAddress().getHostAddress()  + ":" + serverSocket.getLocalPort());
			
			String timeout = config.getProperty("network.socket.timeout");
	       
			if(!timeout.equalsIgnoreCase("never")) {
	        	  serverSocket.setSoTimeout(Integer.parseInt(timeout));
	        }
			
	        while(true){
	        	Socket socket = serverSocket.accept();
	        	System.out.println("Verbunden mit " + socket.getInetAddress().getHostAddress());
	        	
	        	ObjectOutputStream oOut = new ObjectOutputStream(socket.getOutputStream());
	        	outputStreams.put(socket, oOut);
	        	
	        	new ServerThread(this, socket);
	        }
		} catch (NumberFormatException e) {
			logger.log(Level.ERROR, this.getClass(), "Servern konnte nicht auf dem Port gestartet werden...", e);
			System.exit(1);
		} catch (IOException e) {
			logger.log(Level.ERROR, this.getClass(), "ServerSocket konnte nicht gestartet werden. da er schon läuft", e);
			System.exit(1);
		}
		try {
			serverSocket.close();
		} catch (IOException e) {
			logger.log(Level.ERROR, this.getClass(), "ServerSocket konnte nicht geschlossen werden.", e);
			System.exit(1);
		}
	}

	/**
	 * Gibt die Outputstreams als
	 * Enumeration zurück
	 * 
	 * @return
	 */
	public Enumeration getOutputstreams(){
		return outputStreams.elements();
	}

	/**
	 * Sendet das übergebene Objekt an alle
	 * verbundenen Clients
	 * 
	 * @param object
	 */
	public void sendToAll(JobCommand command){
		synchronized (outputStreams) {
			for(Enumeration e = getOutputstreams(); e.hasMoreElements();){
				ObjectOutputStream oos = (ObjectOutputStream) e.nextElement();
				try {
					oos.writeObject(command);
					oos.flush();
				} catch (IOException ex) {
					logger.log(Level.ERROR, this.getClass(), "Probleme beim Senden des Jobs an die Clients", ex);
				}
			}
		}
	}
	

	/**
	 * Socketverbindung wird getrennt
	 * 
	 * @param socket - zu trennendes Socket
	 */
	public void removeConnection(ServerThread socket) {
		outputStreams.remove(socket);
		synchronized (outputStreams) {
			System.out.println("Trenne Verbindung zum Client " + socket.getSocket().getInetAddress());
			try {
				
				socket.close();
				logger.log(Level.INFO, this.getClass(), "Der ServerThread wurde erfolgreich beendet.");
			} catch (IOException e) {
				logger.log(Level.ERROR, this.getClass(), "Konnte die Verbindung zum Client-Socket nicht schließen", e);
			}
		}
		
	}
	
}
```

ServerThread

```
package de.ernsting.network;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import org.apache.log4j.Level;
import org.ietf.jgss.Oid;

import de.ernsting.command.JobCommand;
import de.ernsting.fileaccess.FileIO;
import de.ernsting.upload.Upload;
import de.ernsting.util.Log4JLogger;

public class ServerThread extends Thread {
	
	private Log4JLogger logger = Log4JLogger.getInstance();
	private Socket socket;
	private Server server;
	private FileIO fileIO;
	private ObjectInputStream oIn;
	
	public ServerThread(Server server, Socket socket){
		this.socket = socket;
		this.server = server;
	
		// Thread starten
		start();
	}

	@Override
	public void run() {
		fileIO = new FileIO();
		try {
			oIn  = new ObjectInputStream(this.socket.getInputStream());
			while(true){
				JobCommand commandFromClient = (JobCommand) oIn.readObject();
				JobCommand commandToClient = new JobCommand();
				
				if(commandFromClient != null){
					boolean result = false;
//						if( commandFromClient.getMessage().equals("upload") && commandFromClient.getJob() != null){
//							System.out.println("ServerThread.run() - UPLOAD");
//							Upload upload = new Upload();
//							boolean result = upload.upload(commandFromClient.getJob());
//							if(result){
//								commandToClient.setMessage("reload");
//							}
						if( commandFromClient.getMessage().equals("delete") && commandFromClient.getJob() != null){
							System.out.println("ServerThread.run() - DELETE");
							result = fileIO.deleteDirectory(commandFromClient.getJob().getSourceDirectory());
							commandToClient.setMessage("reload");
							System.out.println("LÖSCHRESULT: " + result);
							logger.log(Level.INFO, this.getClass(), commandFromClient.getJob().getName() + " wurde erfolgreich gelöscht!");
						} else if( commandFromClient.getMessage().equals("reload")){
							System.out.println("ServerThread.run() - RELOAD");
							commandToClient.setMessage("reload");
						}
						if(result){
							logger.log(Level.INFO, this.getClass(), "MESSAGE to Clients");
							server.sendToAll(commandToClient);
						}
				}
			}
		} catch (IOException e) {			
			logger.log(Level.ERROR, this.getClass(), "Der Job wurde nicht verarbeitet", e);
		} catch (ClassNotFoundException e) {
			 logger.log(Level.ERROR, this.getClass(), "Die Klasse des Commands wurde nicht gefunden", e);
		}finally {
			server.removeConnection(this);
		}
	}
	
	public void close() throws IOException{
		oIn.close();
		socket.close();
	}
	
	private JobCommand getDataFromClient( ObjectInputStream oIn ) throws IOException {
        JobCommand  commandFromClient = null;         
        while ( commandFromClient == null ){
           try {
              commandFromClient = (JobCommand) oIn.readObject();
           } catch ( ClassNotFoundException e ) {
        	   logger.log(Level.ERROR, this.getClass(), "Konnte den Command nicht finden", e);
           }
        }
        System.out.println( "Get: " + commandFromClient.getMessage() );
     return commandFromClient;
    } 
	
	public Socket getSocket() {
		return socket;
	}
}
```

ClientSocket

```
public class Client implements Runnable{

	private Config config = Config.getInstance();
	private Log4JLogger logger = Log4JLogger.getInstance();
	private Socket clientSocket;
	private ObjectOutputStream oOut;
	private ObjectInputStream oIn;
	private JobController controller;
	
	public Client(JobController controller){
		this.controller = controller;
		connect();
	}
	
	/**
	 * Verbindet das Socket mit dem 
	 * ServerSocket
	 */
	public void connect(){
		try {
			clientSocket = new Socket(config.getSocketHost(), config.getServerPort());
			oOut = new ObjectOutputStream(clientSocket.getOutputStream());
			oIn = new ObjectInputStream(clientSocket.getInputStream());
			
			controller.setFrametitle(clientSocket.getInetAddress().getHostAddress());
			
			new Thread(this).start();
		} catch (UnknownHostException e) {
			logger.log(Level.ERROR, this.getClass(), "Host konnte nicht gefunden werden.", e);
			e.printStackTrace();
		} catch (IOException e) {
			logger.log(Level.ERROR, this.getClass(), "Konnte nicht mit dem Host verbunden werden.", e);
		}
	}
	
	/**
	 * Schickt einen Command an den Server
	 * 
	 * @param job
	 */
	public void sendCommandToServer(JobCommand commandToServer){
		if(clientSocket.isConnected()){
			try {
			System.out.println("Client.processJob()");
				oOut.writeObject(commandToServer);
				oOut.flush();
			} catch (IOException e) {
				logger.log(Level.ERROR, this.getClass(), "Job konnte nicht an den Server geschickt werden", e);
			}
		}
	}

		
	/**
	 * Schließ die Input- und OutputStream 
	 * und die Verbindung zum Socket
	 */
	public void close(){
		try {
			oOut.close();
			oIn.close();
			clientSocket.close();
		} catch (IOException e) {
			logger.log(Level.ERROR, this.getClass(), "Probleme beim Schließen der Verbindung zum Server", e);
		}
	}
	
	/**
	 * Thread um Sendungen vom Server
	 * anzunehmen und abzuarbeiten
	 */
	public void run(){
		while(true){
			System.out.println("Client.run() - while()");
			try {
				JobCommand commandFromServer = (JobCommand) oIn.readObject();
				
				if(commandFromServer.getMessage().equals("reload")){
					logger.log(Level.INFO, this.getClass(), "Client.run() - RELOAD");
					controller.updateJobList();
				}
			} catch (IOException e) {
				logger.log(Level.ERROR, this.getClass(), "Der Job konnte nicht verarbeitet werden.", e);
			} catch (ClassNotFoundException e) {
				logger.log(Level.ERROR, this.getClass(), "Der Job konnte nicht verarbeitet werden.", e);
			}
			
		}
	}
}
```

JobController - habe hier nur die relevanten Methoden stehen lassen

```
public class JobController {
	
	private JMain view;
	private JobService service;
	private FileIO fileIO;
	private Config config = Config.getInstance();
	private Log4JLogger logger = Log4JLogger.getInstance();
	private Client socket;
	
	public JobController(){
		this.view = new JMain();
    	this.service = JobService.getInstance();
		this.fileIO = new FileIO();
		
    	view.getTable().getModel().setJobList(service.listJobs());
    	view.getTblJobDelete().getModel().updateJobList(service.listJobsToDelete());
    	
		addListener();
		
		socket = new Client(this);
	}
	
	public void addListener(){
		view.getBtnBar().setUploadStartListener(new UploadStartListener());
		view.getBtnBar().setCreateFolderListener(new CreateFolderListener());
		view.getBtnBar().setRefreshListener(new RefreshListener());
		view.getBtnBar().setDeleteListener(new DeleteFolderListener());
	}
		
	private class DeleteFolderListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			ArrayList<Job> selectedJobs = view.getTable().getModel().getSelectedJobs();
			for (Job job : selectedJobs) {			
				socket.sendCommandToServer(getCommandToServer("delete", job));				
			}
//			updateJobList();
		}
	}


	/**
	 * Liest die Jobs neu ein
	 */
	public void updateJobList(){
		System.out.println("JobController.updateJobList()");
		view.getTable().getModel().updateJobList(service.listJobs());
	}

	private JobCommand getCommandToServer(String command, Job job){
		JobCommand commandToServer = new JobCommand();
		commandToServer.setMessage(command);
		commandToServer.setJob(job);
		return commandToServer;
	}
```

JTableModel

```
public class JobTableModel extends AbstractTableModel {
	
	//Columns Number.
    public static final int    JOB_SEL_COL = 0;                                   
    public static final int    JOB_NAME_COL = 1;
    public static final int    JOB_SOURCE_COL = 2;
    public static final int    JOB_FOLDERS_COL = 3;
    public static final int    JOB_DATE_COL = 4;
	
    private String[] columnNames = new String[]{"Upload", "Job Name", "Verzeichnis", "Unterordner", "Datum" };
	private ArrayList<Job> jobList;
	private ArrayList<Job> jobListSeleted;
	
	public JobTableModel(){
		super(); 
		this.jobList = new ArrayList<Job>();
		this.jobListSeleted = new ArrayList<Job>();
		this.addTableModelListener(new TableModelListener() {
			
			@Override
			public void tableChanged(TableModelEvent e) {
				int column = e.getColumn();
				int row = e.getFirstRow();
				//aktuellen Job holen
				Job job = (Job) getValueAt(row);
				if(job.isUpload())
					jobListSeleted.add(job);
				else
					jobListSeleted.remove(job);
			}
		});
	}
	
	
	@Override
	public int getColumnCount() {
		return columnNames.length;
	}

	@Override
	public int getRowCount() {
		return jobList.size();
	}
	
	/**
	 * Aktualisiert die Daten im TableModel
	 * nachdem diese in der Tabelle geändert 
	 * wurden
	 */
	public void setValueAt(Object value, int row, int col){
		
		Job job = jobList.get(row);	
		
		switch(col){
		case JOB_SEL_COL: job.setUpload((Boolean)value); break;
		case JOB_NAME_COL: job.setName((String)value); break;
		case JOB_SOURCE_COL: job.getSourceDirectory(); break;
		case JOB_FOLDERS_COL: value.toString(); break;
		case JOB_DATE_COL: job.setDate((String)value); break;
		}
		fireTableCellUpdated(row, col);
	}

	/**
	 * Gibt den Wert, welcher sich in den 
	 * übergebenen Parametern befindet zurück
	 * 
	 */
	@Override
	public Object getValueAt(int row, int column) {
		Job job = jobList.get(row);
		
		String folders = "";
		if(job.getSourceFolders().length > 0){
			for (File folder : job.getSourceFolders()) {
				if(folders.isEmpty())
					folders += folder.getName();
				else
					folders += "\n" + folder.getName();
			}
		}
		
		switch(column){
		case JOB_SEL_COL: return job.isUpload();
		case JOB_NAME_COL: return job.getName();
		case JOB_SOURCE_COL: return job.getSourceDirectory();
		case JOB_FOLDERS_COL: return folders;
		case JOB_DATE_COL: return job.getDate();
		default: return new Object();
		}
	}
	
	/**
	 * Gibt das Object zurück, was sich
	 * in der übergebenen Zeile befindet
	 * zurück
	 * 
	 * @param row
	 * @return
	 */
	public Object getValueAt(int row){
		return jobList.get(row);
	}
	
	public String getColumnName(int iColumn){
		return columnNames[iColumn];
	}
	
	// passendes Format in der Spalte wird angezeigt
	public Class getColumnClass(int columnIndex){
		switch(columnIndex){
			case 0: 	return Boolean.class;
			default: 	return String.class;
		}
	}
	
	public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
        return col == 0;
    }
	
	public ArrayList<Job> getSelectedJobs() {
		System.out.println("JobTableModel.getSelectedJobs() " + jobListSeleted.size());
		return jobListSeleted;
	}

	public void setSelectedJobs(ArrayList<Job> arrlSelectedJobs) {
		this.jobListSeleted = arrlSelectedJobs;
	}
	
	public ArrayList<Job> getJobList() {
		return jobList;
	}

	public void setJobList(ArrayList<Job> jobList){
		this.jobList = jobList;
	}

	public void updateJobList(ArrayList<Job> jobList) {
		this.jobList.clear();
		this.jobListSeleted.clear();
		if(!jobList.isEmpty()){
			this.jobList = jobList;
			this.fireTableDataChanged();
		} else if(jobList.size() > 0){
			this.fireTableDataChanged();
		}
	}
	
}
```

JTable

```
public class JobTable extends JTable {
	
	private JobTableModel model;
	private Config config = Config.getInstance();
	private FileIO fileIO = new FileIO();
	
	
	public JobTable(){
		init();
	}

	private void init() {
		model = new JobTableModel();			

		this.setModel(model);
		this.setAutoCreateRowSorter(true);
		this.setSurrendersFocusOnKeystroke(true);
		this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);	
			
		// Setzen der passenden Ausrichtung innerhalb der Zelle
		this.setDefaultRenderer(Object.class, new DefaultTableCellRenderer()
		{
			public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
			{
				/* Sollten die Spalten durch den User neu sortiert sein,
				 * dann haben die spalten einen neuen Index. Man benötigt den Modelindex
				 */
				final int modelColumn = table.convertColumnIndexToModel(column);
				switch (modelColumn)
				{
						/* alignment left setzen */
					default :
						setVerticalAlignment(JLabel.TOP);
				}
				return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
			}
		});
		
		// Tabellen Header Einstellungen - aktuellen holen, von der JTable um damit abreiten zu können
		JTableHeader tblHeader = this.getTableHeader();
		tblHeader.setResizingAllowed(false);
		tblHeader.setReorderingAllowed(false);
				
		// Spaltenbreite setzen
		this.getColumnModel().getColumn(0).setMaxWidth(43);
		this.getColumnModel().getColumn(1).setMinWidth(120);
		this.getColumnModel().getColumn(2).setMinWidth(380);
		this.getColumnModel().getColumn(3).setMinWidth(100);
		this.getColumnModel().getColumn(4).setMinWidth(20);
		
		
		TableColumn column = this.getColumnModel().getColumn(3);
		column.setCellRenderer(new TwoLinesCellRenderer());
			
	}
	

	
	public JobTableModel getModel(){
		return model;
	}
	

	/**
	 * Renderer für eine mehrzeilige Zellen.
	 * 
	 * @author leifken
	 *
	 */
	private class TwoLinesCellRenderer extends JTextArea implements TableCellRenderer {

		@Override
		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			
			if (isSelected){		    	
	      		setForeground(table.getSelectionForeground());
	      		setBackground(table.getSelectionBackground());
		    }	 
	    	else{
	      		setForeground(table.getForeground());
	      		setBackground(table.getBackground());
	    	} 
			
			setFont(table.getFont());
			setText(value.toString());
			
			// Passt die Zellenhöhe nur an, wenn die aktuelle ungleich der TA ist und die Tabelle Daten hat
			if (table.getRowHeight() != (int) this.getMinimumSize().height && table.getModel().getRowCount() > 0){ 
				table.setRowHeight(row, (int) this.getMinimumSize().height);
			}
			
			return this;
		}
	}
}
```

Wenn noch andere Dinge am Code auffallen, ich bin für jeden Ratschlag dankbar...

Hoffe ich kann das Problem mit eurer Hilfe nun endlich beenden.. :rtfm:

Viele Grüße


----------



## fastjack (6. Jan 2012)

Du hast überall "Unknown source", führ das mal mit einem JDK aus und kompilere mit (debug=on). Gibt es keine Cause-Exception, oder mehr Exceptions? Debug mal die Methoden getValueAt und checke die row-Angabe. Dadurch das das Model eine getJobList-Methode hat, wird da vielleicht etwas nebenläufig angefügt oder entfernt? Das bekommt dann dein Model nämlich nicht mit.


```
this.jobList.clear();
        this.jobListSeleted.clear();
        if(!jobList.isEmpty()){
            this.jobList = jobList;
            this.fireTableDataChanged();
        } else if(jobList.size() > 0){
            this.fireTableDataChanged();
        }
```

würde ich lieber so machen:


```
this.jobList.clear();
        this.jobListSeleted.clear();
        if(!jobList.isEmpty()){
            this.jobList.addAll(jobList);
        }
        this.fireTableDataChanged();
```

this.jobList und jobList in einer Methode kann unübersichtlich werden, nenn den Parameter lieber newJobList oder so...


----------



## Ollek (13. Jan 2012)

Das hat leider nicht geholfen..

Deine Codeverbesserungen hab ich umgesetzt..

Sonst noch jemand eventuell eine Idee? ???:L


----------



## SlaterB (13. Jan 2012)

updateJobList() wird von einem zur GUI parallelen Thread aufgerufen,
während die GUI potentiell irgendwas macht, z.B. erst eine Anzahl Zeilen abfragen und dann die Zeilen durchläuft,
werden die GUI-Daten geändert -> Exception

GUI-Daten dürfen nur vom AWT-Thread geändert werden, erreichbar mit Ablegen dieser Kommandos in 
SwingUtilies.invokeLater(Runnable)

die gesamte Berechnung mit Server-Anfrage besser nicht, denn solange ist die GUI blockiert,
nur wenn die Ergebnisse fertig formatiert und in neuen Listen abgefüllt sind oder was immer nötig,
dann den hoffentlich kurzen Moment der Verlinkung im TableModel durch invokeLater() ausführen lassen


Threading with Swing: SwingUtilities.invokeLater


----------



## Ollek (13. Jan 2012)

Danke.

Also wenn ich das ganze richtig verstanden habe, soll ich bei jeder Aktion, die durch einen Button ausgelöst wird und die Anzeige in irgendeiner Art verändert, diese mit SwingUtilities.invokeLater ( new Runnable(){} ausführen?

Meine Awendung wird ebenfalls schon dadurch gestartet.


----------



## SlaterB (13. Jan 2012)

Buttons werden standardmäßig eh vom GUI-Thread ausgeführt, da ist das im Grundmodus nicht nötig,
wenn von Button aus lange Aktionen ausgeführt werden, blockiert die GUI solange,
deswegen parallele Threads, 
in deinem Fall scheint es um den generell laufenden Client-Thread zu gehen,

für parallele Threads wird invokeLater dann interessant, 
und wie schon gesagt: wenn invokeLater, dann möglichst kurze Abschnitte, nicht die Server-Anfrage

die main-Methode ist übrigens auch so ein paralleler Thread, deswegen ist das schon ganz schlau,
aber dort am Anfang geht es glaube ich nicht primär um Datenänderungen


----------



## Ollek (13. Jan 2012)

Die Serveranfrage läuft schon in einem eigenen Thread.

Aber ich rufe in der Aktion vom Button eine Methode des Clients auf bzw. übergebe den zu löschenden Job. 

Daran kann es aber nicht liegen, oder?


----------



## SlaterB (13. Jan 2012)

meines Wissens gehts um updateJobList(), welches ich in Client.run() sehe,
meinst du DeleteFolderListener? dort ist es doch auskommentiert,

durch Logging kannst du herausfinden welches updateJobList() Probleme macht, falls du nicht schon den Durchblick hast


> Die Serveranfrage läuft schon in einem eigenen Thread.
+
> Aber ich rufe in der Aktion vom Button eine Methode des Clients auf bzw. übergebe den zu löschenden Job.

ist mir zu ungenau, da schaue ich jetzt nicht nach, welche Codestellen meinst du denn genau, welche Abläufe gibt es?
wenn Listener X einen Thread Y Bescheid sagt und Y dann was macht, ist das genau so zu behandeln als ob Thread Y was macht, denn es ist ja Thread Y der was macht 
wer wann vor Urzeiten einen Befehl dazu gegeben hat, scheint aus meiner Sicht wenig relevant, falls das deine Frage ist


----------



## bERt0r (13. Jan 2012)

Ich würde mal bei setJoblist ein fireTableDataChange machen.


----------



## Ollek (13. Jan 2012)

Wenn ich da ein fireTableDataChange setze und die ArrayList ist leer, wird ebenfalls eine Exception geschmissen.

@SlaterB

Also in dem Client wird über den Controller nur die Methode zum Updaten der Liste aufgerufen.
Das ist doch richtig, oder sehe ich das falsch?
Der Client hat keine eigene Methode zum Updaten.


----------



## SlaterB (13. Jan 2012)

> Das ist doch richtig, oder sehe ich das falsch?
wie kannst du bei einer trivialen Aussage sowas nachfragen, natürlich ist es richtig dass 'über den Controller die Methode zum Updaten der Liste aufgerufen wird'
interessanter sind die Wörter 'nur' + 'doch', was implizierst du damit, worin siehst du einen Widerspruch, schreibe doch deine Gedanken lang und ausführlich hin

zusammen mit
> Der Client hat keine eigene Methode zum Updaten. 
ist die einzige Spekulation, die mir einfällt, die, dass es deiner Meinung nach eine Rolle spielen würde, wo der Code steht,
'der Thread ruft die Methode nur auf, der Methodencode steht aber im TableModel bzw. der Controller ist auch noch dazwischen, daher ...'
wie die ... weitergehen sollten weiß ich aber wiederum nicht,

jeder Code, egal wo er steht, wird von einem Thread ausgeführt, 
meine Vermutung ist, dass updateJobList() von einem Thread deines Programms ausgeführt wird,
damit parallel/ nebenläufig/ gleichzeitig zum AWT-Thread läuft, der auf die Daten für irgendwelche Zwecke zugreift und nicht mit willkürlichen Änderungen zwischendurch (etwa zwischen Anfang und Ende eines paint-Vorgangs) rechnet,
ergo Exception halbwegs verständlich


----------



## bERt0r (13. Jan 2012)

> ```
> this.addTableModelListener(new TableModelListener() {
> 
> @Override
> ...


Du kannst in einem TableModelListener nicht einfach direkt auf die ModelDaten zugreifen und da Werte löschen. Nach jeder änderung am Model muss eine passende fire Methode ausgeführt werden um die Listener zu benachrichtigen. Listener sind nicht nur dein selbstgebauter, sonder auch der JTable und sein Rowsorter, der dir deine Exception wirft.
Wenn du von deinem Listener aus etwas löschen möchtest, musst du eine Methode wie removeRow benutzen.


----------



## SlaterB (13. Jan 2012)

hab nicht alle Verwendungen nachgeschaut, aber direkt im Model scheint ja jobList die Hauptdatenmenge zu sein, die wird nicht verändert,
jobListSeleted ist was anderes


----------



## bERt0r (14. Jan 2012)

Stimmt, aber das verstößt einfach gegen das Model Prinzip.
Die Direktzuweisung der neuen Arralist ist auch nicht gut:

```
public void updateJobList(ArrayList<Job> jobList) {
        this.jobList.clear();
        this.jobListSeleted.clear();
        if(!jobList.isEmpty()){
            this.jobList = jobList;
            this.fireTableDataChanged();
        } else if(jobList.size() > 0){
            this.fireTableDataChanged();
        }
    }
```
Was soll das ganze überhaupt. Wieso clear(), wieso Abfrage auf isEmpty()? Und wenn dein service, den du nicht gepostet hast, von dem die joblist jetzt ein clear auf seine ArrayList macht, betrifft das auch die vom Table und die Exception fliegt. Folgendes sollte doch ausreichen:


```
public void updateJobList(ArrayList<Job> jobList) {
        this.jobListSeleted.clear();
       this.joblist=joblist.clone();
this.fireTableDataChanged();}
```


----------

