# Problem mit der verwirklichung einer Readmethode in einer Chatapplikation



## Shams (18. Nov 2014)

Ich habe ein Problem etwas komplizierterer Natur.

Ich schreibe gerade an einer Chatapplikation, wobei ich vorhabe, die Chat-UI dem User als Vaadin-Servlet zur Verfügung zu stellen.

In dem Moment, da die enter-Methode durchlaufen wird (die muss in jedem VaadinServlet implementiert sein), passiert folgendes:


```
public void enter(ViewChangeEvent event) {
	
//dispatch
write("1"+":"+(String)UI.getCurrent().getSession().getAttribute("currentUser")+"\n");
		
//reads everything incoming from the dipatcher
//HOW?????? -> Das ist mein Problem, siehe Erklärung hier im Threadpost.
 
}
```


Es wird eine write-Methode aufgerufen, die durch einen Outputstream an einen Serversocket eine Mitteilung schreibt, eine „1+currentUser“. Eins bedeutet dabei, dass der User online ist. Hier die Serverklasse, wie diese funktioniert, bitte den Kommentaren entnehmen.


```
package dispatching;
import java.net.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.io.*;

import com.vaadin.ui.Notification;

public class MainServer extends Thread
{
   private ServerSocket serverSocket;
   private HashMap<String,Socket> dispatch = new HashMap<String,Socket>(); 
   private BufferedReader br = null; 
   private BufferedWriter bw = null;
   
   public MainServer(int port) throws IOException
   {
      serverSocket = new ServerSocket(port);
      //serverSocket.setSoTimeout(10000);//Kann am Ende weg.
   }
 
   public void run()
   {
	  	   
      while(true)
      {
         try
         {
            
            Socket server = serverSocket.accept();//Bis hier is klar, ne?
            
           try{
           	   
              br = new BufferedReader(new InputStreamReader(new DataInputStream(server.getInputStream())));

             String line = br.readLine();
             
	  

if(line.split(":")[0].equals("1")){//Also wenn der Buddy on == 1 ist...

	
	if(line.split(":").length==3){//Also wenn Botschfaten geschrieben warden, dann ist line.split(":")[1] immer der Adressat (addressee) und line.split(":")[2] ist immer die Message.  
		 
		dodispatch(line.split(":")[1],line.split(":")[2]);		
	
	}
	
}
           
}catch(IOException e){e.printStackTrace();}
      
          //server.close(); // ???
         }catch(SocketTimeoutException s)
         {
            System.out.println("Socket timed out!");
            break;
         }catch(IOException e)
         {
            e.printStackTrace();
            break;
         }      
      }
      
   }
  
   private void dodispatch(String addressee, String message){

	   //Hier will ich eine HashMap aus Benutzern, die Online sind jeweils mit deren akzeptierten Sockets als Values durchlaufen, um die an den jeweiligen Adressaten adressierte Nachricht dem richtigen Mann / Frau zuzustellen, aber erstmal habe ich es so geschrieben, dass alles an den Testuser zurückgeht, wie ein Echo, halt auch über nen Stream, sieht so zwar schlecht aus, dürfte aber klar sein, was gemeint ist.  

	   for(String str : dispatch.keySet()){
		 
	      //if(str.equals(addressee)){ 
	      /*Vorerst:*/if(str.equals("testuser")){	 
	    	  try{
	    		
	    	  /*Vorerst:*/bw = new BufferedWriter(new OutputStreamWriter(new DataOutputStream(dispatch.get("testuser").getOutputStream())));//This does not go to testuser3, but is echoed back t the testuser.    		  
	    	  //bw = new BufferedWriter(new OutputStreamWriter(new DataOutputStream(dispatch.get(addressee).getOutputStream()))); 
	    	  bw.write(message+"\n");//If the message contained an \n, it would a spurious deed to add one more, but it doesn't.
	    	  bw.flush();
	    	  
	    	  }catch(IOException e){ e.printStackTrace(); }
	      }
		   
	   }
	}



//Das ist die Main des Servers.
public static void main(String [] args)
   {
    
      try
      {
         Thread t = new MainServer(8754);
         t.start();
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}
```


Sobald sich der User angemeldet habe, habe ich im Clienten eine eventbasierte Art und Weise entwickelt, wie ich dann aus dem Chatfenster Botschaften abgreife, und wenn der User halt ne Nachricht senden will, also auf senden drückt, wird halt wieder die write-Methode aufgerufen. 

--> Jetzt habe ich ein riesen Problem: Ich kann im Clientlistener keinen InputstreamReader in einer while(true) Schleife laufen lassen, weil sonst alles blkiert - was soll ich tun? Ich kann auch gerne den gesamten Quellcode posten, wenn das so erwünscht ist.


----------



## Shams (19. Nov 2014)

Update:


Ich habe die ganze Sache nun versucht, einmal vermittels der Anwendung des 


```
com.google.gwt.thirdparty.guava.common.eventbus.EventBus
```


…die Readermethode des Clientreaders aufzurufen. Bevor ich das aber tue, wollte ich einmal ausprobieren, ob das überhaupt geht.

Ich habe also meine UI-Klasse, und in dieser habe ich eine Subscribermethode, welche den Inputstream des Clientsockets liest, und mir per Notification anzeigt, dass sie das tut.



```
@Subscribe
@AllowConcurrentEvents
private void readIncomingMessages(EventBroadcastEvent e){
	Notification.show("Ein Event kommt geflogen.");
BufferedReader bufferedReader = null;
try{
	if(client != null){
bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));	  
	char[] buffer = new char[200];
	  new String(buffer,0,bufferedReader.read(buffer, 0, 200));
	}
   }catch(IOException eOfBroadCast){ eOfBroadCast.printStackTrace(); }
}
```


Der client-Socket ist der UI-Klasse eigen. Den EventBus lasse ich mir aus einem Singleton ausgeben (siehe Code weiter unten). Beim Betreten des VaadinUIs wird der Eventbus registriert.


```
@Override
public void enter(ViewChangeEvent event) {
	
 . . .  


    EventBusSingleton.getInstance().register(this);

		 
 . . . 

}
```

Und wie werfe ich derzeit das Event wie folgt:



Dies geschieht wie folgt (in meiner Serverklasse):



```
EventBusSingleton.getInstance().post(new EventBroadcastEvent());
```



Hier zu guter Letzt das Singleton:


```
import com.google.gwt.thirdparty.guava.common.eventbus.EventBus;

public class EventBusSingleton {
	
	
	private static EventBus eventBus = null;
	 
    /**
     * Default-Konstruktor, der nicht außerhalb dieser Klasse
     * aufgerufen werden kann
     */
    private EventBusSingleton() {}
 
    /**
     * Statische Methode, liefert die einzige Instanz dieser
     * Klasse zurück
     */
    public static EventBus getInstance() {
        if (eventBus == null) {
        	eventBus = new EventBus();
        }
        return eventBus;
    }
	
	
}
```


----------



## Joose (19. Nov 2014)

[ot]
Bitte den Java Code in 
	
	
	
	





```
-Tags packen, danke! :)
[/ot]
```


----------



## Shams (21. Nov 2014)

… danke für den Hinweis.


Um die Sache mal ein bisschen greifbarer zu machen, habe ich aus meinem Ursprünglichen Code das Problem herausgezogen und eine Minianwendung geschrieben, die mein derzeitiges Problem simuliert. Es ist wohlgemerkt auch eine Vaadingeschichte.

Meine Minianwendung besteht aus den drei Packages com.example.vaadin7scratch, dispatching sowie sockets.

Die Idee ist diejenige, dass es einen Sever gibt, der just in dem Moment, da er eine Nachricht bekommt, über einen Eventbus ein Event fetzen lässt, welches dann durch eine Subscribermethode aufgefangen werden soll, und dieses Event soll dann  in einer Notification angezeigt werden.


Kern der Minianwendung, von wo aus auch die Messages geschrieben werden, ist die GUI in com.example.vaadin7scratch:


```
package com.example.vaadin7scratch;

import java.io.*;
import java.net.*;

import javax.servlet.annotation.WebServlet;

import com.google.gwt.thirdparty.guava.common.eventbus.AllowConcurrentEvents;
import com.google.gwt.thirdparty.guava.common.eventbus.Subscribe;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

import dispatching.EventBroadcastEvent;
import dispatching.EventBusSingleton;

@SuppressWarnings("serial")
@Theme("vaadin7scratch")
public class Vaadin7scratchUI extends UI {
	
	Socket client = null;
	DataOutputStream out = null;
	BufferedWriter bw = null;

	@WebServlet(value = "/*", asyncSupported = true)
	@VaadinServletConfiguration(productionMode = false, ui = Vaadin7scratchUI.class)
	public static class Servlet extends VaadinServlet {
	}
	
	
	@Subscribe
	@AllowConcurrentEvents
	private void readIncomingMessages(EventBroadcastEvent e){
		Notification.show("Nachricht von "+e.getName());//Erst soller mir mal das ausgeben, sonst werde ich nichtfroh :\ getName() soll mir die neues Messages ausgeben.
	
//		BufferedReader bufferedReader = null;
//	try{
//		if(client != null){
//	bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));	  
//		char[] buffer = new char[200];
//		  new String(buffer,0,bufferedReader.read(buffer, 0, 200)); //Das ist die Nachricht, die muessen wir noch mit einem Singnal ins richtige Chatfenster oder nach unreadMessages bugsieren.
//		}                                                           //Und wenn es nicht moeglich ist, dies in ein Textfenster hineinzusetzen, dann muss an der richtigen Stelle ein Unread-Message-Singal ausgegeben werden.
//	   }catch(IOException eOfBroadCast){ eOfBroadCast.printStackTrace(); }

	}	
	

	@Override
	protected void init(VaadinRequest request) {
		final VerticalLayout layout = new VerticalLayout();
		layout.setMargin(true);
		setContent(layout);

		 EventBusSingleton.getInstance().register(this);
		 write("This is message is written as soon as the user beholds the newly created UI."+"\n");
		
		
		Button button = new Button("Click Me");
		button.addClickListener(new Button.ClickListener() {
			public void buttonClick(ClickEvent event) {
				
				write("New Message");
				layout.addComponent(new Label("Thank you for clicking and Your new message"));
			
			}
		});
		layout.addComponent(button);
	}

	private void write(String towrite){

		//Sobald der User chatfaehig wird (durch Betreten dieser View), wird automatisch ein Stream geoeffnet.
		try {
			client = new Socket("127.0.0.1", 8754);
			out = new DataOutputStream(client.getOutputStream());
			bw = new BufferedWriter(new OutputStreamWriter(out));	
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	
		
		try {

		bw.write(towrite);
		bw.flush();
		//bw.close();

		}catch (IOException e) {

		e.printStackTrace();
		
		   }
		}

}
```

Dann gibt es in dem package sockets noch den server:


```
package sockets;

import java.net.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.io.*;

import com.google.gwt.thirdparty.guava.common.eventbus.EventBus;
import com.vaadin.ui.Notification;

import dispatching.EventBroadcastEvent;
import dispatching.EventBusSingleton;

public class Server extends Thread
{
   private ServerSocket serverSocket;
   private HashMap<String,Socket> dispatch = new HashMap<String,Socket>(); 
   private BufferedReader br = null; 
   private BufferedWriter bw = null;
   
   public Server(int port) throws IOException
   {
      serverSocket = new ServerSocket(port);
      //serverSocket.setSoTimeout(10000);//Kann am Ende weg.
   }
 
   public void run()
   {
	  	   
      while(true)
      {
         try
         {
            
            Socket server = serverSocket.accept();
            
           try{
           	   
              br = new BufferedReader(new InputStreamReader(new DataInputStream(server.getInputStream())));

             String message = br.readLine();//Hier mit dieser Methode ist etwas nicht in Ordnung.
             


EventBusSingleton.getInstance().post(new EventBroadcastEvent(message));


}catch(IOException e){e.printStackTrace();}
      
      
         }catch(SocketTimeoutException s)
         {
            System.out.println("Socket timed out!");
            break;
         }catch(IOException e)
         {
            e.printStackTrace();
            break;
         }      
      }
      
   }


public static void main(String [] args)
   {
    
      try
      {
         Thread t = new Server(8754);
         t.start();
      }catch(IOException e)
      {
         e.printStackTrace();
      }
   }
}
```

Zu guter Letzt habe ich noch in dem package dispatching alles, was etwas mit Events zu tn hat, d.h. eine Eventklasse, deren Konstruktor ich überladen habe und eben meinen EventBus, den ich immer als Singleton ausgebe – hier die beiden:



```
package dispatching;

import com.google.web.bindery.event.shared.Event;

public class EventBroadcastEvent extends Event{

	String name;
	
	public String getName() {
		return name;
	}


	public void setName(String name) {
		this.name = name;
	}


	public EventBroadcastEvent(String nam){
		super();
		name=nam;
	}
	
	
	@Override
	public Type getAssociatedType() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	protected void dispatch(Object handler) {
		// TODO Auto-generated method stub
		
	}
	
}
```


```
package dispatching;

import com.google.gwt.thirdparty.guava.common.eventbus.EventBus;

public class EventBusSingleton {
	
	
	private static EventBus eventBus = null;
	 
    /**
     * Default-Konstruktor, der nicht außerhalb dieser Klasse
     * aufgerufen werden kann
     */
    private EventBusSingleton() {}
 
    /**
     * Statische Methode, liefert die einzige Instanz dieser
     * Klasse zurück
     */
    public static EventBus getInstance() {
        if (eventBus == null) {
        	eventBus = new EventBus();
        }
        return eventBus;
    }
	
	
}
```

Meine konkrete Frage ist eben, weshalb das nicht geht, er müsste das Event doch eigentlich einfangen, oder bin ich da ganz auf dem falschen Weg?


----------

