diff --git a/.gitignore b/.gitignore
index c394076d75a37b52fdbcd46defe55ebacfc03889..7296ae5dcc5c81743786e9ba0c69368f365a9944 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ target/
 *.project
 **.vscode/
 *.classpath
-**.settings
\ No newline at end of file
+**.settings
+*.iml
\ No newline at end of file
diff --git a/README.md b/README.md
index 846c531f866189e728ea7b4f8e8eb99b5b383b84..832002f8e21f2b596f7f46e25815a2c9000fb1de 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Travail Pratique 4
 Un travail de Alexis Durgnat, Michael El Kharroubi, Quentin Leblanc et Théo Pirkl
-
+ 
 
 ## Description
 Ce travail pratique consiste à proposer une application de réseau social permettant de savoir qui prend
@@ -29,4 +29,4 @@ Schéma des branches (exemple) :
 
 Lors du merge :
 * gui —> devel et opendata-transport —> devel
-* devel -> master (une fois que ca fonctionne)
\ No newline at end of file
+* devel -> master (une fois que ca fonctionne)
diff --git a/pom.xml b/pom.xml
index de23717827f7ac084aef6c7fb37546708ec6ae17..effeb7eab5cb4cfb0b6381300c46c1f536a25898 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,13 @@
         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     </properties>
     <dependencies>
+        <!-- JUnit -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>5.2.0</version>
+            <scope>test</scope>
+        </dependency>
         <!-- Java FX -->
         <dependency>
             <groupId>org.openjfx</groupId>
@@ -76,6 +83,16 @@
                     <mainClass>ch.hepia.Main</mainClass>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.22.0</version>
+                <configuration>
+                    <excludes>
+                    <exclude>some test to exclude</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
\ No newline at end of file
diff --git a/src/main/java/ch/hepia/Main.java b/src/main/java/ch/hepia/Main.java
index fc487a61550ca4ec5aadb13e965eaae23526d72f..7c7c5860d54548641d1615dd0ad2cc517578ed5d 100644
--- a/src/main/java/ch/hepia/Main.java
+++ b/src/main/java/ch/hepia/Main.java
@@ -1,13 +1,21 @@
 package ch.hepia;
 
 import ch.hepia.config.AppConfig;
+import ch.hepia.config.AppContext;
+import ch.hepia.events.*;
+import ch.hepia.models.User;
+import ch.hepia.mq.MessageManager;
 import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.event.EventHandler;
 import javafx.fxml.FXMLLoader;
 import javafx.scene.Parent;
 import javafx.scene.Scene;
 import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
 
 /**
@@ -15,11 +23,18 @@ import java.io.IOException;
  */
 public class Main extends Application {
 
+	private static AppContext appContext;
+
 	/**
 	 * Mein starter
 	 * @param args The passed arguments
 	 */
-	public static void main(String[] args) {
+	public static void main(String[] args) throws Exception {
+		MessageManager m = new MessageManager(
+				AppConfig.RABBITMQ_HOSTNAME, AppConfig.RABBITMQ_USERNAME, AppConfig.RABBITMQ_PASSWORD,
+				AppConfig.RABBITMQ_EXCHANGE
+		);
+		appContext = new AppContext(m);
 		launch(args);
 	}
 
@@ -29,6 +44,27 @@ public class Main extends Application {
 		Scene scene = new Scene(root, AppConfig.APP_WIDTH, AppConfig.APP_HEIGHT);
 
 		stage.setScene(scene);
+		stage.setResizable(false);
 		stage.show();
+		stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
+			@Override
+			public void handle(WindowEvent t) {
+				Platform.exit();
+				try {
+					appContext.getMessageManager().close();
+				} catch (Exception e){
+					System.exit(1);
+				}
+				System.exit(0);
+			}
+		});
+	}
+
+	/**
+	 * Gets the app context of the app
+	 * @return the app context of the app
+	 */
+	public static AppContext getContext(){
+		return appContext;
 	}
 }
diff --git a/src/main/java/ch/hepia/api/transport/Connection.java b/src/main/java/ch/hepia/api/transport/Connection.java
index 2c0f21247b52f196e33312431198a537adc2fc9b..5b4c9a798efce7f40cd12239a8bcfd91975b95fe 100644
--- a/src/main/java/ch/hepia/api/transport/Connection.java
+++ b/src/main/java/ch/hepia/api/transport/Connection.java
@@ -9,9 +9,9 @@ import java.util.List;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
+import java.io.Serializable;
 
-
-public final class Connection {
+public class Connection implements Serializable {
     private Stop from;
     private Stop to;
     private Time duration;
@@ -21,6 +21,26 @@ public final class Connection {
     private int capacity2nd;
     private List<Section> sections;
 
+    /**
+     * Empty Connection
+     */
+    private final static class EmptyConnection extends Connection{
+        /**
+         * Constructor of EmptyConnection
+         * All values are default ones
+         * An Empty Connection go from Geneva to Geneva
+         * @throws ParseException
+         */
+        private EmptyConnection() throws ParseException {
+            super(new Stop.StopBuilder(new JSONObject()).build(),
+                    new Stop.StopBuilder(new JSONObject()).build(),
+                    new Time(new SimpleDateFormat("dd:kk:mm:ss").parse("00:00:00:00").getTime()),
+                    new Service.ServiceBuilder(new JSONObject()).build(),
+                    new ArrayList<>(),
+                    0,0, new ArrayList<>());
+        }
+    }
+
     /**
      * Constructor of Connection Object
      * @param from stop object
@@ -51,10 +71,11 @@ public final class Connection {
         return new ArrayList<>(sections);
     }
 
+
     /**
      * Builder of Connection object
      */
-    public static class ConnectionBuilder{
+    public final static class ConnectionBuilder{
         private JSONObject datas;
 
         /**
@@ -72,27 +93,32 @@ public final class Connection {
          * @throws ParseException
          */
         public Connection build() throws ParseException {
-            JSONObject fromJSON = new JSONObject(datas.get("from"));
+            //if the datas retrieved contain nothing, we have to prevent errors
+            if(datas.isEmpty()){
+                return new EmptyConnection();
+            }
+
+            JSONObject fromJSON = datas.isNull("from") ? new JSONObject() : datas.getJSONObject("from");
             Stop.StopBuilder from = new Stop.StopBuilder(fromJSON);
 
-            JSONObject toJSON = new JSONObject(datas.get("to"));
+            JSONObject toJSON = datas.isNull("to") ? new JSONObject() : datas.getJSONObject("to");
             Stop.StopBuilder to = new Stop.StopBuilder(toJSON);
 
             DateFormat formatter = new SimpleDateFormat("dd:kk:mm:ss");
-            String date = datas.getString("duration").replace('d',':');
+            String date = datas.isNull("duration") ? "00:00:00:00" : datas.getString("duration").replace('d',':');
             Time duration = new Time(formatter.parse(date).getTime());
 
-            JSONObject serviceJSON = new JSONObject(datas.get("service"));
+            JSONObject serviceJSON = datas.isNull("service") ? new JSONObject() : new JSONObject(datas.get("service"));
             Service.ServiceBuilder service = new Service.ServiceBuilder(serviceJSON);
 
-            JSONArray productsJSON = datas.getJSONArray("products");
+            JSONArray productsJSON = datas.isNull("from") ? new JSONArray() : datas.getJSONArray("products");
             List<String> products = new ArrayList<>();
             productsJSON.forEach(k -> products.add(k.toString()));
 
             int capacity1st = datas.isNull("capacity1st") ? 0 : datas.getInt("capacity1st");
             int capacity2nd = datas.isNull("capacity2nd") ? 0 : datas.getInt("capacity2nd");
 
-            JSONArray sectionsJSON = datas.getJSONArray("sections");
+            JSONArray sectionsJSON = datas.isNull("from") ? new JSONArray() : datas.getJSONArray("sections");
             List<Section> sections = new ArrayList<>();
             sectionsJSON.forEach(o -> {
                 JSONObject k = (JSONObject) o;
diff --git a/src/main/java/ch/hepia/api/transport/Coordinates.java b/src/main/java/ch/hepia/api/transport/Coordinates.java
index 543f90b7e14c2899553dd42afd828b7da9e000fd..0041489daaf6841d8c8f10d1e1db2b51cfbe825a 100644
--- a/src/main/java/ch/hepia/api/transport/Coordinates.java
+++ b/src/main/java/ch/hepia/api/transport/Coordinates.java
@@ -1,12 +1,26 @@
 package ch.hepia.api.transport;
 
 import org.json.JSONObject;
+import java.io.Serializable;
 
-public final class Coordinates {
-    private Type type;
+public class Coordinates implements Serializable {
+    private String type;
     private double x;
     private double y;
 
+    /**
+     * Default coordinates containing default values (Geneva is the default value)
+     * WGS84 is the default type value
+     */
+    private final static class DefaultCoordinates extends Coordinates{
+        /**
+         * Constructor of DefaultCoordinates, located in Geneva
+         */
+        private DefaultCoordinates(){
+            super("WGS84", 46.210237, 6.142422);
+        }
+    }
+
     /**
      * Constructor of the class Coordinates
      * A coordinates is represented by a type of place and a point (x, y)
@@ -14,7 +28,7 @@ public final class Coordinates {
      * @param x coordinate in x
      * @param y coordinate in y
      */
-    private Coordinates(Type type, double x, double y){
+    private Coordinates(String type, double x, double y){
         this.type = type;
         this.x = x;
         this.y = y;
@@ -23,7 +37,7 @@ public final class Coordinates {
     @Override
     public String toString(){
         StringBuilder string = new StringBuilder();
-        return string.append(type.toString()).append("au point (").append(x).append(", ").append(y).append(")").toString();
+        return string.append("Coordonnées du type ").append(type).append(" au point (").append(x).append(", ").append(y).append(")").toString();
     }
 
     /**
@@ -45,7 +59,7 @@ public final class Coordinates {
     /**
      * Builder of Coordinates object
      */
-    public static class CoordinatesBuilder{
+    public final static class CoordinatesBuilder{
         private JSONObject datas;
         private String type;
 
@@ -64,9 +78,13 @@ public final class Coordinates {
          * @return a new Coordinates object
          */
         public Coordinates build(){
-            Type t = Type.valueOf(type.toUpperCase());
-            double x = datas.getDouble("x");
-            double y = datas.getDouble("y");
+            if(datas.isEmpty()){
+                return new DefaultCoordinates();
+            }
+
+            String type = datas.getString("type");
+            double x = datas.isNull("x") ? 0 : datas.getDouble("x");
+            double y = datas.isNull("y") ? 0 : datas.getDouble("y");
 
             return new Coordinates(t, x, y);
         }
diff --git a/src/main/java/ch/hepia/api/transport/Journey.java b/src/main/java/ch/hepia/api/transport/Journey.java
index d2362fdeec26d37d1834ca992c757382072f491d..1478529f78f60fe393442984c3e31e3a8b5bc0a6 100644
--- a/src/main/java/ch/hepia/api/transport/Journey.java
+++ b/src/main/java/ch/hepia/api/transport/Journey.java
@@ -6,18 +6,34 @@ import org.json.JSONObject;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.List;
+import java.io.Serializable;
 
-public final class Journey {
+public class Journey implements Serializable {
     private String name;
     private String category;
     private int categoryCode;
-    private int number;
+    private String number;
     private String operator;
     private String to;
     private List<Stop> passList;
     private int capacity1st;
     private int capacity2nd;
 
+    /**
+     * Empty Journey
+     */
+    public final static class EmptyJourney extends Journey{
+        /**
+         * Constructor of EmptyJourney
+         * No Information and no Stops in the Journey
+         * @throws ParseException
+         */
+        private EmptyJourney() throws ParseException {
+            super("", "", 0, "", "", "",
+                    new ArrayList<>(), 0, 0);
+        }
+    }
+
     /**
      * Constructor of the Journey Object
      * @param name
@@ -30,7 +46,7 @@ public final class Journey {
      * @param capacity1st number of seats inst class
      * @param capacity2nd number of seats in nd class
      */
-    private Journey(String name, String category, int categoryCode, int number, String operator, String to, List<Stop> passList, int capacity1st, int capacity2nd){
+    private Journey(String name, String category, int categoryCode, String number, String operator, String to, List<Stop> passList, int capacity1st, int capacity2nd){
         this.name = name;
         this.category = category;
         this.categoryCode = categoryCode;
@@ -73,6 +89,13 @@ public final class Journey {
     public String getTo(){
         return to;
     }
+    /**
+     * Get the transport number
+     * @return number
+     */
+    public String getNumber(){
+        return number;
+    }
 
     /**
      * Get all the Stops where the journey will pass through
@@ -85,7 +108,7 @@ public final class Journey {
     /**
      * Builder of Journey object
      */
-    public static class JourneyBuilder{
+    public final static class JourneyBuilder{
         private JSONObject datas;
 
         /**
@@ -103,15 +126,19 @@ public final class Journey {
          * @throws ParseException
          */
         public Journey build() throws ParseException {
-            String name = datas.getString("name");
-            String category = datas.getString("category");
-            int categoryCode = datas.getInt("categoryCode");
-            int number = datas.getInt("number");
-            String operator = datas.getString("operator");
+            if(datas.isEmpty()){
+                return new EmptyJourney();
+            }
+
+            String name = datas.isNull("name") ? "" : datas.getString("name");
+            String category = datas.isNull("category") ? "" : datas.getString("category");
+            int categoryCode = datas.isNull("categoryCode") ? 0 : datas.getInt("categoryCode");
+            String number = datas.isNull("number") ? "" : datas.getString("number");
+            String operator = datas.isNull("operator") ? "" : datas.getString("operator");
 
-            String to = datas.getString("to");
+            String to = datas.isNull("to") ? "" : datas.getString("to");
 
-            JSONArray passListJSON = datas.getJSONArray("passList");
+            JSONArray passListJSON = datas.isNull("from") ? new JSONArray() : datas.getJSONArray("passList");
             List<Stop> passList = new ArrayList<>();
             passListJSON.forEach( o -> {
                 JSONObject k = (JSONObject) o;
@@ -123,8 +150,8 @@ public final class Journey {
                 }
             });
 
-            int capacity1st = datas.getInt("capacity1st");
-            int capacity2nd = datas.getInt("capacity2nd");
+            int capacity1st = datas.isNull("capacity1st") ? 0 : datas.getInt("capacity1st");
+            int capacity2nd = datas.isNull("capacity2nd") ? 0 : datas.getInt("capacity2nd");
 
             return new Journey(name, category, categoryCode, number, operator, to, passList, capacity1st, capacity2nd);
         }
diff --git a/src/main/java/ch/hepia/api/transport/Location.java b/src/main/java/ch/hepia/api/transport/Location.java
index 1884983b3eff2017d5b1c0297ad1b61086375c20..20125d09f31784a771b1ac62ff99457a6ba3ac5e 100644
--- a/src/main/java/ch/hepia/api/transport/Location.java
+++ b/src/main/java/ch/hepia/api/transport/Location.java
@@ -1,10 +1,9 @@
 package ch.hepia.api.transport;
 
 import org.json.JSONObject;
+import java.io.Serializable;
 
-import java.util.Optional;
-
-public final class Location {
+public class Location implements Serializable {
     private int id;
     private Type type;
     private String name;
@@ -12,6 +11,20 @@ public final class Location {
     private Coordinates where;
     private double distance;
 
+    /**
+     * Default Location
+     */
+    private final static class DefaultLocation extends Location{
+        /**
+         * Constructor of DefaultLocation, centered in Geneva
+         * Type is REFINE, because it's a default choice
+         */
+        private DefaultLocation(){
+            //here building new empty coordinates will return a default coordinates corresponding to Geneva
+            super(8501008, Type.REFINE, "Genève", 0, new Coordinates.CoordinatesBuilder(new JSONObject()).build(), 0);
+        }
+    }
+
     /**
      * Constructor of the class Location
      * @param id unique to the Location
@@ -38,10 +51,18 @@ public final class Location {
         return where;
     }
 
+    /**
+     * Get the name of the location
+     * @return what
+     */
+    public String getName(){
+        return name;
+    }
+
     /**
      * Builder of Location objects
      */
-    public static class LocationBuilder{
+    public final static class LocationBuilder{
         private JSONObject datas;
         private String type;
 
@@ -60,15 +81,19 @@ public final class Location {
          * @return a new Location object
          */
         public Location build(){
-            int id = datas.getInt("id");
+            if(datas.isEmpty()){
+                return new DefaultLocation();
+            }
+
+            int id = datas.isNull("id") ? 0 : datas.getInt("id");
             Type type = Type.valueOf(this.type.toUpperCase());
-            String name = datas.getString("name");
+            String name = datas.isNull("name") ? "" : datas.getString("name");
             double score = datas.isNull("score") ? 0 : datas.getDouble("score");
 
-            JSONObject coordinatesJSON = datas.getJSONObject("coordinate");
-            Coordinates.CoordinatesBuilder coordinates = new Coordinates.CoordinatesBuilder(coordinatesJSON, type.toString());
+            JSONObject coordinatesJSON = datas.isNull("coordinate") ? new JSONObject() : datas.getJSONObject("coordinate");
+            Coordinates.CoordinatesBuilder coordinates = new Coordinates.CoordinatesBuilder(coordinatesJSON);
 
-            double distance = datas.isNull("score") ? 0 : datas.getDouble("distance");
+            double distance = datas.isNull("distance") ? 0 : datas.getDouble("distance");
 
             return new Location(id, type, name, score, coordinates.build(), distance);
         }
diff --git a/src/main/java/ch/hepia/api/transport/Prognosis.java b/src/main/java/ch/hepia/api/transport/Prognosis.java
index 90ebf678d0fd4e27828c37a8af4929f8d3b106a2..0b8ba5527ee0d825f89c9a2ad494d34454feb9ba 100644
--- a/src/main/java/ch/hepia/api/transport/Prognosis.java
+++ b/src/main/java/ch/hepia/api/transport/Prognosis.java
@@ -6,14 +6,33 @@ import java.sql.Time;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.io.Serializable;
 
-public final class Prognosis {
+public class Prognosis implements Serializable {
     private int plateform;
     private Time departure;
     private Time arrival;
     private int capacity1st;
     private int capacity2nd;
 
+    /**
+     * Empty Prognosis
+     */
+    private final static class EmptyPrognosis extends Prognosis{
+        /**
+         * Constructor of EmptyPrognosis
+         * No departure and no Arrival Time
+         * No information on the capacity of 1st and 2nd class nor on the plateform
+         * @throws ParseException
+         */
+        private EmptyPrognosis() throws ParseException {
+            super(0,
+                    new Time(new SimpleDateFormat("dd:kk:mm:ss").parse("00:00:00:00").getTime()),
+                    new Time(new SimpleDateFormat("dd:kk:mm:ss").parse("00:00:00:00").getTime()),
+                    0, 0);
+        }
+    }
+
     /**
      * Constructor of the Prognosis class
      * A Prognosis contains information on the status of a Stop Object
@@ -34,7 +53,7 @@ public final class Prognosis {
     /**
      * Builder of Prognosis Objects
      */
-    public static class PrognosisBuilder{
+    public final static class PrognosisBuilder{
         private JSONObject datas;
 
         /**
@@ -52,17 +71,21 @@ public final class Prognosis {
          * @throws ParseException
          */
         public Prognosis build() throws ParseException {
-            int plateform = datas.getInt("plateform");
+            if(datas.isEmpty()){
+                return new EmptyPrognosis();
+            }
+
+            int plateform = datas.isNull("plateform") ? 0 : datas.getInt("plateform");
 
             DateFormat formatter = new SimpleDateFormat("dd:kk:mm:ss");
-            String depatureString = datas.getString("departure").replace('d',':');
+            String depatureString = datas.isNull("departure") ? "00:00:00:00" : datas.getString("departure").replace('d',':');
             Time departure = new Time(formatter.parse(depatureString).getTime());
 
-            String arrivalString = datas.getString("arrival").replace('d', ':');
+            String arrivalString = datas.isNull("arrival") ? "00:00:00:00" : datas.getString("arrival").replace('d', ':');
             Time arrival = new Time(formatter.parse(arrivalString).getTime());
 
-            int capacity1st = datas.getInt("capacity1st");
-            int capacity2nd = datas.getInt("capacity2nd");
+            int capacity1st = datas.isNull("capacity1st") ? 0 : datas.getInt("capacity1st");
+            int capacity2nd = datas.isNull("capacity2nd") ? 0 : datas.getInt("capacity2nd");
 
             return new Prognosis(plateform, departure, arrival, capacity1st, capacity2nd);
         }
diff --git a/src/main/java/ch/hepia/api/transport/Section.java b/src/main/java/ch/hepia/api/transport/Section.java
index 8fd4df952cd49f77ecf57459c0f8dd985a61b4f7..72f92d728c85d7f2c7fd7071e1fd47de74e63854 100644
--- a/src/main/java/ch/hepia/api/transport/Section.java
+++ b/src/main/java/ch/hepia/api/transport/Section.java
@@ -3,13 +3,31 @@ package ch.hepia.api.transport;
 import org.json.JSONObject;
 
 import java.text.ParseException;
+import java.io.Serializable;
 
-public final class Section {
+public class Section implements Serializable {
     private Journey journey;
     private double walktime;
     private Stop departure;
     private Stop arrival;
 
+    /**
+     * Empty Section
+     */
+    private final static class EmptySection extends Section{
+        /**
+         * Constructor of EmptySection
+         * An empty Section is a Journey From Geneva to Geneva
+         * @throws ParseException
+         */
+        private EmptySection() throws ParseException {
+            super(new Journey.JourneyBuilder(new JSONObject()).build(), 0,
+                    new Stop.StopBuilder(new JSONObject()).build(),
+                    new Stop.StopBuilder(new JSONObject()).build());
+        }
+    }
+
+
     /**
      * Constructor of the class Section
      * @param journey what Journey this section is part of
@@ -51,7 +69,7 @@ public final class Section {
     /**
      * Builder of Section objects
      */
-    public static class SectionBuilder{
+    public final static class SectionBuilder{
         private JSONObject datas;
 
         /**
@@ -69,15 +87,19 @@ public final class Section {
          * @throws ParseException
          */
         public Section build() throws ParseException {
-            JSONObject journeyJSON = new JSONObject(datas.get("journey"));
+            if(datas.isEmpty()){
+                return new EmptySection();
+            }
+
+            JSONObject journeyJSON = datas.isNull("journey") ? new JSONObject() : datas.getJSONObject("journey");
             Journey.JourneyBuilder journey = new Journey.JourneyBuilder(journeyJSON);
 
             double walktime = datas.isNull("walktime") ? 0 : datas.getDouble("walktime");
 
-            JSONObject departureJSON = new JSONObject(datas.get("departure"));
+            JSONObject departureJSON = datas.isNull("departure") ? new JSONObject() : datas.getJSONObject("departure");
             Stop.StopBuilder departure = new Stop.StopBuilder(departureJSON);
 
-            JSONObject arrivalJSON = new JSONObject(datas.get("arrival"));
+            JSONObject arrivalJSON = datas.isNull("arrival") ? new JSONObject() : datas.getJSONObject("arrival");
             Stop.StopBuilder arrival = new Stop.StopBuilder(arrivalJSON);
 
             return new Section(journey.build(), walktime, departure.build(), arrival.build());
diff --git a/src/main/java/ch/hepia/api/transport/Service.java b/src/main/java/ch/hepia/api/transport/Service.java
index 4e04c4198f2524d7102d7c5aba877f3a93ca5394..16796b344db5759759a7774140b0fb9fd35fe917 100644
--- a/src/main/java/ch/hepia/api/transport/Service.java
+++ b/src/main/java/ch/hepia/api/transport/Service.java
@@ -1,12 +1,25 @@
 package ch.hepia.api.transport;
 
 import org.json.JSONObject;
+import java.io.Serializable;
 
-
-public final class Service {
+public class Service implements Serializable {
     private String regular;
     private String irregular;
 
+    /**
+     * Empty Service
+     */
+    private final static class EmptyService extends Service{
+        /**
+         * Constructor of EmptyService
+         * No informations given
+         */
+        private EmptyService(){
+            super("", "");
+        }
+    }
+
     /**
      * Constructor of Service class
      * @param regular is the service regular ?
@@ -20,7 +33,7 @@ public final class Service {
     /**
      * Builder of Service objects
      */
-    public static class ServiceBuilder{
+    public final static class ServiceBuilder{
         private JSONObject datas;
 
         /**
@@ -37,8 +50,12 @@ public final class Service {
          * @return a new Service object
          */
         public Service build(){
-            String regular = datas.getString("regular");
-            String irregular = datas.getString("irregular");
+            if(datas.isEmpty()){
+                return new EmptyService();
+            }
+
+            String regular = datas.isNull("regular") ? "" : datas.getString("regular");
+            String irregular = datas.isNull("irregular") ? "" : datas.getString("irregular");
 
             return new Service(regular, irregular);
         }
diff --git a/src/main/java/ch/hepia/api/transport/Stop.java b/src/main/java/ch/hepia/api/transport/Stop.java
index f30c41070d766aec9b9278c27078c854f352b708..b5c2193043b670bf16a5605a7a182acca89c4400 100644
--- a/src/main/java/ch/hepia/api/transport/Stop.java
+++ b/src/main/java/ch/hepia/api/transport/Stop.java
@@ -6,8 +6,9 @@ import java.sql.Time;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.io.Serializable;
 
-public final class Stop {
+public class Stop implements Serializable {
     private Location station;
     private Time arrival;
     private Time departure;
@@ -15,6 +16,25 @@ public final class Stop {
     private int plateform;
     private Prognosis prognosis;
 
+    /**
+     * Default Stop in Geneva
+     */
+    private final static class DefaultStop extends Stop{
+        /**
+         * Constructor of DefaultStop
+         * DefaultStop is located in Geneva
+         * No Time of departure nor Arrival, no Prognosis, no plateform and no delay
+         * @throws ParseException
+         */
+        private DefaultStop() throws ParseException {
+            super(new Location.LocationBuilder(new JSONObject(), "refine").build(),
+                    new Time(new SimpleDateFormat("dd:kk:mm:ss").parse("00:00:00:00").getTime()),
+                    new Time(new SimpleDateFormat("dd:kk:mm:ss").parse("00:00:00:00").getTime()),
+                    0, 0,
+                    new Prognosis.PrognosisBuilder(new JSONObject()).build());
+        }
+    }
+
     /**
      * Constructor of the Stop class
      * @param station Location where the Stop is
@@ -60,7 +80,7 @@ public final class Stop {
     /**
      * Builder of Stop objects
      */
-    public static class StopBuilder{
+    public final static class StopBuilder{
         private JSONObject datas;
 
         /**
@@ -78,23 +98,28 @@ public final class Stop {
          * @throws ParseException
          */
         public Stop build() throws ParseException {
+            if(datas.isEmpty()){
+                return new DefaultStop();
+            }
+
             //getting the first key to know the type of the Location
-            String[] keys = (String[]) datas.keySet().toArray();
-            String type = keys[0];
-            JSONObject locationJSON = new JSONObject(datas.get(type));
+            String[] keys = new String[10];
+            datas.keySet().toArray(keys);
+            String type = keys[4];
+            JSONObject locationJSON = datas.isNull("location") ? new JSONObject() : datas.getJSONObject("location");
             Location.LocationBuilder location = new Location.LocationBuilder(locationJSON, type);
 
-            DateFormat formatter = new SimpleDateFormat("yyyy-MM-dddkk:mm:ss");
+            DateFormat formatter = new SimpleDateFormat("kk:mm:ss");
 
-            String arrivalString = datas.getString("arrival");
+            String arrivalString = datas.isNull("arrival") ? "00:00:00" : datas.getString("arrival").substring(11, datas.getString("arrival").indexOf("+"));
             Time arrival = new Time(formatter.parse(arrivalString).getTime());
-            String depatureString = datas.getString("departure");
+            String depatureString = datas.isNull("departure") ? "00:00:00" : datas.getString("departure").substring(11, datas.getString("departure").indexOf("+"));
             Time departure = new Time(formatter.parse(depatureString).getTime());
 
-            int delay = datas.getInt("delay");
-            int plateform = datas.getInt("plateform");
+            int delay = datas.isNull("delay") ? 0 : datas.getInt("delay");
+            int plateform = datas.isNull("plateform") ? 0 : datas.getInt("plateform");
 
-            JSONObject prognosisJSON = new JSONObject(datas.get("prognosis"));
+            JSONObject prognosisJSON = datas.isNull("prognosis") ? new JSONObject() : new JSONObject(datas.get("prognosis"));
             Prognosis.PrognosisBuilder prognosis = new Prognosis.PrognosisBuilder(prognosisJSON);
 
             return new Stop(location.build(), arrival, departure, delay, plateform, prognosis.build());
diff --git a/src/main/java/ch/hepia/config/AppConfig.java b/src/main/java/ch/hepia/config/AppConfig.java
index 75392f61204b8b92c396f2dba22bcc5574cea242..b52689701cd4ef6e1000e485281587d3f58941e3 100644
--- a/src/main/java/ch/hepia/config/AppConfig.java
+++ b/src/main/java/ch/hepia/config/AppConfig.java
@@ -1,5 +1,7 @@
 package ch.hepia.config;
 
+import javafx.scene.paint.Color;
+
 /**
  * The variables of the app
  */
@@ -8,6 +10,27 @@ public final class AppConfig {
 	public static final Integer APP_WIDTH = 1000;
 	public static final Integer APP_HEIGHT = 565;
 
+	public static final Integer APP_MAIN_VIEW_WIDTH = 622;
+
 	public static final String RABBITMQ_HOSTNAME = "redgrave.science";
-	public static final Integer RABBITMQ_PORT = 5672;
+	public static final String RABBITMQ_USERNAME = "frog";
+	public static final String RABBITMQ_PASSWORD = "poney1234";
+	public static final String RABBITMQ_EXCHANGE = "broadcaster";
+	public static final Integer RABBITMQ_PORT = 5672; // The MQ uses the default port, so this value is not used.
+
+
+	public static final String ERROR_API_UNREACHABLE = "Impossible de contacter les services de transport Suisses.";
+
+	/**
+	 * Resources
+	 */
+	public static final String CHAT_MESSAGE_ICON = "/img/bubble.png";
+	
+	/**
+	 * Style
+	 */
+	public static final Color COLOR_BLUE_10_OPACITY = Color.color(0.207, 0.694, 0.933, 0.1);
+	public static final Color COLOR_BLUE_50_OPACITY = Color.color(0.207, 0.694, 0.933, 0.5);
+	public static final Color COLOR_BLUE_100_OPACITY = Color.color(0.207, 0.694, 0.933);
+
 }
diff --git a/src/main/java/ch/hepia/config/AppContext.java b/src/main/java/ch/hepia/config/AppContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1db3272275c6b9bf947cdac9ae1f52cb8e800a1
--- /dev/null
+++ b/src/main/java/ch/hepia/config/AppContext.java
@@ -0,0 +1,47 @@
+package ch.hepia.config;
+
+import ch.hepia.models.User;
+import ch.hepia.mq.MessageManager;
+
+import java.util.Optional;
+
+/**
+ * Represents the current config of the app.
+ */
+public class AppContext {
+	private MessageManager messageManager;
+	private Optional<User> user;
+
+	/**
+	 * Main constructor
+	 * @param messageManager the message manager instance
+	 */
+	public AppContext(MessageManager messageManager){
+		this.messageManager = messageManager;
+		this.user = Optional.empty();
+	}
+
+	/**
+	 * Gets the message manager
+	 * @return The message manager
+	 */
+	public MessageManager getMessageManager() {
+		return messageManager;
+	}
+
+	/**
+	 * Gets the user
+	 * @return The current user
+	 */
+	public Optional<User> getUser() {
+		return user;
+	}
+
+	/**
+	 * Sets the user
+	 * @param user the user
+	 */
+	public void setUser(User user) {
+		this.user = Optional.of(user);
+	}
+}
diff --git a/src/main/java/ch/hepia/events/ChatMessage.java b/src/main/java/ch/hepia/events/ChatMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..a06b6bff38fed5300051b7568d63d2579ea6fa09
--- /dev/null
+++ b/src/main/java/ch/hepia/events/ChatMessage.java
@@ -0,0 +1,22 @@
+package ch.hepia.events;
+
+import ch.hepia.models.User;
+import java.io.Serializable;
+
+public class ChatMessage implements Serializable {
+    private User user;
+    private String chatMessage;
+
+    public ChatMessage(User user, String chatMessage) {
+        this.user = user;
+        this.chatMessage = chatMessage;
+    }
+
+    public User getUser() {
+        return this.user;
+    }
+
+    public String getMessage() {
+        return this.chatMessage;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/ch/hepia/events/JoinedJourney.java b/src/main/java/ch/hepia/events/JoinedJourney.java
index 5e1cb2e823426db511d4072eb5b6ea6b0124fcdf..aef52676c54626140953e8b5dab95a7f6887585b 100644
--- a/src/main/java/ch/hepia/events/JoinedJourney.java
+++ b/src/main/java/ch/hepia/events/JoinedJourney.java
@@ -27,6 +27,14 @@ public class JoinedJourney implements Serializable{
         this.sections = new ArrayList<>(sections);
     }
 
+    public User getUser() {
+        return this.user;
+    }
+
+    public List<Section> getSections() {
+        return this.sections;
+    }
+
     @Override
     public String toString() {
         // TODO
diff --git a/src/main/java/ch/hepia/events/LeftJourney.java b/src/main/java/ch/hepia/events/LeftJourney.java
index 11c49eafe3f94421464631575055822f8f4145cc..47964e860ebc6cdbef6433b033eac9c5b35f42c8 100644
--- a/src/main/java/ch/hepia/events/LeftJourney.java
+++ b/src/main/java/ch/hepia/events/LeftJourney.java
@@ -3,9 +3,9 @@ package ch.hepia.events;
 import ch.hepia.api.transport.Section;
 import ch.hepia.models.User;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
-import java.io.Serializable;
 
 /**
  * Represents an event where a user has left a journey.
@@ -27,6 +27,14 @@ public class LeftJourney implements Serializable {
         this.sections = new ArrayList<>(sections);
     }
 
+    public User getUser() {
+        return this.user;
+    }
+
+    public List<Section> getSections() {
+        return this.sections;
+    }
+    
     @Override
     public String toString() {
         // TODO
diff --git a/src/main/java/ch/hepia/models/User.java b/src/main/java/ch/hepia/models/User.java
index 1678ca61dfe1cf9d8c9aad662ab21ea2a46733e8..911acbb3634d32720b44b53b00fba1de14600135 100644
--- a/src/main/java/ch/hepia/models/User.java
+++ b/src/main/java/ch/hepia/models/User.java
@@ -1,19 +1,66 @@
 package ch.hepia.models;
-
-import java.util.Objects;
-
+import java.io.Serializable;
+import java.util.*;
 /**
  * Represents an user.
  */
-public class User {
+public class User implements Serializable {
 
 	private String username;
+	private transient List<User> ignoredUserList;
 
 	public User(String username){
 		this.username = username;
+		this.ignoredUserList = new ArrayList<>();
+	}
+
+	/** 
+	 * Add a user to the ignored list
+	 * @param user User to add
+	*/
+	public void addIgnoredUser(User user) {
+		this.ignoredUserList.add(user); 
+	}
+
+	/** 
+	 * Remove a user to the ignored list
+	 * @param user User to remove
+	*/	
+	public void removeIgnoredUser(User user) {
+		this.ignoredUserList.remove(user);
 	}
 
+	/** 
+	 * Get the ignored list
+	 * @return The Ingored user list to remove
+	*/	
+	public List<User> getIgnoredUserList(){
+		return this.ignoredUserList;
+	}
+
+	/** 
+	 * Get the name
+	 * @return The username
+	*/
+	public String getName(){
+		return this.username;
+	}
+
+	@Override
 	public String toString(){
-		return username;
+		return this.username;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if((o == null) || (this.getClass() != o.getClass())){
+			return false;
+		} else {
+			User u = (User)o;
+			if (this.username.equals(u.username)){
+				return true;
+			}
+		}
+		return false;
 	}
 }
diff --git a/src/main/java/ch/hepia/mq/DummyClass.java b/src/main/java/ch/hepia/mq/DummyClass.java
deleted file mode 100644
index b37bb3270d4677db67ba7854a3262ba25e11a702..0000000000000000000000000000000000000000
--- a/src/main/java/ch/hepia/mq/DummyClass.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package ch.hepia;
-import java.io.Serializable;
-
-public class DummyClass implements Serializable{
-    private static final long serialVersionUID = 0xAEF34565672L;
-    private String prop0;
-    public int prop1;
-
-    public DummyClass(){
-        this.prop0 = "élè";
-        this.prop1 = -12;
-    }
-    public String getProp0(){
-        return this.prop0;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/ch/hepia/mq/Message.java b/src/main/java/ch/hepia/mq/Message.java
new file mode 100644
index 0000000000000000000000000000000000000000..20b0c81c40891c4d8371070c505cc037f1288b7a
--- /dev/null
+++ b/src/main/java/ch/hepia/mq/Message.java
@@ -0,0 +1,36 @@
+package ch.hepia.mq;
+import java.io.Serializable;
+
+public final class Message implements Serializable {
+    public static enum Type {
+        JoinedJourney, LeftJourney, ChatMessage
+    }
+    
+    private static final long serialVersionUID = 0xAEF34565673L;
+    private final Type type;
+    private byte[] data;
+
+    /**
+     * Standard constructor
+     * @param type The type of the message
+     * @param object Payload to serialize
+     */
+    public <T extends Serializable> Message(Type type, T object) {
+        this.type = type;
+        this.data = MessageQueue.serialize(object);
+    }
+    /**
+     * Get the message type
+     * @return The type of the message
+     */
+    public Type getMessageType() {
+        return this.type;
+    }
+    /**
+     * Get the payload of the message
+     * @return The payload of the message
+     */
+    public <T> T getData() {
+        return MessageQueue.unserialize(this.data);
+    }
+}
diff --git a/src/main/java/ch/hepia/mq/MessageManager.java b/src/main/java/ch/hepia/mq/MessageManager.java
index 0bc3c17ca7c476a8332e5e3f11692474c339e359..1b57f6ce76a11011c5399b54876fe5f14ea80992 100644
--- a/src/main/java/ch/hepia/mq/MessageManager.java
+++ b/src/main/java/ch/hepia/mq/MessageManager.java
@@ -7,28 +7,7 @@ import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 public class MessageManager extends MessageQueue {
-    private enum MessageType {
-        JoinedJourney, LeftJourney
-    }
-
-    private final class Message implements Serializable {
-        private static final long serialVersionUID = 0xAEF34565673L;
-        private final MessageType type;
-        private byte[] data;
-
-        public <T extends Serializable> Message(MessageType type, T object) {
-            this.type = type;
-            this.data = MessageQueue.serialize(object);
-        }
 
-        public MessageType getMessageType() {
-            return this.type;
-        }
-
-        public <T> T getData() {
-            return MessageQueue.unserialize(this.data);
-        }
-    }
 
     public MessageManager(String host, String username, String password, String exchange) throws Exception {
         super(host, username, password, exchange);
@@ -37,7 +16,13 @@ public class MessageManager extends MessageQueue {
     /*
      * Private functions
      */
-    private <T extends Serializable> void sendEvent(MessageType type, T event) {
+
+    /**
+     * Send an event to the queue
+     * @param type The type of the message
+     * @param event The event
+     */
+    private <T extends Serializable> void sendEvent(Message.Type type, T event) {
         Message m = new Message(type, event);
         try {
             this.sendBytes(MessageQueue.serialize(m));
@@ -46,11 +31,16 @@ public class MessageManager extends MessageQueue {
         }
     }
 
-    private <T extends Serializable> void conditionalSubscribe(MessageType type, Consumer<T> eventHandler,
+    /**
+     * Add a consumer for a specific type of message
+     * @param type The type of the message
+     * @param eventHandler Consumer to add
+     */
+    private <T extends Serializable> void conditionalSubscribe(Message.Type type, Consumer<T> eventHandler,
             Predicate<T> condition) {
         Consumer<byte[]> consumer = (bytes) -> {
             Message receivedMessage = MessageQueue.unserialize(bytes);
-            if (receivedMessage.type == type) {
+            if (receivedMessage.getMessageType() == type) {
                 T event = receivedMessage.getData();
                 if (condition.test(event)) {
                     eventHandler.accept(event);
@@ -63,28 +53,80 @@ public class MessageManager extends MessageQueue {
     /*
      * Public functions
      */
+
+    /**
+     * Subscribe to all JoinedJourney events
+     * @param eventHandler JoinedJourney consumer
+     */
     public void subscribeJoinedJourney(Consumer<JoinedJourney> eventHandler) {
         this.conditionalSubscribeJoinedJourney(eventHandler, (event) -> true);
     }
 
+    /**
+     * Subscribe to all LeftJourney events
+     * @param eventHandler LeftJourney consumer
+     */
     public void subscribeLeftJourney(Consumer<LeftJourney> eventHandler) {
         this.conditionalSubscribeLeftJourney(eventHandler, (event) -> true);
     }
 
+    /**
+     * Subscribe to JoinedJourney events validating the condition
+     * @param eventHandler JoinedJourney consumer
+     * @param condition JoinedJourney predicate
+     */
     public void conditionalSubscribeJoinedJourney(Consumer<JoinedJourney> eventHandler,
             Predicate<JoinedJourney> condition) {
-        this.conditionalSubscribe(MessageType.JoinedJourney, eventHandler, condition);
+        this.conditionalSubscribe(Message.Type.JoinedJourney, eventHandler, condition);
     }
 
+    /**
+     * Subscribe to LeftJourney events validating the condition
+     * @param eventHandler LeftJourney consumer
+     * @param condition LeftJourney predicate
+     */
     public void conditionalSubscribeLeftJourney(Consumer<LeftJourney> eventHandler, Predicate<LeftJourney> condition) {
-        this.conditionalSubscribe(MessageType.LeftJourney, eventHandler, condition);
+        this.conditionalSubscribe(Message.Type.LeftJourney, eventHandler, condition);
     }
 
+    /**
+     * Send a JoinedJourney event
+     * @param event JoinedJourney event
+     */
     public void sendJoinedJourney(JoinedJourney event) {
-        this.sendEvent(MessageType.JoinedJourney, event);
+        this.sendEvent(Message.Type.JoinedJourney, event);
     }
 
+    /**
+     * Send a LeftJourney event
+     * @param event LeftJourney event
+     */
     public void sendLeftJourney(LeftJourney event) {
-        this.sendEvent(MessageType.LeftJourney, event);
+        this.sendEvent(Message.Type.LeftJourney, event);
+    }
+
+    /**
+     * Subscribe to all ChatMessage events
+     * @param eventHandler ChatMessage consumer
+     */
+    public void subscribeChatMessage(Consumer<ChatMessage> eventHandler) {
+        this.conditionalSubscribeChatMessage(eventHandler, (event) -> true);
+    }
+
+    /**
+     * Subscribe to ChatMessage events validating the condition
+     * @param eventHandler ChatMessage consumer
+     * @param condition ChatMessage predicate
+     */
+    public void conditionalSubscribeChatMessage(Consumer<ChatMessage> eventHandler, Predicate<ChatMessage> condition) {
+        this.conditionalSubscribe(Message.Type.ChatMessage, eventHandler, condition);
+    }
+
+    /**
+     * Send a ChatMessage event
+     * @param event ChatMessage event
+     */
+    public void sendChatMessage(ChatMessage event) {
+        this.sendEvent(Message.Type.ChatMessage, event);
     }
 }
\ No newline at end of file
diff --git a/src/main/java/ch/hepia/mq/MessageQueue.java b/src/main/java/ch/hepia/mq/MessageQueue.java
index e898fb70d28a3ff108c8d1aa950da825575c7cc5..a9df5112fe5473f98124e7076aba41ad0251b4cb 100644
--- a/src/main/java/ch/hepia/mq/MessageQueue.java
+++ b/src/main/java/ch/hepia/mq/MessageQueue.java
@@ -15,74 +15,98 @@ import com.rabbitmq.client.Connection;
 import com.rabbitmq.client.DeliverCallback;
 
 public abstract class MessageQueue {
-    private final String exchange;
-    private final Connection mqConnection;
-    private final Channel mqChannel;
+	private final String exchange;
+	private final Connection mqConnection;
+	private final Channel mqChannel;
     private final List<Consumer<byte[]>> consumers;
+    
+    /** 
+     * Construct a temporary RabbitMQ queue, bind it to an exchange
+     * on the server
+     * @param host Hostname of the rabbitmq server
+     * @param username RabbitMQ user
+     * @param password RabbitMQ password
+     * @param exchange Exchange to bind
+    */
+	public MessageQueue(String host, String username, String password, String exchange) throws Exception {
+		this.consumers = new ArrayList<>();
+		this.exchange = exchange;
+		ConnectionFactory queueConnector = new ConnectionFactory();
+		queueConnector.setHost(host);
+		queueConnector.setUsername(username);
+		queueConnector.setPassword(password);
 
-    public MessageQueue(String host, String username, String password, String exchange) throws Exception {
-        this.consumers = new ArrayList<>();
-        this.exchange = exchange;
-        ConnectionFactory queueConnector = new ConnectionFactory();
-        queueConnector.setHost(host);
-        queueConnector.setUsername(username);
-        queueConnector.setPassword(password);
+		this.mqConnection = queueConnector.newConnection();
+		this.mqChannel = this.mqConnection.createChannel();
+		String queueName = this.mqChannel.queueDeclare().getQueue();
+		this.mqChannel.queueBind(queueName, this.exchange, "");
+		DeliverCallback deliverCallback = (consumerTag, delivery) -> {
+			for (Consumer<byte[]> consumer : this.consumers) {
+				consumer.accept(delivery.getBody());
+			}
+		};
+		this.mqChannel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
+		});
+	}
 
-        this.mqConnection = queueConnector.newConnection();
-        this.mqChannel = this.mqConnection.createChannel();
-        String queueName = this.mqChannel.queueDeclare().getQueue();
-        this.mqChannel.queueBind(queueName, this.exchange, "");
-        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
-            for (Consumer<byte[]> consumer : this.consumers) {
-                consumer.accept(delivery.getBody());
-            }
-        };
-        this.mqChannel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
-        });
-    }
-
-    protected void addConsumer(Consumer<byte[]> messageHandler) {
-        this.consumers.add(messageHandler);
-    }
+    /** 
+     * Add a consumer to the queue
+     * @param messageHandler Message consumer
+    */
+	protected void addConsumer(Consumer<byte[]> messageHandler) {
+		this.consumers.add(messageHandler);
+	}
 
-    protected void sendBytes(byte[] bytes) throws Exception {
-        this.mqChannel.basicPublish(this.exchange, "", null, bytes);
-    }
+    /** 
+     * Send a byte array in the queue. 
+     * @param bytes The byte array
+    */
+	protected void sendBytes(byte[] bytes) throws Exception {
+		this.mqChannel.basicPublish(this.exchange, "", null, bytes);
+	}
 
-    public void close() throws Exception {
-        this.mqChannel.close();
-        this.mqConnection.close();
-    }
-
-    public static <T extends Serializable> byte[] serialize(T object){
-        ObjectOutputStream oos = null;
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        try {
-            oos = new ObjectOutputStream(byteStream);
-            oos.writeObject(object);
-            oos.flush();
-            return byteStream.toByteArray();
-        } catch (final Exception e) {
-            e.printStackTrace();
-        }finally{
-            // TODO
-            System.exit(1);
-        }
-        return null;
-    }
+    /** 
+     * Terminate the connection with rabbitMQ
+    */
+	public void close() throws Exception {
+		this.mqChannel.close();
+		this.mqConnection.close();
+	}
 
-    public static <T extends Serializable> T unserialize(byte[] serializedData){
-        ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedData);
-        T result = null;
-        try {
-            ObjectInputStream ois = new ObjectInputStream(inputStream);
-            result = (T) ois.readObject();
-        } catch (final Exception e) {
-            e.printStackTrace();
-        }finally{
-            // TODO
-            System.exit(1);
-        }
-        return result;
+    /** 
+     * Serialize an object, return a byte array.
+     * @param object Object to serialize
+    */
+	public static <T extends Serializable> byte[] serialize(T object){
+		try (
+				ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+				ObjectOutputStream oos = new ObjectOutputStream(byteStream)
+		) {
+			oos.writeObject(object);
+			oos.flush();
+			return byteStream.toByteArray();
+		} catch (final Exception e) {
+			e.printStackTrace();
+			System.exit(1);
+		}
+		return null;
     }
+    
+    /** 
+     * Deserialize a byte array
+     * @param serializedData serialized object
+    */
+	public static <T extends Serializable> T unserialize(byte[] serializedData){
+		T result = null;
+		try (
+				ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedData);
+				ObjectInputStream ois = new ObjectInputStream(inputStream)
+		) {
+			result = (T) ois.readObject();
+		} catch (final Exception e) {
+			e.printStackTrace();
+			System.exit(1);
+		}
+		return result;
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/ch/hepia/mq/TesterClass.java b/src/main/java/ch/hepia/mq/TesterClass.java
deleted file mode 100644
index defcddcd9b78f3ac68a59a8ad9d0042c5e8b4491..0000000000000000000000000000000000000000
--- a/src/main/java/ch/hepia/mq/TesterClass.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package ch.hepia.mq;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Arrays;
-import java.util.function.Consumer;
-import java.util.*;
-
-public class TesterClass {
-    public static void main(String[] argv) throws Exception{
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/ch/hepia/ui/ConnectionController.java b/src/main/java/ch/hepia/ui/ConnectionController.java
index 1f7f5e083adcfb01c788916ba6909e415519a8ef..9e6abe25f1d0984ba0e5f2ac5dc9c8b6c97ef5ec 100644
--- a/src/main/java/ch/hepia/ui/ConnectionController.java
+++ b/src/main/java/ch/hepia/ui/ConnectionController.java
@@ -2,6 +2,7 @@ package ch.hepia.ui;
 
 import ch.hepia.Main;
 import ch.hepia.config.AppConfig;
+import ch.hepia.models.User;
 import javafx.animation.FadeTransition;
 import javafx.event.ActionEvent;
 import javafx.fxml.FXML;
@@ -12,8 +13,10 @@ import javafx.scene.Scene;
 import javafx.scene.control.Button;
 import javafx.scene.control.Label;
 import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
 import javafx.stage.Stage;
 import javafx.stage.WindowEvent;
+import javafx.application.Platform;
 import javafx.util.Duration;
 
 import java.net.URL;
@@ -52,7 +55,8 @@ public class ConnectionController implements Initializable {
 			fadeIn.setAutoReverse(false);
 			fadeIn.playFromStart();
 		} else {
-			// User user = new User(UsernameSelectionTextField.getText());
+			User user = new User(usernameSelectionTextField.getText());
+			Main.getContext().setUser(user);
 			Stage stage = (Stage) handleConfirmButton.getScene().getWindow();
 			Parent root = FXMLLoader.load(Main.class.getResource("/fxml/MainWindow.fxml"));
 			Scene scene = new Scene(root, AppConfig.APP_WIDTH, AppConfig.APP_HEIGHT);
@@ -72,5 +76,7 @@ public class ConnectionController implements Initializable {
 		// Faire les initialisations avant affichage ici.
 		appNameLabel.setText(AppConfig.APP_NAME);
 		appConnectionStatusLabel.setText("Merci de rentrer votre nom. Il permettra de vous identifier.");
+		Platform.runLater(() -> usernameSelectionTextField.requestFocus());
+		UiUtils.buttonWhenEnter(usernameSelectionTextField, handleConfirmButton);
 	}
 }
diff --git a/src/main/java/ch/hepia/ui/MainWindowController.java b/src/main/java/ch/hepia/ui/MainWindowController.java
new file mode 100644
index 0000000000000000000000000000000000000000..bcd829cf834905012e22296a3055c79d1fbc304b
--- /dev/null
+++ b/src/main/java/ch/hepia/ui/MainWindowController.java
@@ -0,0 +1,274 @@
+package ch.hepia.ui;
+
+import ch.hepia.Main;
+import ch.hepia.api.transport.Connection;
+import ch.hepia.api.transport.Journey;
+import ch.hepia.api.transport.LinkAPI;
+import ch.hepia.api.transport.Section;
+import ch.hepia.config.AppConfig;
+import ch.hepia.config.AppContext;
+import ch.hepia.events.ChatMessage;
+import ch.hepia.models.User;
+import javafx.animation.TranslateTransition;
+import javafx.application.Platform;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.geometry.Insets;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.control.*;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.*;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.CycleMethod;
+import javafx.scene.paint.LinearGradient;
+import javafx.scene.paint.Stop;
+import javafx.util.Duration;
+import org.json.JSONArray;
+
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import javafx.scene.layout.Pane;
+
+public class MainWindowController implements Initializable {
+
+    @FXML
+    private Label currentJourneyLabel;
+
+    @FXML
+    private Label startStopLabel;
+
+    @FXML
+    private ComboBox<String> originComboBox;
+
+    @FXML
+    private ComboBox<String> destinationComboBox;
+
+    @FXML
+    private Canvas connectionCanvas;
+
+    @FXML
+    private Button searchOriginButton;
+
+    @FXML
+    private Button searchDestinationButton;
+
+    @FXML
+    private Button launchItinaryButton;
+
+    @FXML
+    private Button sendMessageButton;
+
+    @FXML
+    private TextField messageTextBox;
+
+    @FXML
+    private Pane chatContainer;
+
+    /**
+     * Shows a sad message when the API crashes.
+     */
+    private void showSadMessage(String text){
+        UiUtils.dialog(Alert.AlertType.ERROR, "Erreur",
+            "Une erreur est survenue.", text);
+    }
+
+    /**
+     * Searches stops that matches the query.
+     * @param newValue The stop name query.
+     * @param transportApi The API to connect to
+     * @param target The combo box where to put the results.
+     */
+    private void searchStops(String newValue, LinkAPI transportApi, ComboBox<String> target){
+        if (!newValue.isEmpty() && newValue.length() > 3 && !target.getItems().contains(newValue)){
+            try {
+                JSONArray a = transportApi.getStations(newValue);
+                ArrayList<String> results = new ArrayList<>();
+                for (int i = 0; i < a.length(); i++){
+                    results.add(a.getJSONObject(i).getString("name"));
+                }
+                target.getItems().clear();
+                target.getItems().addAll(results);
+                target.show();
+            } catch (IOException e) {
+                showSadMessage(AppConfig.ERROR_API_UNREACHABLE);
+            }
+        }
+    }
+
+
+    /**
+     * Draws a connection between two places. Basically lines.
+     * @param connection The connection to draw
+     * @param x Where to draw
+     * @param y Where to draw but vertically
+     */
+    private void drawConnection(Connection connection, int x, int y){
+        List<Section> sections = connection.getSections();
+        GraphicsContext gcx = connectionCanvas.getGraphicsContext2D();
+        //Draw starting station
+        gcx.setFill(Color.RED);
+        gcx.fillText(sections.get(0).getDeparture().getLocation().getName(), x, y - 20);
+        gcx.setFill(Color.BLACK);
+        gcx.fillText(sections.get(0).getDeparture().getDepartureTime().toString(), x, y - 40);
+        gcx.setFill(Color.RED);
+        gcx.fillOval(x - 5, y - 5, 10, 10);
+        gcx.strokeLine(x, y, x + 622 / (sections.size() + 1), y);
+		for (int i = 0; i < sections.size(); i++){
+            //Draw each step
+            drawConnectionStep(sections.get(i), sections.size(), x, y, i);
+		}
+    }
+    /**
+     * Draws a connection step between two places.
+     * @param section The section to draw
+     * @param size Number of section in the connection
+     * @param x Where to draw
+     * @param y Where to draw but vertically
+     * @param cnt Section position in connection list.
+     */
+    private void drawConnectionStep(Section section, int size, int x, int y, int cnt) {
+        GraphicsContext gcx = connectionCanvas.getGraphicsContext2D();
+        int xSize = (AppConfig.APP_MAIN_VIEW_WIDTH / (size + 1));
+        gcx.strokeLine( x + xSize * (cnt + 1), y, AppConfig.APP_MAIN_VIEW_WIDTH / (size + 1), y);
+        gcx.setFill(Color.RED);
+        gcx.fillOval(x + xSize * (cnt + 1) - 5, y - 5, 10, 10);
+        gcx.fillText(section.getArrival().getLocation().getName(), x + xSize * (cnt + 1), y - 20);
+        gcx.setFill(Color.BLACK);
+        gcx.fillText(section.getArrival().getArrivalTime().toString(), x + xSize * (cnt + 1), y - 40);
+        Journey jrn = section.getJourney();
+            String transportType = "MARCHE"; //Draw the type of transport used on previous step
+            if (!(jrn instanceof Journey.EmptyJourney)) {
+                transportType = jrn.getCategory() + ", " + jrn.getNumber();
+            }
+        gcx.setFill(Color.BLUE);
+        gcx.fillText(transportType,x + xSize * (cnt), y - 55);
+        gcx.setFill(Color.RED);
+    }
+
+    /**
+     * Draws a newly received message
+     * @param message The message to draw
+     */
+    private void drawMessage(User user, String message, String image, Color color){
+        Pane p = new Pane();
+        setChatPanelStyle(p, color);
+        Image lblImg = new Image(Main.class.getResourceAsStream(image));
+        Label msg = new Label();
+        msg.setWrapText(true);
+        msg.setStyle("-fx-padding: 8px");
+        msg.setText(message);
+        msg.setMaxWidth(310);
+        msg.setGraphic(new ImageView(lblImg));
+        p.getChildren().add(msg);
+        if (chatContainer.getChildren().size() >= 8) {
+            chatContainer.getChildren().remove(0);
+        }
+        insertMessageIntoQueue();
+        chatContainer.getChildren().add(p);
+    }
+
+    private void setChatPanelStyle(Pane p, Color color) {
+        p.setBackground(
+            new Background(
+                new BackgroundFill(
+                    new LinearGradient(0, 0, 0, 1, true, 
+                    CycleMethod.NO_CYCLE,
+                    new Stop(1, color), 
+                    new Stop(0, Color.WHITE)
+                    ),
+                new CornerRadii(5), 
+                Insets.EMPTY)));
+        p.setBorder(new Border(new BorderStroke(Color.color(0.6, 0.6, 0.6), BorderStrokeStyle.SOLID, new CornerRadii(5),
+                BorderWidths.DEFAULT)));
+        p.setPrefWidth(312);
+        p.setMaxWidth(320);
+    }
+
+    /**
+     * Moves the older messages downwards
+     */
+    private void insertMessageIntoQueue(){
+        for (int i = 0; i < chatContainer.getChildren().size(); i++){
+            Pane sp = (Pane) chatContainer.getChildren().get(i);
+            TranslateTransition t = new TranslateTransition(Duration.seconds(0.25), sp);
+
+            t.setToY((chatContainer.getChildren().size() - i) * (sp.getHeight() + 4));
+            t.play();
+        }
+    }
+
+    /**
+     * Sets the form up
+     * @param url The JavaFX URL handler
+     * @param resourceBundle The JavaDX ResB handle
+     */
+    @Override
+    public void initialize(URL url, ResourceBundle resourceBundle) {
+        currentJourneyLabel.setText("Vous n'avez prévu aucun voyage pour le moment.");
+        startStopLabel.setText(""); // No text should be visible when no journey has been selected.
+        messageTextBox.textProperty().addListener((ov, oldValue, newValue) -> {
+            if (messageTextBox.getText().length() > 60) {
+                messageTextBox.setText(messageTextBox.getText().substring(0, 60));
+            }
+        });
+
+        LinkAPI transportApi = new LinkAPI();
+        AppContext app = Main.getContext();
+        UiUtils.buttonWhenEnter(originComboBox, searchOriginButton);
+        UiUtils.buttonWhenEnter(destinationComboBox, searchDestinationButton);
+        UiUtils.buttonWhenEnter(messageTextBox, sendMessageButton);
+
+        searchOriginButton.
+                setOnAction(event -> { originComboBox.setValue(originComboBox.getEditor().getText());
+                searchStops(originComboBox.getValue(), transportApi, originComboBox);
+                });
+
+        searchDestinationButton.
+                setOnAction(event -> { destinationComboBox.setValue(destinationComboBox.getEditor().getText());
+                searchStops(destinationComboBox.getValue(), transportApi, destinationComboBox);
+                });
+
+        launchItinaryButton.setOnAction(event -> {
+            try {
+            	connectionCanvas.getGraphicsContext2D()
+						.clearRect(0, 0, connectionCanvas.getWidth(), connectionCanvas.getHeight());
+
+                JSONArray connections = transportApi.getConnections(
+                        originComboBox.getValue(), destinationComboBox.getValue());
+                startStopLabel.setText(originComboBox.getValue() + " - " + destinationComboBox.getValue());
+                for (int i = 0; i < connections.length(); i++){
+                    // Now iterating over connections
+                    Connection c = new Connection.ConnectionBuilder(connections.getJSONObject(i)).build();
+                    drawConnection(c, 30, 100 + 100 * i);
+
+                }
+            } catch (IOException | ParseException e) {
+                showSadMessage(AppConfig.ERROR_API_UNREACHABLE);
+            }
+        });
+
+        sendMessageButton.setOnAction(event -> {
+            if (app.getUser().isPresent() && !messageTextBox.getText().isEmpty()){
+                app.getMessageManager().sendChatMessage(new ChatMessage(app.getUser().get(), messageTextBox.getText()));
+                messageTextBox.clear();
+            }
+        });
+
+        app.getMessageManager().conditionalSubscribeChatMessage(
+            chatMessage -> {
+                Platform.runLater(() -> {
+                    String message = chatMessage.getUser().toString() + ": " + chatMessage.getMessage();
+                    drawMessage(chatMessage.getUser(), message, AppConfig.CHAT_MESSAGE_ICON, AppConfig.COLOR_BLUE_10_OPACITY);
+                });
+            },
+            chatMessage -> !(app.getUser().get().getIgnoredUserList().contains(chatMessage.getUser()))
+        );
+    }
+}
diff --git a/src/main/java/ch/hepia/ui/UiUtils.java b/src/main/java/ch/hepia/ui/UiUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..040ca7672dd3753cc6739f2deed31704e936b434
--- /dev/null
+++ b/src/main/java/ch/hepia/ui/UiUtils.java
@@ -0,0 +1,39 @@
+package ch.hepia.ui;
+
+import javafx.scene.Node;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.input.KeyCode;
+
+/**
+ * Utilities for reusing UI code.
+ */
+public class UiUtils {
+    /**
+     * Shows a dialog box.
+     * @param type The dialog box type
+     * @param title The dialog box title
+     * @param header The dialog box header
+     * @param content The dialog box content
+     */
+    public static void dialog(Alert.AlertType type, String title, String header, String content){
+        Alert alert = new Alert(type);
+        alert.setTitle(title);
+        alert.setHeaderText(header);
+        alert.setContentText(content);
+        alert.show();
+    }
+
+    /**
+     * Fires a button when enter is pressed
+     * @param target The node on where to add the event to
+     * @param button The button to fire
+     */
+    public static void buttonWhenEnter(Node target, Button button){
+        target.setOnKeyPressed(event -> {
+            if (event.getCode().equals(KeyCode.ENTER)){
+                button.fire();
+            }
+        });
+    }
+}
diff --git a/src/main/resources/fxml/MainWindow.fxml b/src/main/resources/fxml/MainWindow.fxml
index df15eceb11d4e6656f83d662e0c1f9ec21ad5d2e..35e9be27c159b0fe217872191b50afd54745e551 100644
--- a/src/main/resources/fxml/MainWindow.fxml
+++ b/src/main/resources/fxml/MainWindow.fxml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
+<?import javafx.scene.canvas.*?>
 <?import javafx.scene.text.*?>
 <?import javafx.scene.paint.*?>
 <?import javafx.scene.shape.*?>
@@ -8,48 +9,59 @@
 <?import javafx.scene.layout.*?>
 <?import javafx.collections.FXCollections?>
 
-<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="565.0" prefWidth="1000.0" style="-fx-background-color: #fafafa;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
-   <top>
-      <Pane maxHeight="48.0" maxWidth="1000.0" minHeight="48.0" minWidth="32.0" prefHeight="48.0" prefWidth="32.0" style="-fx-background-color: #35B1EE;" BorderPane.alignment="CENTER">
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="565.0" prefWidth="1000.0" style="-fx-background-color: #fafafa;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.hepia.ui.MainWindowController">
+    <top>
+        <Pane maxHeight="48.0" maxWidth="1000.0" minHeight="48.0" minWidth="32.0" prefHeight="48.0" prefWidth="32.0" style="-fx-background-color: #35B1EE;" BorderPane.alignment="CENTER">
+            <children>
+                <ComboBox id="originComboBox" fx:id="originComboBox" editable="true" layoutX="12.0" layoutY="10.0" prefHeight="27.0" prefWidth="275.0" promptText="Tapez le nom d'un arrêt..." />
+                <Line endY="48.0" layoutX="411.0" layoutY="-1.0" startY="1.0">
+                    <stroke>
+                        <LinearGradient>
+                            <stops>
+                                <Stop color="#35b1ee" />
+                                <Stop color="#004dff" offset="1.0" />
+                            </stops>
+                        </LinearGradient>
+                    </stroke>
+                </Line>
+                <ComboBox id="destinationComboBox" fx:id="destinationComboBox" editable="true" layoutX="421.0" layoutY="9.0" prefHeight="27.0" prefWidth="282.0" promptText="Choisissez votre destination..." />
+                <Line endY="48.0" layoutX="827.0" layoutY="-1.0" startY="1.0">
+                    <stroke>
+                        <LinearGradient>
+                            <stops>
+                                <Stop color="#35b1ee" />
+                                <Stop color="#004dff" offset="1.0" />
+                            </stops>
+                        </LinearGradient>
+                    </stroke>
+                </Line>
+            <Button id="searchOriginButton" fx:id="searchOriginButton" layoutX="293.0" layoutY="10.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="107.0" text="Chercher" />
+            <Button fx:id="searchDestinationButton" layoutX="711.0" layoutY="9.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="107.0" text="Chercher" />
+            <Button id="launchItinaryButton" fx:id="launchItinaryButton" layoutX="836.0" layoutY="9.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="156.0" text="Itinéraire..." />
+            </children>
+        </Pane>
+    </top>
+    <right>
+        <Pane prefHeight="517.0" prefWidth="326.0" BorderPane.alignment="CENTER">
          <children>
-            <ComboBox id="stopComboBox" fx:id="stopComboBox" editable="true" layoutX="12.0" layoutY="10.0" prefHeight="27.0" prefWidth="275.0" promptText="Tapez le nom d'un arrêt..." />
-            <Line endY="48.0" layoutX="300.0" startY="1.0">
-               <stroke>
-                  <LinearGradient>
-                     <stops>
-                        <Stop color="#35b1ee" />
-                        <Stop color="#004dff" offset="1.0" />
-                     </stops>
-                  </LinearGradient>
-               </stroke>
-            </Line>
-            <ComboBox id="directionComboBox" fx:id="directionComboBox" editable="true" layoutX="310.0" layoutY="10.0" prefHeight="27.0" prefWidth="282.0" promptText="Choisissez votre destination..." />
-            <Line endY="48.0" layoutX="603.0" layoutY="-1.0" startY="1.0">
-               <stroke>
-                  <LinearGradient>
-                     <stops>
-                        <Stop color="#35b1ee" />
-                        <Stop color="#004dff" offset="1.0" />
-                     </stops>
-                  </LinearGradient>
-               </stroke>
-            </Line>
-         </children>
-      </Pane>
-   </top>
-   <right>
-      <Pane prefHeight="517.0" prefWidth="326.0" BorderPane.alignment="CENTER" />
-   </right>
-   <center>
-      <Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
-         <children>
-            <Label id="StopLabel" fx:id="StopLabel" layoutX="14.0" layoutY="14.0" text="Arrêt : CHANGEZ-MOI" textFill="#4c7ba8">
-               <font>
-                  <Font size="28.0" />
-               </font>
-            </Label>
-            <Line endX="100.0" layoutX="115.0" layoutY="49.0" startX="-100.0" stroke="#4c7ba8" />
-         </children>
-      </Pane>
-   </center>
+            <Line endX="326.0" endY="475.0" startY="475.0" stroke="#0000008c" />
+            <TextField id="messageTextBox" fx:id="messageTextBox" layoutX="6.0" layoutY="483.0" prefHeight="27.0" prefWidth="206.0" />
+            <Button id="sendMessageButton" fx:id="sendMessageButton" layoutX="219.0" layoutY="483.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="101.0" text="Envoyer" />
+            <Pane fx:id="chatContainer" layoutX="7.0" layoutY="6.0" prefHeight="465.0" prefWidth="315.0" />
+         </children></Pane>
+    </right>
+    <center>
+        <Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+            <children>
+                <Label id="startStopLabel" fx:id="startStopLabel" layoutX="14.0" layoutY="14.0" text="Arrêt : CHANGEZ-MOI" textFill="#4c7ba8">
+                    <font>
+                        <Font size="28.0" />
+                    </font>
+                </Label>
+                <Line endX="100.0" layoutX="115.0" layoutY="49.0" startX="-100.0" stroke="#4c7ba8" />
+            <Canvas id="connectionCanvas" fx:id="connectionCanvas" height="451.0" layoutX="15.0" layoutY="59.0" width="652.0" />
+                <Label id="currentJourneyLabel" fx:id="currentJourneyLabel" layoutX="15.0" layoutY="59.0" text="CHANGEZ-MOI" textFill="#2600ff" />
+            </children>
+        </Pane>
+    </center>
 </BorderPane>
diff --git a/src/main/resources/img/bubble.png b/src/main/resources/img/bubble.png
new file mode 100644
index 0000000000000000000000000000000000000000..98fcb6b83c43d21ba51d30df9f40b1b90c43769c
Binary files /dev/null and b/src/main/resources/img/bubble.png differ
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000000000000000000000000000000000000..393e0877ec1c2207f866bcf94740760170893486
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,8 @@
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
\ No newline at end of file
diff --git a/src/test/java/ch/hepia/models/UserTest.java b/src/test/java/ch/hepia/models/UserTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b06742b3b3dc7d7f185d2115414f134f23c2b3fc
--- /dev/null
+++ b/src/test/java/ch/hepia/models/UserTest.java
@@ -0,0 +1,13 @@
+package ch.hepia.models;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class UserTest {
+    @Test 
+    void constructorTest() {
+        User u = new User("Hubert-Stanislas");
+        assertEquals(u, new User("Hubert-Stanislas"));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/ch/hepia/mq/MessageTest.java b/src/test/java/ch/hepia/mq/MessageTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..371cc0a12eae483de2b2ccef1d07fddeda5ff7fd
--- /dev/null
+++ b/src/test/java/ch/hepia/mq/MessageTest.java
@@ -0,0 +1,23 @@
+package ch.hepia.mq;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.awt.TrayIcon.MessageType;
+
+import org.junit.jupiter.api.Test;
+
+import ch.hepia.mq.Message.Type;
+import ch.hepia.events.ChatMessage;
+import ch.hepia.models.*;
+class UserTest {
+    User u = new User("Test");
+    ChatMessage cm = new ChatMessage(u, "Bonjour");
+    Message m = new Message(Type.ChatMessage, cm);
+
+    @Test 
+    void ChatMessageTest() {
+        ChatMessage msg = m.getData();
+        assertEquals(u, msg.getUser());
+        assertEquals("Bonjour", msg.getMessage());
+    }
+}
\ No newline at end of file