Commit d03afbde authored by Alvin Cheung's avatar Alvin Cheung
Browse files

HW8 released

parent 3fda2926
This diff is collapsed.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FlightService
{
public static final String DBCONFIG_FILENAME = "dbconn.properties";
public static void usage()
{
/* prints the choices for commands and parameters */
System.out.println();
System.out.println(" *** Please enter one of the following commands *** ");
System.out.println("> create <username> <password> <initial amount>");
System.out.println("> login <username> <password>");
System.out.println("> search <origin city> <destination city> <direct> <day of the month> <num itineraries>");
System.out.println("> book <itinerary id>");
System.out.println("> pay <reservation id>");
System.out.println("> reservations");
System.out.println("> cancel <reservation id>");
System.out.println("> quit");
}
public static String[] tokenize(String command)
{
String regex = "\"([^\"]*)\"|(\\S+)";
Matcher m = Pattern.compile(regex).matcher(command);
List<String> tokens = new ArrayList<>();
while (m.find())
{
if (m.group(1) != null)
tokens.add(m.group(1));
else
tokens.add(m.group(2));
}
return tokens.toArray(new String[0]);
}
public static String execute (Query q, String command)
{
String response;
String[] tokens = tokenize(command.trim());
if (tokens.length == 0)
response = "Please enter a command";
else if (tokens[0].equals("login"))
{
if (tokens.length == 3)
{
/* authenticate the user */
String username = tokens[1];
String password = tokens[2];
response = q.transaction_login(username, password);
}
else
response = "Error: Please provide a username and password";
}
else if (tokens[0].equals("create"))
{
/* create a new customer */
if (tokens.length == 4)
{
String username = tokens[1];
String password = tokens[2];
double initAmount = Double.parseDouble(tokens[3]);
response = q.transaction_createCustomer(username, password, initAmount);
}
else
response = "Error: Please provide a username, password, and initial amount in the account";
}
else if (tokens[0].equals("search"))
{
/* search for flights */
if (tokens.length == 6)
{
String originCity = tokens[1];
String destinationCity = tokens[2];
boolean direct = tokens[3].equals("1");
Integer day;
Integer count;
try
{
day = Integer.valueOf(tokens[4]);
count = Integer.valueOf(tokens[5]);
//System.out.println("Searching for flights");
//response = q.transaction_search_unsafe(originCity, destinationCity, direct, day, count);
response = q.transaction_search(originCity, destinationCity, direct, day, count);
}
catch (NumberFormatException e) { response = "Failed to parse integer"; }
}
else
response = "Error: Please provide all search parameters <origin_city> <destination_city> <direct> <date> <nb itineraries>";
}
else if (tokens[0].equals("book"))
{
/* book a flight ticket */
if (tokens.length == 2)
{
int itinerary_id = Integer.parseInt(tokens[1]);
//System.out.println("Booking itinerary.");
response = q.transaction_book(itinerary_id);
}
else
response = "Error: Please provide an itinerary_id";
}
else if (tokens[0].equals("reservations"))
{
/* list all reservations */
response = q.transaction_reservations();
}
else if (tokens[0].equals("pay"))
{
/* pay for an unpaid reservation */
if (tokens.length == 2)
{
int reservation_id = Integer.parseInt(tokens[1]);
//System.out.println("Paying reservation.");
response = q.transaction_pay(reservation_id);
}
else
response = "Error: Please provide a reservation_id";
}
else if (tokens[0].equals("cancel"))
{
/* cancel a reservation */
if (tokens.length == 2)
{
int reservation_id = Integer.parseInt(tokens[1]);
//System.out.println("Canceling reservation.");
response = q.transaction_cancel(reservation_id);
}
else
response = "Error: Please provide a reservation_id";
}
else if (tokens[0].equals("quit"))
response = "Goodbye\n";
else
response = "Error: unrecognized command '" + tokens[0] + "'";
return response;
}
/* REPL (Read-Execute-Print-Loop) */
public static void menu(Query q) throws Exception
{
while (true)
{
usage();
BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
System.out.print("> ");
String command = r.readLine();
String response = execute(q, command);
System.out.print(response);
if (response.equals("Goodbye\n"))
break;
}
}
public static void main(String[] args) throws Exception
{
/* prepare the database connection stuff */
Query q = new Query(DBCONFIG_FILENAME);
q.openConnection();
q.prepareStatements();
menu(q); /* menu(...) does the real work */
q.closeConnection();
}
}
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.assertTrue;
/**
* Autograder for the transaction assignment
*
*/
@RunWith(Parameterized.class)
public class Grader
{
/**
* Models a single user. Callable from a thread.
*/
static class User implements Callable<String>
{
Query q;
List<String> cmds; // commands that this user will execute
List<String> results; // the expected results from those commands
User (List<String> cmds, List<String> results) throws Exception
{
this.q = new Query(FlightService.DBCONFIG_FILENAME);
q.openConnection();
q.prepareStatements();
this.cmds = cmds;
this.results = results;
}
public List<String> results () { return results; }
@Override
public String call ()
{
StringBuffer sb = new StringBuffer();
for (String c : cmds)
sb.append(FlightService.execute(q, c));
return sb.toString();
}
public void shutdown () throws Exception
{
this.q.closeConnection();
}
}
static final String COMMENTS = "#";
static final String DELIMITER = "*"; // delimiter between command inputs and expected results
static final String SEPARATOR = "|"; // delimiter between command inputs and expected results
/**
* Parse the input test case
* @param filename test case's path and file name
* @return new User objects with commands to run and expected results
* @throws Exception
*/
static List<User> parse (String filename) throws Exception
{
List<User> users = new ArrayList<>();
List<String> cmds = new ArrayList<>();
List<String> results = new ArrayList<>();
String r = "";
boolean isCmd = true;
BufferedReader fr = new BufferedReader(new FileReader(filename));
String l;
int lineNumber = 0;
while ( (l = fr.readLine()) != null)
{
lineNumber++;
if (l.startsWith(COMMENTS))
continue;
else if (l.startsWith(DELIMITER))
{
if (isCmd)
isCmd = false;
else
{
results.add(r);
users.add(new User(cmds, results));
cmds = new ArrayList<>();
results = new ArrayList<>();
r = "";
isCmd = true;
}
}
else if (l.startsWith(SEPARATOR))
{
if (isCmd)
throw new Exception("input file is malformatted on line: " + lineNumber);
else
{
results.add(r);
r = "";
}
}
else
{
// remove trailing comments
l = l.split(COMMENTS, 2)[0];
if (isCmd)
cmds.add(l);
else
r = r + l + "\n";
}
}
fr.close();
// everything should be parsed by now and put into user objects
if (cmds.size() > 0 || r.length() > 0 || results.size() > 0)
throw new Exception("input file is malformatted, cmds.size()=" + cmds.size() + ", r.length()=" + r.length() + ", results.size()=" + results.size());
// check that all users have the same number of possible scenarios
int n = users.get(0).results().size();
for (int i = 1; i < users.size(); ++i)
{
int u = users.get(i).results().size();
if (u != n)
throw new Exception("user " + i + " should have " + n + " possible results rather than " + u);
}
return users;
}
// maximum number of concurrent users we will be testing
protected static final int MAX_USERS = 5;
// thread pool used to run different users
protected static ExecutorService pool;
// folder name and path that contains the test cases
protected static String casesFolder;
/**
* Creates the thread pool to execute test cases with multiple users.
* This method is called before the entire test suite is executed.
*/
@BeforeClass
public static void setup()
{
System.out.println("running setup");
pool = Executors.newFixedThreadPool(MAX_USERS);
}
protected String file;
public Grader (String file)
{
this.file = file;
}
@Parameterized.Parameters
public static List<String> files () throws IOException
{
casesFolder = System.getProperty("folder");
if (casesFolder == null || casesFolder.length() == 0)
casesFolder = "cases"; // default, override with -Dfolder=xxx
System.out.println("running cases from: " + casesFolder + "\n");
try (Stream<Path> paths = Files.walk(Paths.get(casesFolder)))
{
return paths.filter(Files::isRegularFile).map(p -> p.toAbsolutePath().toString())
.collect(Collectors.toList());
}
// comment out the above and use this to run individual test cases
//return Arrays.asList( Paths.get("path to case file") );
}
@Before
public void clearDB ()
{
try
{
Query q = new Query(FlightService.DBCONFIG_FILENAME);
q.openConnection();
q.prepareStatements();
q.clearTables();
q.closeConnection();
} catch (Exception e) { e.printStackTrace(); }
}
@Test
public void runTest () throws Exception
{
System.out.println("running test: " + this.file);
List<User> users = parse(this.file);
List<Future<String>> futures = new ArrayList<>();
for (User u : users)
futures.add(pool.submit(u));
List<String> outputs = new ArrayList<>();
for (Future<String> f : futures)
outputs.add(f.get());
boolean passed = false;
// record all possible outcomes to display for debugging
Map<Integer, List<String>> outcomes = new HashMap<Integer, List<String>>();
int n = users.get(0).results().size();
// there are n possible scenarios. Does the returned output correspond to any of them?
for (int i = 0; i < n; ++i)
{
boolean isSame = true;
// check whether the output from all users match outcome i
for (int j = 0; j < users.size(); ++j) {
isSame = isSame && outputs.get(j).equals(users.get(j).results().get(i));
if (!outcomes.containsKey(i)) {
outcomes.put(i, new ArrayList<String>());
}
outcomes.get(i).add(users.get(j).results().get(i));
}
// the test is passed if the output from all users match one of the possible n scenarios.
passed = passed || isSame;
}
if (passed)
System.out.println("passed");
else
System.out.println("failed");
String outcomesFormatted = "";
// if we failed, print the outcomes we were looking for
if (!passed)
{
for (Map.Entry<Integer, List<String>> outcome : outcomes.entrySet())
{
outcomesFormatted += "=====Outcome " + outcome.getKey() + "=====\n";
outcomesFormatted += outcome.getValue().toString() + "\n";
}
}
// print out the returned outputs if test is not passed
assertTrue("Failed: actual outputs for " + this.file + " were: \n" + outputs + "\n\nPossible outcomes were: \n" + outcomesFormatted, passed);
for (User u : users)
u.shutdown();
}
}
import java.io.FileInputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* Runs queries against a back-end database
*/
public class Query
{
private String configFilename;
private Properties configProps = new Properties();
private String jSQLDriver;
private String jSQLUrl;
private String jSQLUser;
private String jSQLPassword;
// DB Connection
private Connection conn;
// Logged In User
private String username; // customer username is unique
// Canned queries
private static final String CHECK_FLIGHT_CAPACITY = "SELECT capacity FROM Flights WHERE fid = ?";
private PreparedStatement checkFlightCapacityStatement;
// transactions
private static final String BEGIN_TRANSACTION_SQL = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION;";
private PreparedStatement beginTransactionStatement;
private static final String COMMIT_SQL = "COMMIT TRANSACTION";
private PreparedStatement commitTransactionStatement;
private static final String ROLLBACK_SQL = "ROLLBACK TRANSACTION";
private PreparedStatement rollbackTransactionStatement;
class Flight
{
public int fid;
public int dayOfMonth;
public String carrierId;
public String flightNum;
public String originCity;
public String destCity;
public int time;
public int capacity;
public int price;
@Override
public String toString()
{
return "ID: " + fid + " Day: " + dayOfMonth + " Carrier: " + carrierId +
" Number: " + flightNum + " Origin: " + originCity + " Dest: " + destCity + " Duration: " + time +
" Capacity: " + capacity + " Price: " + price;
}
}
public Query(String configFilename)
{
this.configFilename = configFilename;
}
/* Connection code to SQL Azure. */
public void openConnection() throws Exception
{
configProps.load(new FileInputStream(configFilename));
jSQLDriver = configProps.getProperty("flightservice.jdbc_driver");
jSQLUrl = configProps.getProperty("flightservice.url");
jSQLUser = configProps.getProperty("flightservice.sqlazure_username");
jSQLPassword = configProps.getProperty("flightservice.sqlazure_password");
/* load jdbc drivers */
Class.forName(jSQLDriver).newInstance();
/* open connections to the flights database */
conn = DriverManager.getConnection(jSQLUrl, // database
jSQLUser, // user
jSQLPassword); // password
conn.setAutoCommit(true); //by default automatically commit after each statement
/* You will also want to appropriately set the transaction's isolation level through:
conn.setTransactionIsolation(...)
See Connection class' JavaDoc for details.
*/
}
public void closeConnection() throws Exception
{
conn.close();
}
/**
* Clear the data in any custom tables created. Do not drop any tables and do not
* clear the flights table. You should clear any tables you use to store reservations
* and reset the next reservation ID to be 1.
*/
public void clearTables ()
{
// your code here
}
/**
* prepare all the SQL statements in this method.
* "preparing" a statement is almost like compiling it.
* Note that the parameters (with ?) are still not filled in
*/
public void prepareStatements() throws Exception
{
beginTransactionStatement = conn.prepareStatement(BEGIN_TRANSACTION_SQL);
commitTransactionStatement = conn.prepareStatement(COMMIT_SQL);
rollbackTransactionStatement = conn.prepareStatement(ROLLBACK_SQL);
checkFlightCapacityStatement = conn.prepareStatement(CHECK_FLIGHT_CAPACITY);
/* add here more prepare statements for all the other queries you need */
/* . . . . . . */
}
/**
* Takes a user's username and password and attempts to log the user in.
*
* @param username
* @param password
*
* @return If someone has already logged in, then return "User already logged in\n"
* For all other errors, return "Login failed\n".
*
* Otherwise, return "Logged in as [username]\n".
*/
public String transaction_login(String username, String password)
{
return "Login failed\n";
}
/**
* Implement the create user function.
*
* @param username new user's username. User names are unique the system.
* @param password new user's password.
* @param initAmount initial amount to deposit into the user's account, should be >= 0 (failure otherwise).
*
* @return either "Created user {@code username}\n" or "Failed to create user\n" if failed.
*/
public String transaction_createCustomer (String username, String password, int initAmount)
{
return "Failed to create user";
}
/**
* Implement the search function.
*
* Searches for flights from the given origin city to the given destination