first commit
This commit is contained in:
39
week2_TinsaeGhilay/Task2/.gitignore
vendored
Normal file
39
week2_TinsaeGhilay/Task2/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
.kotlin
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
8
week2_TinsaeGhilay/Task2/.idea/.gitignore
generated
vendored
Normal file
8
week2_TinsaeGhilay/Task2/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
7
week2_TinsaeGhilay/Task2/.idea/encodings.xml
generated
Normal file
7
week2_TinsaeGhilay/Task2/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
15
week2_TinsaeGhilay/Task2/.idea/misc.xml
generated
Normal file
15
week2_TinsaeGhilay/Task2/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21 (3)" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
week2_TinsaeGhilay/Task2/.idea/vcs.xml
generated
Normal file
6
week2_TinsaeGhilay/Task2/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
34
week2_TinsaeGhilay/Task2/pom.xml
Normal file
34
week2_TinsaeGhilay/Task2/pom.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.distributed</groupId>
|
||||||
|
<artifactId>Task2</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.activemq</groupId>
|
||||||
|
<artifactId>activemq-all</artifactId>
|
||||||
|
<version>5.18.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>2.23.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||||
|
<version>2.23.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package org.distributed;
|
||||||
|
/**
|
||||||
|
* ChatHandler.java
|
||||||
|
* by Tinsae Ghilay
|
||||||
|
* Handles chat session, message sending and receiving using JMS queues.
|
||||||
|
* Uses ActiveMQ as the message broker.
|
||||||
|
* done for Distributed Systems Course - Week 2 Task 2
|
||||||
|
* Date: June 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
import javax.jms.*;
|
||||||
|
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||||
|
|
||||||
|
// ChatHandler class to manage chat sessions
|
||||||
|
public class ChatHandler {
|
||||||
|
|
||||||
|
// chatting ids
|
||||||
|
private String userId, corespondent;
|
||||||
|
private Connection connection;
|
||||||
|
private Session session;
|
||||||
|
int status = 0; // 0: offline, 1: online
|
||||||
|
|
||||||
|
// Initialize connection and session
|
||||||
|
public void initializeSession() throws JMSException {
|
||||||
|
String BROKER_URL = "tcp://localhost:61616";
|
||||||
|
ConnectionFactory factory = new ActiveMQConnectionFactory(BROKER_URL);
|
||||||
|
this.connection = factory.createConnection();
|
||||||
|
this.connection.start();
|
||||||
|
this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start continuous conversation with multiple receivers
|
||||||
|
public void startConversation(Scanner sc) throws JMSException {
|
||||||
|
|
||||||
|
// Initialize all producers and consumers
|
||||||
|
Thread t = setupReceiver();
|
||||||
|
|
||||||
|
// Continuous input loop
|
||||||
|
try {
|
||||||
|
while (sc.hasNextLine()) {
|
||||||
|
String line = sc.nextLine();
|
||||||
|
|
||||||
|
// Send message to receiver
|
||||||
|
sendMessage(createProducer(), line);
|
||||||
|
|
||||||
|
// Exit condition
|
||||||
|
if (line.equalsIgnoreCase("exit")) {
|
||||||
|
System.out.println("Exiting conversation...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error in continuous conversation: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
t.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup producer and consumer for a receiver
|
||||||
|
private Thread setupReceiver() throws JMSException {
|
||||||
|
|
||||||
|
MessageConsumer consumer = createConsumer();
|
||||||
|
|
||||||
|
return getThread(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create producer queue for sending messages
|
||||||
|
private MessageProducer createProducer() throws JMSException {
|
||||||
|
String from = "queue_" + userId;
|
||||||
|
Queue prod = session.createQueue(from);
|
||||||
|
return session.createProducer(prod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create consumer queue for receiving messages
|
||||||
|
private MessageConsumer createConsumer() throws JMSException {
|
||||||
|
String to = "queue_" + getCorespondent();
|
||||||
|
Queue cons = session.createQueue(to);
|
||||||
|
return session.createConsumer(cons);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a message to the producer
|
||||||
|
private void sendMessage(MessageProducer producer, String line) throws JMSException {
|
||||||
|
TextMessage m = session.createTextMessage(line);
|
||||||
|
m.setStringProperty("sender", userId);
|
||||||
|
producer.send(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close connection and session
|
||||||
|
public void closeSession() throws JMSException {
|
||||||
|
if (session != null) {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
if (connection != null) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a thread object to handle receiving messages
|
||||||
|
private Thread getThread(MessageConsumer consumer) {
|
||||||
|
Thread receivingThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
// continously listen for messages
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
Message msg = consumer.receive(300);
|
||||||
|
if (msg instanceof TextMessage tm) {
|
||||||
|
|
||||||
|
// Status handling for online/offline
|
||||||
|
if(tm.getText().isBlank() || tm.getText().isEmpty()) {
|
||||||
|
setStatus(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit condition because correspondent went offline
|
||||||
|
if(tm.getText().equalsIgnoreCase("exit")) {
|
||||||
|
setStatus(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display received message
|
||||||
|
String sender = tm.getStringProperty("sender");
|
||||||
|
String body = tm.getText();
|
||||||
|
System.out.println("[" + sender + "]: " + body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (javax.jms.JMSException e) {
|
||||||
|
// Silently handle interruption - normal shutdown behavior
|
||||||
|
if (!Thread.currentThread().isInterrupted()) {
|
||||||
|
System.err.println("Error receiving message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
receivingThread.start();
|
||||||
|
// we are returning this so we can interrupt it later
|
||||||
|
return receivingThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for user ID
|
||||||
|
public void promptForUser(Scanner sc){
|
||||||
|
|
||||||
|
System.out.print("Enter your user ID: ");
|
||||||
|
setUserId(prompt(sc));
|
||||||
|
// Instructions
|
||||||
|
System.out.print("Welcome "+getUserId());
|
||||||
|
System.out.println(" -> Type 'exit' to quit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for correspondent ID
|
||||||
|
public void promptForCorespondent(Scanner sc){
|
||||||
|
|
||||||
|
System.out.print("Who do you want to chat with? ");
|
||||||
|
setCorespondent(prompt(sc));
|
||||||
|
System.out.println("You can chat with: " + getCorespondent());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic prompt method prompts for whatever we need
|
||||||
|
private String prompt(Scanner sc) {
|
||||||
|
|
||||||
|
String user = "";
|
||||||
|
|
||||||
|
while (user.isEmpty()) {
|
||||||
|
try {
|
||||||
|
user = sc.next();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error reading correspondent ID: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters and getters
|
||||||
|
// user
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// correspondent
|
||||||
|
public String getCorespondent() {
|
||||||
|
return corespondent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorespondent(String corespondent) {
|
||||||
|
this.corespondent = corespondent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update status of correspondent and print status message
|
||||||
|
private void setStatus(int status) {
|
||||||
|
|
||||||
|
// just incase of duplicate status messages, if status is same, return
|
||||||
|
if(this.status == status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.status = status;
|
||||||
|
if(status == 0) {
|
||||||
|
System.out.println(getCorespondent()+" is now offline.");
|
||||||
|
} else {
|
||||||
|
System.out.println(getCorespondent()+" is now online.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package org.distributed;
|
||||||
|
/**
|
||||||
|
* Main.java
|
||||||
|
* by Tinsae Ghilay
|
||||||
|
* Entry point for the chat application.
|
||||||
|
* done for Distributed Systems Course - Week 2 Task 2
|
||||||
|
* Date: June 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
// entry point
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
// create a client
|
||||||
|
ChatHandler handler = new ChatHandler();
|
||||||
|
|
||||||
|
// init connection and session session
|
||||||
|
handler.initializeSession();
|
||||||
|
|
||||||
|
// create scanner for user input
|
||||||
|
Scanner sc = new Scanner(System.in);
|
||||||
|
|
||||||
|
// log in user
|
||||||
|
handler.promptForUser(sc);
|
||||||
|
|
||||||
|
// specify correspondent
|
||||||
|
handler.promptForCorespondent(sc);
|
||||||
|
|
||||||
|
// start conversation
|
||||||
|
handler.startConversation(sc);
|
||||||
|
|
||||||
|
// close session after all receivers are processed
|
||||||
|
handler.closeSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
39
week2_TinsaeGhilay/Task3/.gitignore
vendored
Normal file
39
week2_TinsaeGhilay/Task3/.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
.kotlin
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/modules.xml
|
||||||
|
.idea/jarRepositories.xml
|
||||||
|
.idea/compiler.xml
|
||||||
|
.idea/libraries/
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
||||||
8
week2_TinsaeGhilay/Task3/.idea/.gitignore
generated
vendored
Normal file
8
week2_TinsaeGhilay/Task3/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
7
week2_TinsaeGhilay/Task3/.idea/encodings.xml
generated
Normal file
7
week2_TinsaeGhilay/Task3/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
week2_TinsaeGhilay/Task3/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
week2_TinsaeGhilay/Task3/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,java.util.Map,remove" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
15
week2_TinsaeGhilay/Task3/.idea/misc.xml
generated
Normal file
15
week2_TinsaeGhilay/Task3/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21 (2)" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
week2_TinsaeGhilay/Task3/.idea/vcs.xml
generated
Normal file
6
week2_TinsaeGhilay/Task3/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
34
week2_TinsaeGhilay/Task3/pom.xml
Normal file
34
week2_TinsaeGhilay/Task3/pom.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.distributed</groupId>
|
||||||
|
<artifactId>Task3</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.activemq</groupId>
|
||||||
|
<artifactId>activemq-all</artifactId>
|
||||||
|
<version>5.18.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-core</artifactId>
|
||||||
|
<version>2.23.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||||
|
<version>2.23.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,396 @@
|
|||||||
|
package org.distributed;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import javax.jms.*;
|
||||||
|
import org.apache.activemq.ActiveMQConnectionFactory;
|
||||||
|
|
||||||
|
public class ChatHandler {
|
||||||
|
|
||||||
|
private String userId, corespondent;
|
||||||
|
private Connection connection;
|
||||||
|
private Session /*session,*/ senderSession, receiverSession;
|
||||||
|
private int status = 0; // 0: offline, 1: online
|
||||||
|
private final Map<String, MessageProducer> blocked = new ConcurrentHashMap<>();
|
||||||
|
private final String TAG = "Task3_queue_";
|
||||||
|
private final String BROKER_URL = "tcp://localhost:61616";
|
||||||
|
/**
|
||||||
|
* Initialize JMS session
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
public void initializeSession() throws JMSException {
|
||||||
|
ConnectionFactory factory = new ActiveMQConnectionFactory(BROKER_URL);
|
||||||
|
this.connection = factory.createConnection();
|
||||||
|
this.connection.start();
|
||||||
|
//this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
this.senderSession = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
this.receiverSession = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start conversation loop
|
||||||
|
* @param sc
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
public void startConversation(Scanner sc) throws JMSException {
|
||||||
|
|
||||||
|
// init receiving thread
|
||||||
|
Thread t = setupReceiver(userId);
|
||||||
|
|
||||||
|
// Continuous input loop
|
||||||
|
try {
|
||||||
|
while (sc.hasNextLine()){
|
||||||
|
|
||||||
|
// handle user input and send messages
|
||||||
|
// or if exit command, break loop
|
||||||
|
if(!respond(sc)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error in continuous conversation: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
t.interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle user input and send messages
|
||||||
|
* @param sc
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
private boolean respond(Scanner sc) throws JMSException {
|
||||||
|
String line = sc.nextLine();
|
||||||
|
|
||||||
|
// commands to block or unblock contact
|
||||||
|
if(line.startsWith("-")){
|
||||||
|
delegateBlockingManagement(sc,line);
|
||||||
|
//return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.equalsIgnoreCase("exit")) {
|
||||||
|
System.out.println("Exiting conversation...");
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message to receiver
|
||||||
|
sendMessage(createProducer(corespondent), line);
|
||||||
|
return !line.equalsIgnoreCase("exit");//true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate blocking and unblocking management
|
||||||
|
* @param sc
|
||||||
|
* @param line
|
||||||
|
*/
|
||||||
|
private void delegateBlockingManagement(Scanner sc, String line) {
|
||||||
|
|
||||||
|
// block command is -b followed by contact id
|
||||||
|
if(line.startsWith("-b")){
|
||||||
|
blockContact(line);
|
||||||
|
promptForCorespondent(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unblock command is -u followed by contact id
|
||||||
|
if(line.startsWith("-u")){
|
||||||
|
String contact = unBlockContact(line);
|
||||||
|
System.out.println("do you want to chat with: " + contact+" yes or no");
|
||||||
|
while(true){
|
||||||
|
String answer = sc.nextLine();
|
||||||
|
if(answer.equalsIgnoreCase("yes")){
|
||||||
|
setCorespondent(contact);
|
||||||
|
break;
|
||||||
|
} else if(answer.equalsIgnoreCase("no")){
|
||||||
|
promptForCorespondent(sc);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
System.out.println("please answer with yes or no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup producer and consumer for a receiver
|
||||||
|
private Thread setupReceiver(String receiver) throws JMSException {
|
||||||
|
MessageConsumer contact = createConsumer();
|
||||||
|
|
||||||
|
// add consumer to contacts map
|
||||||
|
//contacts.put(receiver, contact);
|
||||||
|
return getThread(contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a producer for a given contact ID so we can send messages
|
||||||
|
* @param id
|
||||||
|
* @return MessageProducer
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
private MessageProducer createProducer(String id) throws JMSException {
|
||||||
|
String from = TAG + id;
|
||||||
|
Queue prod = senderSession.createQueue(from);
|
||||||
|
return senderSession.createProducer(prod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block a contact
|
||||||
|
* @param command
|
||||||
|
*/
|
||||||
|
private void blockContact(String command) {
|
||||||
|
// remove the command part to get the contact id
|
||||||
|
String contact = command.replace("-b", "").trim();
|
||||||
|
try {
|
||||||
|
MessageProducer ignored = createProducer(contact);
|
||||||
|
// remove from contacts map
|
||||||
|
// and close his ass
|
||||||
|
if(ignored != null){
|
||||||
|
ignored.close();
|
||||||
|
blocked.put(contact, ignored);
|
||||||
|
System.out.println(contact + " has been blocked.");
|
||||||
|
}
|
||||||
|
} catch (JMSException e) {
|
||||||
|
System.err.println("Error unblocking consumer: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock a contact
|
||||||
|
* @param command
|
||||||
|
* @return contact id
|
||||||
|
*/
|
||||||
|
private String unBlockContact(String command) {
|
||||||
|
|
||||||
|
String contact = command.replace("-u", "").trim();
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
// unblock by removing his ass from blocklist
|
||||||
|
blocked.remove(contact);
|
||||||
|
// retun contact name for further handling
|
||||||
|
return contact;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error blocking consumer: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to topics meant for this user
|
||||||
|
* @return MessageConsumer
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
private MessageConsumer createConsumer() throws JMSException {
|
||||||
|
String to = TAG + userId;
|
||||||
|
Queue cons = receiverSession.createQueue(to);
|
||||||
|
return receiverSession.createConsumer(cons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a messag
|
||||||
|
* @param producer
|
||||||
|
* @param line
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
private void sendMessage(MessageProducer producer, String line) throws JMSException {
|
||||||
|
|
||||||
|
TextMessage m = senderSession.createTextMessage(line);
|
||||||
|
// add sender identity to message
|
||||||
|
m.setStringProperty("sender", userId);
|
||||||
|
producer.send(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close session and connection
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
public void closeSession() throws JMSException {
|
||||||
|
if (senderSession != null) {
|
||||||
|
senderSession.close();
|
||||||
|
}
|
||||||
|
if( receiverSession != null) {
|
||||||
|
receiverSession.close();
|
||||||
|
}
|
||||||
|
if (connection != null) {
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a thread object to handle receiving messages
|
||||||
|
* @param consumer
|
||||||
|
* @return Thread
|
||||||
|
*/
|
||||||
|
private Thread getThread(MessageConsumer consumer) {
|
||||||
|
|
||||||
|
Thread receivingThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
|
|
||||||
|
// receive message with timeout
|
||||||
|
Message msg = consumer.receive(100); // 100ms timeout
|
||||||
|
if (msg instanceof TextMessage tm) {
|
||||||
|
// handle received message
|
||||||
|
String sender = tm.getStringProperty("sender");
|
||||||
|
|
||||||
|
// display message if sender is not blocked
|
||||||
|
if (!isBlocked(sender)) {
|
||||||
|
String body = tm.getText();
|
||||||
|
displayMessage(sender, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (javax.jms.JMSException e) {
|
||||||
|
// Silently handle interruption - normal shutdown behavior
|
||||||
|
if (!Thread.currentThread().isInterrupted()) {
|
||||||
|
System.err.println("Error receiving message: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
receivingThread.start();
|
||||||
|
// we are returning this so we can interrupt it later
|
||||||
|
return receivingThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display received message
|
||||||
|
* Checks if sender is blocked
|
||||||
|
* checks if message is empty for status update
|
||||||
|
* checks for exit condition
|
||||||
|
* @param tm
|
||||||
|
* @throws JMSException
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void displayMessage(String sender, String body) {
|
||||||
|
|
||||||
|
|
||||||
|
// Exit condition because correspondent went offline
|
||||||
|
if(body.equalsIgnoreCase("exit")) {
|
||||||
|
setStatus(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// display only if not empty
|
||||||
|
if(!body.isBlank() || !body.isEmpty()) {
|
||||||
|
// Status handling for online/offline
|
||||||
|
System.out.println("[" + sender + "]: " + body);
|
||||||
|
}
|
||||||
|
setStatus(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if sender is blocked
|
||||||
|
* if sender is blocked, print info message
|
||||||
|
* @param sender
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean isBlocked(String sender) {
|
||||||
|
boolean isBlocked = blocked.containsKey(sender);
|
||||||
|
if(isBlocked){
|
||||||
|
System.out.println(" >> Message from '"+sender+"' ignored. because user is blocked.");
|
||||||
|
}
|
||||||
|
return isBlocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt for user ID
|
||||||
|
* @param sc
|
||||||
|
*/
|
||||||
|
public void promptForUser(Scanner sc){
|
||||||
|
|
||||||
|
System.out.print("Enter your user ID: ");
|
||||||
|
//userId = prompt(sc);
|
||||||
|
setUserId(prompt(sc));
|
||||||
|
// Instructions
|
||||||
|
System.out.print("Welcome "+getUserId());
|
||||||
|
System.out.println(" -> Type 'exit' to quit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt for correspondent ID
|
||||||
|
* @param sc
|
||||||
|
*/
|
||||||
|
public void promptForCorespondent(Scanner sc){
|
||||||
|
|
||||||
|
System.out.print("Who do you want to chat with? ");
|
||||||
|
String contact = prompt(sc);
|
||||||
|
setCorespondent(contact);
|
||||||
|
// check if contact is blocked
|
||||||
|
if(blocked.containsKey(contact)){
|
||||||
|
System.out.println(contact + " is currently blocked. Unblock to chat.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic prompt method prompts for whatever we need
|
||||||
|
* @param sc Scanner object
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
private String prompt(Scanner sc) {
|
||||||
|
|
||||||
|
String user = "";
|
||||||
|
|
||||||
|
while (user.isEmpty()) {
|
||||||
|
try {
|
||||||
|
user = sc.next();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Error reading correspondent ID: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters and getters
|
||||||
|
// user
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// correspondent
|
||||||
|
public String getCorespondent() {
|
||||||
|
return corespondent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorespondent(String corespondent) {
|
||||||
|
this.corespondent = corespondent;
|
||||||
|
System.out.println("You can chat with: " + corespondent);
|
||||||
|
// reset old status when new corepondent is set
|
||||||
|
setStatus(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates corespondents status
|
||||||
|
* @param status
|
||||||
|
*/
|
||||||
|
private void setStatus(int status) {
|
||||||
|
|
||||||
|
// just in case of duplicate status messages, if status is same, return
|
||||||
|
if(this.status == status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update status
|
||||||
|
this.status = status;
|
||||||
|
|
||||||
|
// and print accordingly
|
||||||
|
if(status == 0) {
|
||||||
|
System.out.println(getCorespondent()+" is now offline.");
|
||||||
|
} else {
|
||||||
|
System.out.println(getCorespondent()+" is now online.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package org.distributed;
|
||||||
|
import java.util.Scanner;
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
// entry point
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
|
// create a client
|
||||||
|
ChatHandler handler = new ChatHandler();
|
||||||
|
|
||||||
|
// init session
|
||||||
|
handler.initializeSession();
|
||||||
|
|
||||||
|
// create scanner for user input
|
||||||
|
Scanner sc = new Scanner(System.in);
|
||||||
|
|
||||||
|
// log in user
|
||||||
|
handler.promptForUser(sc);
|
||||||
|
|
||||||
|
// specify correspondent
|
||||||
|
handler.promptForCorespondent(sc);
|
||||||
|
|
||||||
|
// start conversation
|
||||||
|
handler.startConversation(sc);
|
||||||
|
|
||||||
|
// close session after all receivers are processed
|
||||||
|
handler.closeSession();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user