Lecture from: 17.12.2024 | Video: Videos ETHZ

Chat Application Example

This section explores the implementation of a simple chat application in Java, demonstrating several key programming concepts and the use of external libraries.

Chat Application Overview

The chat application allows multiple users to connect to a central server and exchange text messages. The functionality includes user login, sending and receiving messages, and user presence notifications (e.g. join/leave messages).

Shows two chat clients connected to the same server.

Client-Server Architecture

The chat application uses a client-server architecture:

  • Clients Connect to Server: All chat clients establish a connection with a central server.
  • Server as Intermediary: Clients communicate only with the server, not directly with each other.
  • Server Broadcasts Messages: The server receives messages from clients and distributes (broadcasts) them to all other connected clients.
  • Alternative Architectures: Other architectures, like peer-to-peer (where clients communicate directly), are also possible but not explored here.

Concepts and Implementation

The core concepts and their implementation in the chat application are:

  • Messages (Nachrichten): Represented as String objects.
  • Client and Server as Java Programs: Both the client and the server are implemented as Java programs.
  • Events and Actions: The programs react to events (e.g., incoming messages, user actions) by invoking appropriate methods.

Server Lifecycle

The lifecycle of the chat server:

  1. Start and Listen: The server starts and listens (binds and listens) on a specific network address for incoming connections.
  2. Client Connections: Clients connect to the server.
  3. Receive and Distribute: The server receives messages from clients and broadcasts them to other connected clients.
  4. Client Disconnections: Clients disconnect.
  5. Server Stop: The server shuts down.

Client Lifecycle

The client lifecycle mirrors server interaction:

  1. Start and Connect: The client starts and connects to the server using the server’s network address.
  2. Send and Receive: The client sends messages to the server and receives messages from the server (which originated from other clients).
  3. Disconnect: The client disconnects from the server.
  4. Client Stop: The client application terminates.

Technologies Used in the Demo

The chat demo uses several technologies:

  • GUI (Graphical User Interface): Built using Swing (a Java GUI toolkit).
  • Message Handling (Nachrichten): Uses (de)serialization to convert messages (objects) to strings and vice-versa for network transmission.
  • Networking (Netzwerk): Employs WebSockets for real-time communication between clients and server.
  • Libraries (Bibliotheken): Utilizes dependency management tools to handle external libraries.

You can download the project here: https://lec.inf.ethz.ch/infk/eprog/2024/downloads/chat-project.zip

Graphical User Interfaces (GUIs)

There are various approaches to creating GUIs:

  • Java:
    • AWT/Swing: An older but still widely used toolkit included in Java’s standard library. AWT is the Abstract Window Toolkit, Swing is a GUI widget toolkit built on top of AWT.
    • JavaFX: A newer, more modern UI toolkit but it is not part of Java’s core library and may depend on the operating system.
  • HTML: Can be used for cross-platform GUIs rendered in web browsers.
  • Other Languages: Python, C++, etc., typically have their own GUI libraries/frameworks.

Common GUI Challenges

Building GUIs often involves addressing these challenges:

  1. Form Elements: Creating and managing form elements such as buttons, text fields, scrollbars, and tables.
  2. Layout: Arranging elements in a visually appealing and user-friendly way. This includes ensuring that interface components are displayed in a clear and organized manner. The layout should also be adaptive to various window sizes and screen resolutions, automatically adjusting the positioning and sizing of elements to maintain a consistent and usable interface.
  3. Interactivity: Handling user input and events (clicks, key presses, etc.).
  4. Styling: Applying visual styles and themes to enhance the look and feel of the interface. This can be achieved through platform specific tools or style libraries.

Form Elements and Layout with Swing: Example

Swing provides various layout managers to control the arrangement of components.

Layout Managers in Swing: BorderLayout and GridBagLayout

Swing offers several layout managers (at least eight). Here’s an illustration of BorderLayout (used in the previous example) and GridBagLayout:

Layout: IDE Support

Some IDEs (like Eclipse and IntelliJ) have visual GUI builders that significantly simplify the process of designing and laying out user interfaces. This What You See Is What You Get (WYSIWYG) functionality allows for easier modification of form elements and layout properties.

Docs: https://eclipse.dev/windowbuilder/

Interactivity in Swing

Swing handles interactivity using event listeners.

// Click on "Send" button -> create new message, send to server
sendButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent event) {  //Method called when the button is clicked
        Message message = new Message(messageField.getText());
        server.send(message);
 
        // Clear the message text field
        messageField.setText("");
    }
});

The addActionListener() method (anonymous method/lambda function) registers a listener that responds to button clicks. Within the listener, a new Message is created and sent to the server, and message field is cleared.

Similar Concepts in Other Languages

GUI concepts are similar across different languages. For example, layout managers like those in Java Swing find parallels in HTML/CSS frameworks. The code below demonstrates how to react to events in JavaScript.

(De)Serialization of Objects

Serialization is the process of converting an object into a stream of bytes, typically for storage or transmission over a network. Deserialization is the reverse process, reconstructing an object from its serialized form (the stream of bytes). This is crucial for sending objects (messages) in a chat application over the network. This mechanism is used for storing and transmitting objects in the chat application, where messages are converted into strings for sending over the network.

Serialization and Deserialization in the Chat Application

In the chat application, messages (objects) are serialized into strings on the server-side before being sent over the network. On the client-side, these strings are deserialized back into message objects.

Key Ideas in Serialization

  • Object to String: Serialization converts the object’s state (its attributes and their values) into a string representation. The string representation can be chosen freely.
  • Dynamic String Generation: The format of the generated string is dependent on the object’s type (its class and the types of its attributes). The actual class of the object (its dynamic type) determines what gets serialized, leading to various outputs. This enables flexible serialization based on the object’s specific attributes. This string needs to include information about the object’s type and its attributes, so the object can be properly reconstructed during deserialization. JSON is a popular and often used format, particularly for web applications.
  • String Tagging (JSON): To handle different message subtypes, the serialized string needs to encode the object’s type. This is typically done by including a field that identifies the type (e.g., "kind": "ChatMessage" in JSON format) so that the appropriate object can be instantiated on deserialization. The string generated often utilizes JSON for easy transfer over networks.

Simplified (De)Serialization Example

This illustrates a simplified serialization/deserialization mechanism using JSON. Libraries like Gson or Jackson are used in real applications for robust JSON handling.

WebSockets

WebSockets provide a full-duplex (bi-directional), persistent connection between a web browser and a server, enabling real-time communication. They are widely used in applications that require live data updates, such as the chat program or multiplayer games.

Network Protocols as a Layered Architecture

Network protocols are often organized in layers, each handling a specific aspect of communication. This layered architecture provides abstraction and modularity, allowing changes or improvements to one layer without affecting others. An example model is the OSI (Open Systems Interconnection) model shown below. The OSI Model consists of 7 layers, which are from top to bottom: Application, Presentation, Session, Transport, Network, Data Link and Physical.

Excerpt from the OSI Model
  • Application Layer: Where applications (like web browsers, email clients, and our chat application) interact with the network. Protocols at this layer include HTTP, SMTP, and WebSocket.
  • Transport Layer: Provides reliable data transfer between applications. Protocols include TCP (Transmission Control Protocol) and UDP (User Datagram Protocol).
  • Network Layer: Handles routing of data packets across networks. The main protocol here is IP (Internet Protocol).
  • Link Layer: Deals with the physical transmission of data over a specific medium (e.g., Ethernet cable, Wi-Fi).

Our chat application uses the WebSocket protocol at the application layer.

Server-Side WebSocket Code (Client is similar)

This code snippet shows how the chat server handles new client connections and incoming messages using WebSockets. Libraries, such as org.java_websocket, provide framework code for WebSocket specific operations.

Dependency Management

Modern software projects often rely on external libraries to provide functionality that would be complex or time-consuming to implement from scratch. Managing these external dependencies (libraries) effectively is crucial for building and maintaining complex applications, particularly in larger collaborative projects. This process is known as dependency management.

Standard Libraries vs. External Libraries

  • Standard Libraries: Java’s standard library includes many useful classes (e.g., Random, String, collections). However, it can’t cover every possible need, as software development continuously evolves. Standard libraries offer core, widely used components, but large-scale projects often benefit from specialized components found in external libraries, allowing more efficient development.
  • External Libraries (Open-Source or Closed-Source): Offer additional functionality beyond the standard library. They facilitate code reuse, allowing you to focus on your specific project logic rather than reinventing the wheel.

Standard libraries offer a curated collection of common utilities. For special needs, you often incorporate external libraries. Managing them efficiently is crucial.

Advantages and Disadvantages of Using External Libraries

  • Advantages:
    • Division of Labor: You can concentrate on your project’s core logic while leveraging the work of others for common tasks.
    • Reusability: Libraries promote code reuse, reducing development time and effort. Once implemented and tested, this functionality can be reliably reused, reducing code duplication and maintenance efforts.
  • Disadvantages:
    • Dependencies: Your project becomes dependent on external code.
    • Maintenance and Updates: You might need to update libraries as new versions are released or encounter conflicts if your project depends on various versions of the same library. This may result in additional maintenance work and compatibility issues.

Dependency Management: Manual/Local Approach

One way to manage dependencies is to manually download and include library files (.jar files, Java Archives) in your project.

  • Self-Contained Project: This makes the project self-contained and easy to distribute. Since all libraries are located locally within the project, execution is straightforward.

  • Disadvantages:

    • Increased Project Size: Library files can make the project large, especially if you have many dependencies.
    • Version Conflicts: Manually managing library versions can be challenging, especially with transitive dependencies where libraries may depend on other libraries, and different versions cause conflicts. This can introduce compatibility issues that can be hard to detect and resolve.

Dependency Management: Using Tools

Modern projects often use dependency management tools like Maven, Gradle, or Ivy.

  • Core Idea: Your project configuration file lists the required libraries (and their versions). The build tool automatically downloads and manages these libraries for you, resolving transitive dependencies and ensuring consistent versioning.

  • Advantages:

    • Automated Dependency Resolution: Simplifies the process of including and updating dependencies.
    • Centralized Repositories: Libraries are downloaded from a central repository (or a company-specific one) hence only downloaded once, and project sizes remain small.
    • Version Management: Tools handle version conflicts and updates efficiently. Centralized repositories help maintain consistency by storing only one instance of each library version.
  • Disadvantages:

    • Added Complexity: Requires learning and configuring the build tool.
    • External Infrastructure: Your project now depends on the build tool and the repository, so the project is not directly executable without the build tool and a connection to the repository.

The image shows an example of a Maven dependency declaration for org.java-websocket. It highlights the organized and standardized manner in which dependency information is presented. This structure makes it easy for build tools like Maven to locate and resolve these dependencies.

Build Tools

Dependency management is only one aspect of build tools. They automate many tasks in the software development lifecycle, including:

  • Compilation
  • Testing
  • Packaging
  • Deployment
  • Documentation generation

Popular build tools include Ant, Maven, and Gradle. Popular dependency management tools are Maven and Ivy. IDEs often integrate with build tools.

Introspection and Reflection

Introspection and reflection allow programs to inspect and modify their own structure and behavior at runtime. This powerful functionality enables:

  • dynamic behavior at runtime
  • analysis and modification of internal states and structures

  • Introspection: Inspecting the structure and properties of objects at runtime, like their attributes, methods, and type.

  • Reflection: Modifying the structure or behavior of objects at runtime, like accessing/modifying private members or creating new instances dynamically.

“Introspection and reflection are the abilities of a system to inspect and modify itself at runtime, respectively.” - Wikipedia

Project Link: https://lec.inf.ethz.ch/infk/eprog/2024/downloads/reflection-project.zip

Applications of Reflection

Reflection is a powerful but complex technique with several uses, like de/serialization discussed earlier, but also:

  • (De)Serialization: Converting objects to/from string representations, as seen in the chat example.
  • Plugins/Extensions: Loading and integrating external plugins or extensions dynamically into an application. This allows for extended functionality without recompiling the main application, contributing to increased versatility.
  • Stubbing/Mocking: Creating mock or stub objects for testing, enhancing the testing process by simplifying the simulation of various scenarios.

Serialization with Reflection

The serialize() method in the chat application used reflection (but omitted from code and delegated to ...). Here’s how it could be done:

This code can serialize any Message subtype without needing to know the specific type at compile time.

Plugins with Reflection

Reflection can be used to implement a plugin system.

The application can dynamically load and use different PasswordChecker plugins without needing to know about them at compile time. Kinda insane but cool though…

Conclusion: Chat Application

The chat application demonstrates several concepts learned in the course, plus libraries for GUIs and WebSockets, including dependency management and reflection.

The code for the demo is available on the course website (https://lec.inf.ethz.ch/infk/eprog/2024).

Continue here: 26 Memory Management, Stack & Heap, Garbage Collection