Fix small bug (ignore if book is on loan or not)
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
73d797e638
commit
688da71131
5 changed files with 15 additions and 553 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
|
||||
}
|
13
assignment4/submission/generate.sh
Normal file
13
assignment4/submission/generate.sh
Normal 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
|
Reference in a new issue