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:
- Start and Listen: The server starts and listens (binds and listens) on a specific network address for incoming connections.
- Client Connections: Clients connect to the server.
- Receive and Distribute: The server receives messages from clients and broadcasts them to other connected clients.
- Client Disconnections: Clients disconnect.
- Server Stop: The server shuts down.
Client Lifecycle
The client lifecycle mirrors server interaction:
- Start and Connect: The client starts and connects to the server using the server’s network address.
- Send and Receive: The client sends messages to the server and receives messages from the server (which originated from other clients).
- Disconnect: The client disconnects from the server.
- 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:
- Form Elements: Creating and managing form elements such as buttons, text fields, scrollbars, and tables.
- 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.
- Interactivity: Handling user input and events (clicks, key presses, etc.).
- 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.
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