Fix small bug (ignore if book is on loan or not)

Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
akp 2023-05-01 02:31:54 +01:00
parent 73d797e638
commit 688da71131
No known key found for this signature in database
GPG key ID: AA5726202C8879B7
5 changed files with 15 additions and 553 deletions

View file

@ -82,8 +82,8 @@ public class BooksDatabaseService extends Thread{
String sql = "select book.title, book.publisher, book.genre, book.rrp, count(*) as \"number_available\" from bookcopy join book on bookcopy.bookid = book.bookid where\n" +
"\tlibraryid = (select libraryid from \"library\" where city = ?) and\n" +
"\tbookcopy.bookid in (select bookid from book where authorid = (select authorid from author where familyname = ?)) and\n" +
"\t(onloan = false or onloan is null)\n" +
"\tbookcopy.bookid in (select bookid from book where authorid = (select authorid from author where familyname = ?))" +
// "\t and (onloan = false or onloan is null)\n" +
"group by book.title, book.publisher, book.genre, book.rrp;";
// city then author name

View file

@ -1,301 +0,0 @@
/*
* Client.java
*
* A client for accessing the books database
* A naive JavaFX for connecting to the database server and interact
* with the database.
*
* author: 2430671
*
*/
import java.io.*;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.lang.ClassNotFoundException;
import java.lang.IndexOutOfBoundsException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.sql.*;
import javax.sql.rowset.CachedRowSet;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TextArea;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.stage.Stage;
import javafx.geometry.Insets;
import javafx.collections.ObservableList;
import javafx.collections.FXCollections;
import javafx.beans.property.StringProperty;
import javafx.beans.property.SimpleStringProperty;
public class BooksDatabaseClient extends Application {
public static BooksDatabaseClient me; //Get the application instance in javafx
public static Stage thePrimaryStage; //Get the application primary scene in javafx
private Socket clientSocket = null;
private String userCommand = null; //The user command
private CachedRowSet serviceOutcome = null; //The service outcome
//Convenient to populate the TableView
public class MyTableRecord {
private StringProperty title;
private StringProperty publisher;
private StringProperty genre;
private StringProperty rrp;
private StringProperty copyID;
public void setTitle(String value) { titleProperty().set(value); }
public String getTitle() { return titleProperty().get(); }
public void setPublisher(String value) { publisherProperty().set(value); }
public String getPublisher() { return publisherProperty().get(); }
public void setGenre(String value) { genreProperty().set(value); }
public String getGenre() { return genreProperty().get(); }
public void setRrp(String value) { rrpProperty().set(value); }
public String getRrp() { return rrpProperty().get(); }
public void setCopyID(String value) { copyIDProperty().set(value); }
public String getCopyID() { return copyIDProperty().get(); }
public StringProperty titleProperty() {
if (title == null)
title = new SimpleStringProperty(this, "");
return title;
}
public StringProperty publisherProperty() {
if (publisher == null)
publisher = new SimpleStringProperty(this, "");
return publisher;
}
public StringProperty genreProperty() {
if (genre == null)
genre = new SimpleStringProperty(this, "");
return genre;
}
public StringProperty rrpProperty() {
if (rrp == null)
rrp = new SimpleStringProperty(this, "");
return rrp;
}
public StringProperty copyIDProperty() {
if (copyID == null)
copyID = new SimpleStringProperty(this, "");
return copyID;
}
}
//Class Constructor
public BooksDatabaseClient(){
me=this;
}
//Initializes the client socket using the credentials from class Credentials.
public void initializeSocket(){
try {
clientSocket = new Socket(InetAddress.getByName(Credentials.HOST), Credentials.PORT);
} catch (Exception e) {
System.err.println("cannot open socket: " + e);
System.exit(1);
}
}
public void requestService() {
try {
System.out.println("Client: Requesting books database service for user command\n" + this.userCommand +"\n");
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
out.print(userCommand + "#");
out.flush();
}catch(IOException e){
System.out.println("Client: I/O error. " + e);
}
}
public void reportServiceOutcome() {
try {
ObjectInputStream outcomeStreamReader = new ObjectInputStream(clientSocket.getInputStream());
serviceOutcome = (CachedRowSet) outcomeStreamReader.readObject();
LinkedList<MyTableRecord> r = new LinkedList<>();
while (serviceOutcome.next()) {
MyTableRecord tr = new MyTableRecord();
tr.setTitle(serviceOutcome.getString(1));
tr.setPublisher(serviceOutcome.getString(2));
tr.setGenre(serviceOutcome.getString(3));
tr.setRrp(serviceOutcome.getString(4));
tr.setCopyID(serviceOutcome.getString(5));
r.add(tr);
}
TableView<MyTableRecord> view = (TableView<MyTableRecord>) ((GridPane) thePrimaryStage.getScene().getRoot()).getChildren().get(5);
view.setItems(FXCollections.observableList(r));
System.out.println(r +"\n====================================\n");
}catch(IOException e){
System.out.println("Client: I/O error. " + e);
}catch(ClassNotFoundException e){
System.out.println("Client: Unable to cast read object to CachedRowSet. " + e);
}catch(SQLException e){
System.out.println("Client: Can't retrieve requested attribute from result set. " + e);
}
}
//Execute client
public void execute(){
GridPane grid = (GridPane) thePrimaryStage.getScene().getRoot();
ObservableList<Node> childrens = grid.getChildren();
TextField authorInputBox = (TextField) childrens.get(1);
TextField libraryInputBox = (TextField) childrens.get(3);
//Build user message command
userCommand = authorInputBox.getText() + ";" + libraryInputBox.getText();
//Request service
try{
//Initializes the socket
this.initializeSocket();
//Request service
this.requestService();
//Report user outcome of service
this.reportServiceOutcome();
//Close the connection with the server
this.clientSocket.close();
}catch(Exception e)
{// Raised if connection is refused or other technical issue
System.out.println("Client: Exception " + e);
}
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Books Database Client");
//Create a GridPane container
GridPane grid = new GridPane();
grid.setPadding(new Insets(10, 10, 10, 10));
grid.setVgap(5);
grid.setHgap(5);
//Add the input boxes
Label authorLabel = new Label("Author's Surname:");
GridPane.setConstraints(authorLabel, 0, 0);
grid.getChildren().add(authorLabel);
TextField authorInputBox = new TextField ();
authorInputBox.setPromptText("Author's Surname:");
authorInputBox.setPrefColumnCount(30);
GridPane.setConstraints(authorInputBox, 1, 0);
grid.getChildren().add(authorInputBox);
Label libraryLabel = new Label("Library's city:");
GridPane.setConstraints(libraryLabel, 0, 1);
grid.getChildren().add(libraryLabel);
TextField libraryInputBox = new TextField ();
libraryInputBox.setPromptText("Library's city:");
libraryInputBox.setPrefColumnCount(30);
GridPane.setConstraints(libraryInputBox, 1, 1);
grid.getChildren().add(libraryInputBox);
//Add the service request button
Button btn = new Button();
btn.setText("Request Books Database Service");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
me.execute();
}
});
GridPane.setConstraints(btn, 0, 2, 2, 1);
grid.getChildren().add(btn);
//Add the output box
TableView<MyTableRecord> outputBox = new TableView<MyTableRecord>();
TableColumn<MyTableRecord,String> titleCol = new TableColumn<MyTableRecord,String>("Title");
TableColumn<MyTableRecord,String> publisherCol = new TableColumn<MyTableRecord,String>("Publisher");
TableColumn<MyTableRecord,String> genreCol = new TableColumn<MyTableRecord,String>("Genre");
TableColumn<MyTableRecord,String> rrpCol = new TableColumn<MyTableRecord,String>("RRP");
TableColumn<MyTableRecord,String> copyIDCol = new TableColumn<MyTableRecord,String>("Num. Copies");
titleCol.setCellValueFactory(new PropertyValueFactory("title"));
publisherCol.setCellValueFactory(new PropertyValueFactory("publisher"));
genreCol.setCellValueFactory(new PropertyValueFactory("genre"));
rrpCol.setCellValueFactory(new PropertyValueFactory("rrp"));
copyIDCol.setCellValueFactory(new PropertyValueFactory("copyID"));
@SuppressWarnings("unchecked") ObservableList<TableColumn<MyTableRecord,?>> tmp = outputBox.getColumns();
tmp.addAll(titleCol, publisherCol, genreCol, rrpCol, copyIDCol);
//Leaving this type unchecked by now... It may be convenient to compile with -Xlint:unchecked for details.
GridPane.setConstraints(outputBox, 0, 3, 2, 1);
grid.getChildren().add(outputBox);
//Adjust gridPane's columns width
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(25);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(75);
grid.getColumnConstraints().addAll( col1, col2);
primaryStage.setScene(new Scene(grid, 505, 505));
primaryStage.show();
thePrimaryStage = primaryStage;
}
public static void main (String[] args) {
launch(args);
System.out.println("Client: Finished.");
System.exit(0);
}
}

View file

@ -1,85 +0,0 @@
/*
* BooksDatabaseServer.java
*
* The server main class.
* This server provides a service to access the Books database.
*
* author: 2430671
*
*/
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
public class BooksDatabaseServer {
private int thePort = 0;
private String theIPAddress = null;
private ServerSocket serverSocket = null;
//Support for closing the server
//private boolean keypressedFlag = false;
//Class constructor
public BooksDatabaseServer(){
//Initialize the TCP socket
thePort = Credentials.PORT;
theIPAddress = Credentials.HOST;
//Initialize the socket and runs the service loop
System.out.println("Server: Initializing server socket at " + theIPAddress + " with listening port " + thePort);
System.out.println("Server: Exit server application by pressing Ctrl+C (Windows or Linux) or Opt-Cmd-Shift-Esc (Mac OSX)." );
try {
//Initialize the socket
serverSocket = new ServerSocket(thePort, 1, InetAddress.getByName(theIPAddress));
System.out.println("Server: Server at " + theIPAddress + " is listening on port : " + thePort);
} catch (Exception e){
//The creation of the server socket can cause several exceptions;
//See https://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html
System.out.println(e);
System.exit(1);
}
}
//Runs the service loop
public void executeServiceLoop()
{
System.out.println("Server: Start service loop.");
try {
//Service loop
while (true) {
Socket sock = serverSocket.accept();
new BooksDatabaseService(sock);
}
} catch (Exception e){
//The creation of the server socket can cause several exceptions;
//See https://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html
System.out.println(e);
}
System.out.println("Server: Finished service loop.");
}
/*
@Override
protected void finalize() {
//If this server has to be killed by the launcher with destroyForcibly
//make sure we also kill the service threads.
System.exit(0);
}
*/
public static void main(String[] args){
//Run the server
BooksDatabaseServer server=new BooksDatabaseServer(); //inc. Initializing the socket
server.executeServiceLoop();
System.out.println("Server: Finished.");
System.exit(0);
}
}

View file

@ -1,165 +0,0 @@
/*
* BooksDatabaseService.java
*
* The service threads for the books database server.
* This class implements the database access service, i.e. opens a JDBC connection
* to the database, makes and retrieves the query, and sends back the result.
*
* author: 2430671
*
*/
import java.io.*;
//import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Arrays;
import java.util.Properties;
import java.util.StringTokenizer;
import java.sql.*;
import javax.sql.rowset.*;
//Direct import of the classes CachedRowSet and CachedRowSetImpl will fail becuase
//these clasess are not exported by the module. Instead, one needs to impor
//javax.sql.rowset.* as above.
public class BooksDatabaseService extends Thread{
private Socket serviceSocket = null;
private String[] requestStr = new String[2]; //One slot for author's name and one for library's name.
private ResultSet outcome = null;
//JDBC connection
private String USERNAME = Credentials.USERNAME;
private String PASSWORD = Credentials.PASSWORD;
private String URL = Credentials.URL;
//Class constructor
public BooksDatabaseService(Socket aSocket){
serviceSocket = aSocket;
this.start();
}
//Retrieve the request from the socket
public String[] retrieveRequest()
{
StringBuilder sb = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(serviceSocket.getInputStream()));
int c = 0;
while (c != '#') {
c = in.read();
if (c == 0) {
System.out.println("reading null bytes");
System.exit(1);
}
sb.append((char) c);
}
}catch(IOException e){
System.out.println("Service thread " + this.getId() + ": " + e);
}
String inp = sb.toString();
this.requestStr = inp.substring(0, inp.length() - 1).split(";");
// returns [0]: author, [1]: city
return this.requestStr;
}
//Parse the request command and execute the query
public boolean attendRequest()
{
boolean flagRequestAttended = true;
this.outcome = null;
String sql = "select book.title, book.publisher, book.genre, book.rrp, count(*) as \"number_available\" from bookcopy join book on bookcopy.bookid = book.bookid where\n" +
"\tlibraryid = (select libraryid from \"library\" where city = ?) and\n" +
"\tbookcopy.bookid in (select bookid from book where authorid = (select authorid from author where familyname = ?)) and\n" +
"\t(onloan = false or onloan is null)\n" +
"group by book.title, book.publisher, book.genre, book.rrp;";
// city then author name
try {
//Connet to the database
Properties connectionProps = new Properties();
connectionProps.put("user", USERNAME);
connectionProps.put("password", PASSWORD);
Connection conn = DriverManager.getConnection(URL, connectionProps);
//Make the query
PreparedStatement q = conn.prepareStatement(sql);
q.setString(1, this.requestStr[1]); // city
q.setString(2, this.requestStr[0]); // author surname
//Process query
ResultSet res = q.executeQuery();
RowSetFactory aFactory = RowSetProvider.newFactory() ;
CachedRowSet crs = aFactory.createCachedRowSet();
crs.populate(res);
outcome = crs;
//Clean up
res.close();
q.close();
conn.close();
} catch (Exception e)
{ System.out.println(e); }
return flagRequestAttended;
}
//Wrap and return service outcome
public void returnServiceOutcome(){
try {
//Return outcome
ObjectOutputStream output = new ObjectOutputStream(serviceSocket.getOutputStream());
output.writeObject(outcome);
System.out.println("Service thread " + this.getId() + ": Service outcome returned; " + this.outcome);
//Terminating connection of the service socket
output.close();
serviceSocket.close();
}catch (IOException e){
System.out.println("Service thread " + this.getId() + ": " + e);
}
}
//The service thread run() method
public void run()
{
try {
System.out.println("\n============================================\n");
//Retrieve the service request from the socket
this.retrieveRequest();
System.out.println("Service thread " + this.getId() + ": Request retrieved: "
+ "author->" + this.requestStr[0] + "; library->" + this.requestStr[1]);
//Attend the request
boolean tmp = this.attendRequest();
//Send back the outcome of the request
if (!tmp)
System.out.println("Service thread " + this.getId() + ": Unable to provide service.");
this.returnServiceOutcome();
}catch (Exception e){
System.out.println("Service thread " + this.getId() + ": " + e);
}
//Terminate service thread (by exiting run() method)
System.out.println("Service thread " + this.getId() + ": Finished service.");
}
}

View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -ex
OUTPUT_FILENAME="fsad-assignment4-2430671.zip"
TEMP_DIR=$(mktemp --directory --tmpdir=.)
cp ../src/main/java/net/akpain/BooksDatabase*.java $TEMP_DIR
sed -i -- 's/package net.akpain;//g' $TEMP_DIR/*
zip -j0 fsad-assignment4-2430671.zip $TEMP_DIR/*
rm -rf $TEMP_DIR