screameth

This commit is contained in:
2025-03-23 19:13:07 -05:00
parent bbee3979d0
commit 3ced22bbdd
13 changed files with 435 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.Socket;
/**
@@ -42,6 +43,19 @@ public class ClientConnect extends JFrame {
public void actionPerformed(ActionEvent actionEvent) {
logger.info("Triggered a Connection request.");
// Connection logic would go here (e.g., retrieve values and initiate connection)
try {
Socket socket = new Socket(getSanitizedText(hostname), Integer.valueOf(getSanitizedText(portNumber)));
logger.info("socket connection has been successfully established by the client");
dispose();
if(socket.isConnected()){
Dashboard dashboard = new Dashboard(socket);
}
//pass a socket with a worker thread to a new variable so that the dashboard can get access to a consumer of messages
//make sure those messages are in turn written to the ServerDaemon socket. The Dashboard will make the messages
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="net.ayyalasomayajula.net.client.Dashboard">
<grid id="27dc6" binding="main" layout-manager="GridLayoutManager" row-count="5" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="d5ffa" class="javax.swing.JTextField" binding="textField1" default-binding="true">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="e594b" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Search Field"/>
</properties>
</component>
<component id="2bac" class="javax.swing.JButton" binding="findEHRButton" default-binding="true">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Find EHR"/>
</properties>
</component>
<component id="674f5" class="javax.swing.JButton" binding="findXrayButton" default-binding="true">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Find xray"/>
</properties>
</component>
<component id="d567f" class="javax.swing.JButton" binding="generateEHRButton" default-binding="true">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Generate EHR"/>
</properties>
</component>
</children>
</grid>
</form>

View File

@@ -0,0 +1,141 @@
package net.ayyalasomayajula.net.client;
import net.ayyalasomayajula.net.shared.Message;
import net.ayyalasomayajula.net.shared.MessageVariant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* The Dashboard class represents the user interface for interacting with the client after a successful connection.
* It provides buttons to trigger different actions (e.g., searching for Electronic Health Records (EHRs), finding X-ray files),
* and sends relevant messages to the server for processing. It also listens for incoming responses from the server.
*/
public class Dashboard extends JFrame {
private static final Logger logger = LoggerFactory.getLogger(Dashboard.class);
private JTextField textField1;
private JPanel main;
private JButton findEHRButton;
private JButton findXrayButton;
private JButton generateEHRButton;
private JTextArea messageDisplayArea;
private Socket connection;
private ObjectOutputStream outputStream;
private ObjectInputStream inputStream;
private List<Message> receivedMessages;
/**
* Constructs a new {@code Dashboard} frame, initializing the GUI components and setting up
* the button actions to send requests to the server. It also starts a background thread to listen
* for incoming messages from the server.
*
* @param connection The active socket connection to the server.
*/
public Dashboard(Socket connection) {
this.connection = connection;
this.receivedMessages = new CopyOnWriteArrayList<>(); // Store messages
try {
this.outputStream = new ObjectOutputStream(connection.getOutputStream());
this.inputStream = new ObjectInputStream(connection.getInputStream());
} catch (IOException e) {
logger.error("Failed to create output/input streams: {}", e.getMessage());
}
// Setup GUI components (buttons, text fields, etc.)
setTitle("Client Dashboard");
setContentPane(main);
setSize(800, 600);
setVisible(true);
// Action listener for "Find EHR" button
findEHRButton.addActionListener(e -> sendMessage("EHR", textField1.getText()));
// Action listener for "Find X-ray" button
findXrayButton.addActionListener(e -> sendMessage("XRAY", textField1.getText()));
// Action listener for "Generate EHR" button
generateEHRButton.addActionListener(e -> generateEHR());
// Start the thread to listen for incoming messages
new Thread(this::listenForMessages).start();
}
/**
* Sends a message to the server with the specified asset type and asset ID.
* The message is wrapped in a {@link Message} object and sent to the server via the output stream.
*
* @param assetType The type of asset (either "EHR" or "XRAY").
* @param assetId The ID of the asset, typically the UUID or path.
*/
private void sendMessage(String assetType, String assetId) {
try {
String query = assetType + " " + assetId;
logger.info("Query: {}", query);
Message message = new Message(MessageVariant.GET, query, 5, new byte[]{});
// Ensure the message is written to the output stream
outputStream.writeObject(message);
outputStream.flush(); // Flush to ensure the message is sent
logger.info("Sent {} request for asset ID: {}", assetType, assetId);
// Wait for a response from the server
Message response = (Message) inputStream.readObject();
updateMessageDisplay(response); // Update the display with the server's response
} catch (IOException | ClassNotFoundException e) {
logger.error("Failed to send message: {}", e.getMessage());
e.printStackTrace();
}
}
/**
* Sends a request to the server to generate an EHR. This simulates the generation of a new EHR.
*/
private void generateEHR() {
try {
String query = "EHR generate";
Message message = new Message(MessageVariant.GET, query, 5, new byte[]{});
outputStream.writeObject(message);
logger.info("Sent request to generate new EHR.");
} catch (IOException e) {
logger.error("Failed to send EHR generation request: {}", e.getMessage());
}
}
/**
* Listens for incoming messages from the server and stores them in the list of received messages.
* It also updates the message display area in the GUI to show new messages.
*/
private void listenForMessages() {
try {
while (true) {
// Read the next message from the server
Message receivedMessage = (Message) inputStream.readObject();
receivedMessages.add(receivedMessage);
updateMessageDisplay(receivedMessage); // Update the GUI with the new message
}
} catch (IOException | ClassNotFoundException e) {
logger.error("Error while listening for messages: {}", e.getMessage());
}
}
/**
* Updates the message display area in the GUI with the received message.
* This method is called whenever a new message is received from the server.
*
* @param receivedMessage The message received from the server.
*/
private void updateMessageDisplay(Message receivedMessage) {
String displayText = "Received: " + receivedMessage.messageQuery() + "\n";
messageDisplayArea.append(displayText); // Append the message to the display area
messageDisplayArea.setCaretPosition(messageDisplayArea.getDocument().getLength()); // Auto-scroll to the bottom
}
}

View File

@@ -0,0 +1,105 @@
package net.ayyalasomayajula.net.server;
import net.ayyalasomayajula.net.shared.EHR;
import java.io.*;
import java.nio.file.*;
import java.util.UUID;
import java.util.Comparator;
import java.util.List;
/**
* Utility class for handling EHR operations: serialization, deserialization, and searching.
*/
public class EHRUtils {
/**
* Constructs the file path for a given EHR based on its UUID.
*
* @param dirPath The base directory for storing EHRs.
* @param ehrUUID The UUID of the EHR.
* @return The full path to the EHR file.
*/
public static Path getEHRFilePath(Path dirPath, UUID ehrUUID) {
return dirPath.resolve(ehrUUID.toString() + ".ehr");
}
/**
* Writes an EHR object to a file.
*
* @param ehr The EHR object to write.
* @param dirPath The directory where the file will be stored.
* @throws IOException If an I/O error occurs.
*/
public static void writeEHRToFile(EHR ehr, Path dirPath) throws IOException {
if (!Files.exists(dirPath)) {
Files.createDirectories(dirPath);
}
Path filePath = getEHRFilePath(dirPath, ehr.getUuid());
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath.toFile()))) {
oos.writeObject(ehr);
}
}
/**
* Reads and deserializes an EHR object from a file.
*
* @param dirPath The directory containing the EHR files.
* @param ehrUUID The UUID of the EHR to retrieve.
* @return The deserialized EHR object, or null if not found.
*/
public static EHR readEHRFromFile(Path dirPath, UUID ehrUUID) {
Path filePath = getEHRFilePath(dirPath, ehrUUID);
if (!Files.exists(filePath)) return null;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath.toFile()))) {
return (EHR) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
return null;
}
}
/**
* Searches for the closest matching EHR based on an asset ID (UUID or patient name).
*
* @param assetId The UUID string or patient name to search for.
* @param dirPath The directory containing serialized EHR files.
* @return The closest matching EHR, or null if none are found.
*/
public static EHR searchClosestEHR(String assetId, Path dirPath) {
File[] ehrFiles = dirPath.toFile().listFiles((dir, name) -> name.endsWith(".ehr"));
if (ehrFiles == null || ehrFiles.length == 0) return null;
return List.of(ehrFiles).stream()
.map(file -> readEHRFromFile(dirPath, UUID.fromString(file.getName().replace(".ehr", ""))))
.filter(ehr -> ehr != null)
.min(Comparator.comparingInt(ehr -> Math.min(
levenshteinDistance(ehr.getUuid().toString(), assetId),
levenshteinDistance(ehr.getPatientName(), assetId)
)))
.orElse(null);
}
/**
* Computes the Levenshtein distance between two strings.
*
* @param s1 First string.
* @param s2 Second string.
* @return The Levenshtein distance between s1 and s2.
*/
private static int levenshteinDistance(String s1, String s2) {
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
for (int i = 0; i <= s1.length(); i++)
for (int j = 0; j <= s2.length(); j++)
if (i == 0) dp[i][j] = j;
else if (j == 0) dp[i][j] = i;
else dp[i][j] = Math.min(dp[i - 1][j - 1] + (s1.charAt(i - 1) == s2.charAt(j - 1) ? 0 : 1),
Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
return dp[s1.length()][s2.length()];
}
}

View File

@@ -1,5 +1,9 @@
package net.ayyalasomayajula.net.server;
import net.ayyalasomayajula.net.shared.EHR;
import net.ayyalasomayajula.net.shared.Message;
import net.ayyalasomayajula.net.shared.MessageVariant;
import net.ayyalasomayajula.net.shared.SerializationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -12,6 +16,8 @@ import java.util.Scanner;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The ServerDaemon class implements a server daemon that listens for incoming client connections.
@@ -19,8 +25,9 @@ import java.util.concurrent.TimeUnit;
* and handles incoming socket connections using a thread pool for concurrency.
*/
public class ServerDaemon implements Runnable {
private static final String patientEhrs = "patients/";
private static final Logger logger = LoggerFactory.getLogger(ServerDaemon.class);
private String basePath = "";
/**
* The entry point of the server daemon that sets up the server, initializes root paths,
* and listens for client connections.
@@ -40,7 +47,7 @@ public class ServerDaemon implements Runnable {
boolean success = initRootPath(path);
if (!success) throw new RuntimeException("Your Path is dysfunctional");
}
basePath=path.toString();
try {
ServerSocket serverSocket = new ServerSocket(8475);
logger.info("Server to handle connections has been made at: {} on 0.0.0.0", serverSocket.getLocalPort());
@@ -55,7 +62,7 @@ public class ServerDaemon implements Runnable {
public void run() {
try {
handleConnections(socketToPass);
} catch (IOException e) {
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@@ -109,7 +116,7 @@ public class ServerDaemon implements Runnable {
}
// Create the patient directory
String patientEhrs = "patients/";
Path patientPath = path.resolve(patientEhrs);
File dir = patientPath.toFile();
@@ -147,10 +154,76 @@ public class ServerDaemon implements Runnable {
* @param socket The socket for the incoming connection.
* @throws IOException If an I/O error occurs while setting up the streams.
*/
private void handleConnections(Socket socket) throws IOException {
private void handleConnections(Socket socket) throws IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.flush(); // Important to flush the stream to avoid issues on the first write
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
// Further processing can be added here
logger.info("Got a handle running on the socket. Will await new message objects.");
Object readBuffer = objectInputStream.readObject();
while (socket.isConnected()) {
if (readBuffer instanceof Message intercepted) {
// Handle the received Message
Message toPush = handleMessage(intercepted);
objectOutputStream.writeObject(toPush);
objectOutputStream.flush(); // Ensure the response is sent
}
// Keep reading from the input stream
readBuffer = objectInputStream.readObject();
}
}
private Message handleMessage(Message in) throws IOException {
switch (in.messageVariant()) {
case GET: {
// Format of GET query:
// <asset_type> <asset_id> | asset types: EHR, XRAY | asset id: uuid field or the path
// Define regex pattern with capture groups
Pattern pattern = Pattern.compile("^(EHR|XRAY) (.+)$");
Matcher matcher = pattern.matcher(in.messageQuery());
if (matcher.matches()) {
String assetType = matcher.group(1); // Captures EHR or XRAY
String assetId = matcher.group(2); // Captures UUID or path
logger.debug("Asset Type: {}", assetType);
logger.debug("Asset ID: {}", assetId);
switch (assetType) {
case "EHR":
logger.info("EHR caught");
EHR found = EHRUtils.searchClosestEHR(assetId, Path.of(basePath));
return new Message(MessageVariant.SET, "", 5, SerializationUtils.toBytes(found));
case "XRAY":
logger.info("XRAY caught");
// Construct full file path for XRAY asset
Path xrayFilePath = Path.of(basePath, assetId);
File xrayFile = xrayFilePath.toFile();
// Check if the file exists and is a valid file
if (xrayFile.exists() && xrayFile.isFile()) {
// Read the file into a byte array and return it
try (FileInputStream fis = new FileInputStream(xrayFile)) {
byte[] fileBytes = fis.readAllBytes();
return new Message(MessageVariant.SET, "", 5, fileBytes);
} catch (IOException e) {
logger.error("Error reading XRAY file: {}", e.getMessage());
return new Message(MessageVariant.SET, "ERROR: Failed to read XRAY file.", 5, new byte[]{});
}
} else {
logger.error("XRAY file not found or invalid path: {}", xrayFilePath);
return new Message(MessageVariant.SET, "ERROR: XRAY file not found.", 5, new byte[]{});
}
}
} else {
logger.debug("No match found for: {}", in.messageQuery());
}
break;
}
}
return new Message(MessageVariant.SET, "THROW;", 5, new byte[]{});
}
}

View File

@@ -1,3 +1,5 @@
package net.ayyalasomayajula.net.shared;
import net.ayyalasomayajula.net.shared.Appointment;
import net.ayyalasomayajula.net.shared.Message;

View File

@@ -0,0 +1,41 @@
package net.ayyalasomayajula.net.shared;
import java.io.*;
/**
* Utility class for serializing and deserializing objects to and from byte arrays.
* This class provides methods for converting an object into a byte array and vice versa.
*/
public class SerializationUtils {
/**
* Serializes an object to a byte array.
*
* @param obj the object to serialize
* @return a byte array representing the serialized object
* @throws IOException if an I/O error occurs during serialization
*/
public static byte[] toBytes(Object obj) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
oos.flush();
return bos.toByteArray();
}
}
/**
* Deserializes a byte array to an object.
*
* @param bytes the byte array to deserialize
* @return the deserialized object
* @throws IOException if an I/O error occurs during deserialization
* @throws ClassNotFoundException if the class of a serialized object cannot be found
*/
public static Object fromBytes(byte[] bytes) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return ois.readObject();
}
}
}

Binary file not shown.