diff options
Diffstat (limited to 'src/uk/org/ury/backend/database')
9 files changed, 498 insertions, 0 deletions
diff --git a/src/uk/org/ury/backend/database/DatabaseDriver.java b/src/uk/org/ury/backend/database/DatabaseDriver.java new file mode 100644 index 0000000..5cd9b98 --- /dev/null +++ b/src/uk/org/ury/backend/database/DatabaseDriver.java @@ -0,0 +1,166 @@ +package uk.org.ury.backend.database; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import uk.org.ury.backend.config.ConfigReader; +import uk.org.ury.backend.database.exceptions.ConnectionFailureException; +import uk.org.ury.backend.database.exceptions.MissingCredentialsException; + +/** + * A database connection manager that connects to the URY databases using + * suitably privileged accounts, and handles the processing of SQL queries. + * + * @author Matt Windsor + * @author Nathan Lasseter + * + */ +public class DatabaseDriver { + /* The JDBC path used to connect to the URY database. */ + private String DATABASE_PATH = "jdbc:postgresql://"; + + /* The database connection. */ + private Connection conn; + + /** + * Construct a new DatabaseDriver with the given user class. + * + * @param config + * The config with login details. + * + * @param type + * The user class to log in to the database with. + * + * @throws IllegalArgumentException + * if the user class is not supported (this should not happen). + * + * @throws MissingCredentialsException + * if the user class login credentials could not be loaded. + * + * @throws ConnectionFailureException + * if the database backend failed to connect to the database + * server. + */ + public DatabaseDriver(ConfigReader config, UserClass type) + throws MissingCredentialsException, ConnectionFailureException { + try { + connect(config, type); + } catch (SQLException e) { + throw new ConnectionFailureException(e.getMessage()); + } + + } + + /** + * Connect to the URY database. + * + * @param config + * The config to use for the connection. + * @param type + * The access level of the connection + * + * @throws SQLException + * if the database connection failed. + */ + private void connect(ConfigReader config, UserClass type) + throws SQLException { + if (config == null) + throw new IllegalArgumentException("Supplied null config."); + + if (config.getDatabase().getHost() == null) + throw new IllegalArgumentException("config has no associated host."); + + if (config.getDatabase().getDb() == null) + throw new IllegalArgumentException( + "config has no associated database."); + + DATABASE_PATH = DATABASE_PATH + config.getDatabase().getHost() + "/" + + config.getDatabase().getDb(); + + if (type == UserClass.READ_ONLY) { + if (config.getRoAuth().getUser() == null) + throw new IllegalArgumentException( + "config has no associated username."); + if (config.getRoAuth().getPass() == null) + throw new IllegalArgumentException( + "config has no associated password."); + conn = DriverManager.getConnection(DATABASE_PATH, config + .getRoAuth().getUser(), config.getRoAuth().getPass()); + } else if (type == UserClass.READ_WRITE) { + if (config.getRwAuth().getUser() == null) + throw new IllegalArgumentException( + "config has no associated username."); + if (config.getRwAuth().getPass() == null) + throw new IllegalArgumentException( + "config has no associated password."); + conn = DriverManager.getConnection(DATABASE_PATH, config + .getRwAuth().getUser(), config.getRwAuth().getPass()); + } + } + + /** + * Execute an unprepared SQL statement with no arguments. + * + * @param sql + * The SQL statement to execute. + * @param fetchSize + * The maximum number of query rows to return. + * + * @return the JDBC results set. + * + * @throws SQLException + * if a SQL error occurs. + */ + public ResultSet executeQuery(String sql, int fetchSize) + throws SQLException { + Statement st = conn.createStatement(); + st.setFetchSize(fetchSize); + + return st.executeQuery(sql); + } + + /** + * Perform a SQL statement with arguments. + * + * This accepts an array of parameter objects, which must each either be + * String or Integer objects. The objects will be used sequentially to fill + * in '?' placeholders in the query text. + * + * @param sql + * The SQL statement to execute. + * @param params + * A list of parameter objects. + * @param fetchSize + * The maximum number of query rows to return. + * + * @return the set of results from the query. + * + * @throws IllegalArgumentException + * if any of the parameters is unsupported by the database as a + * statement parameter. + * + * @throws SQLException + * if a SQL error occurs. + */ + public ResultSet executeQuery(String sql, Object[] params, int fetchSize) + throws SQLException { + PreparedStatement st = conn.prepareStatement(sql); + + st.setFetchSize(fetchSize); + + for (int i = 0; i < params.length; i++) + if (params[i] instanceof String) + st.setString(i + 1, (String) params[i]); + else if (params[i] instanceof Integer) + st.setInt(i + 1, (Integer) params[i]); + else + throw new IllegalArgumentException("Unsupported parameter #" + + (i + 1)); + + return st.executeQuery(); + } +} diff --git a/src/uk/org/ury/backend/database/DatabaseItem.java b/src/uk/org/ury/backend/database/DatabaseItem.java new file mode 100644 index 0000000..b25e9f2 --- /dev/null +++ b/src/uk/org/ury/backend/database/DatabaseItem.java @@ -0,0 +1,80 @@ +package uk.org.ury.backend.database; + +import java.util.HashMap; +import java.util.Map; + +import uk.org.ury.backend.database.exceptions.MissingPropertyException; + +/** + * An abstract class presenting a template for objects serving as a data + * structure for collections of properties retrieved from a SQL database. + * + * @param E + * The enumeration type used as the property list. + * + * @param T + * The type of datum stored for each property. + * + * @author Matt Windsor + */ +public abstract class DatabaseItem<E, T> { + private Map<E, T> properties; + + /** + * Construct a new item from an existing list of properties. + * + * @param properties + * The map of properties that the new item will inherit. + */ + public DatabaseItem(Map<E, T> properties) { + this.properties = properties; + } + + /** + * Check whether a property has been set in the item. + * + * @return true if the property has been set; false otherwise. + */ + + public boolean has(E property) { + return properties.containsKey(property); + } + + /** + * Query this item for a property. + * + * @param property + * The property to query. + * + * @return The property, if it exists. + * + * @throws MissingPropertyException + * if the property does not exist. + */ + public T get(E property) throws MissingPropertyException { + if (properties.containsKey(property)) + return properties.get(property); + else + throw new MissingPropertyException(property.toString()); + } + + /** + * Retrieve a map of string representations of the properties. + * + * This relies on E and T having meaningful toString methods. + * + * @return a list of lines representing the response. + */ + public Map<String, String> asResponse() { + // TODO: Fan out implementation details into separate class + Map<String, String> response = new HashMap<String, String>(); + + for (E property : properties.keySet()) { + if (properties.get(property) != null) + response.put(property.toString(), properties.get(property) + .toString()); + } + + return response; + } +} diff --git a/src/uk/org/ury/backend/database/UserClass.java b/src/uk/org/ury/backend/database/UserClass.java new file mode 100644 index 0000000..1cd39fc --- /dev/null +++ b/src/uk/org/ury/backend/database/UserClass.java @@ -0,0 +1,38 @@ +/** + * + */ +package uk.org.ury.backend.database; + +/** + * The various user classes of the database driver. + * + * These refer to various users in the database proper, and thus grant various + * levels of permission to the program. + * + * Please use the least privileged user class that works. For most cases, + * READ_ONLY should work perfectly. + * + * @author Matt Windsor + * + */ + +public enum UserClass { + // Constant configName + READ_ONLY ("read_only"), + READ_WRITE ("read_write"); + + /** + * The name of the tag in the configuration file that contains the + * credentials for this user class. + */ + public String configName; + + /** + * Constructs a new UserClass. + * + * @param configName The name of the user class in the config. + */ + private UserClass(String configName) { + this.configName = configName; + } +} diff --git a/src/uk/org/ury/backend/database/exceptions/ConnectionFailureException.java b/src/uk/org/ury/backend/database/exceptions/ConnectionFailureException.java new file mode 100644 index 0000000..393f3ce --- /dev/null +++ b/src/uk/org/ury/backend/database/exceptions/ConnectionFailureException.java @@ -0,0 +1,44 @@ +/** + * + */ +package uk.org.ury.backend.database.exceptions; + +/** + * Exception thrown when the database backend fails to connect to + * the database server, in absence of a more specific exception. + * + * @author Matt Windsor + */ + +public class ConnectionFailureException extends Exception +{ + /** + * + */ + private static final long serialVersionUID = -7353531873142099828L; + + +/** + * Construct a new ConnectionFailureException with a + * default reason. + */ + + public + ConnectionFailureException () + { + super ("Connection failure."); + } + + + /** + * Construct a new ConnectionFailureException. + * + * @param reason The explanation for the exception. + */ + + public + ConnectionFailureException (String reason) + { + super (reason); + } +} diff --git a/src/uk/org/ury/backend/database/exceptions/MissingCredentialsException.java b/src/uk/org/ury/backend/database/exceptions/MissingCredentialsException.java new file mode 100644 index 0000000..4c73683 --- /dev/null +++ b/src/uk/org/ury/backend/database/exceptions/MissingCredentialsException.java @@ -0,0 +1,52 @@ +/** + * + */ +package uk.org.ury.backend.database.exceptions; + +/** + * Exception thrown when the database credentials required to + * log into the URY database under a user class are missing, + * and thus the log-in cannot continue. + * + * The best practice for handling a MissingCredentialsException + * is to attempt to log into the database with a less privileged + * user class or, if the credentials for read-only access are + * missing, give up. + * + * @author Matt Windsor + */ + +public class MissingCredentialsException extends Exception +{ + + /** + * + */ + + private static final long serialVersionUID = -397479334359858162L; + + + /** + * Construct a new MissingCredentialsException with a + * default reason. + */ + + public + MissingCredentialsException () + { + super ("Missing credentials."); + } + + + /** + * Construct a new MissingCredentialsException. + * + * @param reason The explanation for the exception. + */ + + public + MissingCredentialsException (String reason) + { + super (reason); + } +} diff --git a/src/uk/org/ury/backend/database/exceptions/MissingPropertyException.java b/src/uk/org/ury/backend/database/exceptions/MissingPropertyException.java new file mode 100644 index 0000000..485771b --- /dev/null +++ b/src/uk/org/ury/backend/database/exceptions/MissingPropertyException.java @@ -0,0 +1,47 @@ +/** + * + */ +package uk.org.ury.backend.database.exceptions; + + +/** + * Exception thrown when a DatabaseItem is queried for a property + * that does not exist. + * + * This is (usually) not a fatal error. + * + * @author Matt Windsor + */ + +public class MissingPropertyException extends Exception +{ + /** + * + */ + private static final long serialVersionUID = -7353531873142099828L; + + +/** + * Construct a new MissingPropertyException with a + * default reason. + */ + + public + MissingPropertyException () + { + super ("Query failure."); + } + + + /** + * Construct a new MissingPropertyException. + * + * @param reason The explanation for the exception. + */ + + public + MissingPropertyException (String reason) + { + super (reason); + } +} diff --git a/src/uk/org/ury/backend/database/exceptions/QueryFailureException.java b/src/uk/org/ury/backend/database/exceptions/QueryFailureException.java new file mode 100644 index 0000000..6c01e61 --- /dev/null +++ b/src/uk/org/ury/backend/database/exceptions/QueryFailureException.java @@ -0,0 +1,45 @@ +/** + * + */ +package uk.org.ury.backend.database.exceptions; + + +/** + * Exception thrown when the database backend fails to execute + * a query. + * + * @author Matt Windsor + */ + +public class QueryFailureException extends Exception +{ + /** + * + */ + private static final long serialVersionUID = -7353531873142099828L; + + +/** + * Construct a new QueryFailureException with a + * default reason. + */ + + public + QueryFailureException () + { + super ("Query failure."); + } + + + /** + * Construct a new QueryFailureException. + * + * @param reason The explanation for the exception. + */ + + public + QueryFailureException (String reason) + { + super (reason); + } +} diff --git a/src/uk/org/ury/backend/database/exceptions/package.html b/src/uk/org/ury/backend/database/exceptions/package.html new file mode 100644 index 0000000..b5e7cac --- /dev/null +++ b/src/uk/org/ury/backend/database/exceptions/package.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" +"http://www.w3.org/TR/html4/strict.dtd"> + +<HTML> + <HEAD> + <TITLE>uk.org.ury.database.exceptions</TITLE> + </HEAD> + <BODY> + <P>Exceptions thrown by the database services classes.</P> + </BODY> +</HTML>
\ No newline at end of file diff --git a/src/uk/org/ury/backend/database/package.html b/src/uk/org/ury/backend/database/package.html new file mode 100644 index 0000000..2b138b8 --- /dev/null +++ b/src/uk/org/ury/backend/database/package.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" +"http://www.w3.org/TR/html4/strict.dtd"> + +<HTML> + <HEAD> + <TITLE>uk.org.ury.database</TITLE> + </HEAD> + <BODY> + <P>Database services for the URY Presenter Suite.</P> + <P>The classes provided within this package are expected to be + used by the back-end through utility classes, and <EM>not</EM> + by the frontend, which should use the server API to indirectly + query the database.</P> + </BODY> +</HTML>
\ No newline at end of file |