Finally
The "finally" clause is for cleaning up resources and not for continued processing. Java does not prevent your kind of usage but their are some assertions to the finally clause your code does not make use of. So it can be placed in the try-block.
try with resource
Use the try with resource construct when loading the properties from the file. This ensures proper resource closing. In your case "input.close()" will never be called if an exception happens before. So you will introduce a memory leak.
Extract method
You should separate the concerns of loading the properties and connecting to the database.
Exception handling
I suggest to throw all exceptions instead of handling it. Maybe you should wrap it to a custom exception that is not signature relevant (RuntimeException).
The alternative you provided leaves the caller in doubt why he gets null. I guess you currently check for null by the caller when the getConnection()-emtoh gets called. Instead of checking for null you should handle the exception there where some code has to decide what to if no connection could be established.
Avoid null and not useful checks
I suggest to remap missing values to a type specific neutral element (e.g.):
this.hostname = prop.getProperty("hostname", "" /*default value*/);
Furthermore do not check things that other code will check anyway (DriverManager.getConnection). So omit
if (this.database != null)
Encapsulate connection information
Introduce a new structure that represents the information necessary to establish a connection and use it to be independent of "retreiving the data from a properties object" that provides no semantic.
Variable scope
You can reduce the scope of the connection information variables to "method". Once you are connected they will be present within the connection. And you never reinitialize the "conn"-variable.
Extract constants
You have a lot of Strings that should be extracted as constants
String templates
Not neccessarily objective but using template strings will have influence how you express String composition.
Naming
As you are currently supporting a MySQL Database I suggest to rename "Database" to "MySQLDatabase".
Separate concerns of process and output
If you introduce a callback or an observer mechanism you separate the console output from the real doing. You are no longer tied to a console output. You can easily change console output to log file output.
Refactored code
public class MySQLDatabase {
private static final String DATABASE_PROPERTIES_FILENAME = "database.properties";
private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
private static final String MYSQL_CONNECTION_STRING_TEMPLATE = "jdbc:mysql://%1$s/%2$s?user=%3$s&password=%4$s";
private static final String KEY_PASSWORD = "password";
private static final String KEY_USERNAME = "username";
private static final String KEY_DATABASE = "database";
private static final String KEY_HOSTNAME = "hostname";
private Connection conn;
private Callback callback;
public MySQLDatabase(Callback callback) {
this.callback = callback;
}
private static class ConnectionInfo {
private String hostname;
private String database;
private String username;
private String password;
public ConnectionInfo(String hostname, String database, String username, String password) {
this.hostname = hostname;
this.database = database;
this.username = username;
this.password = password;
}
public String getHostname() {
return hostname;
}
public String getDatabase() {
return database;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
public Connection getConnection() {
if (conn == null) {
try {
ConnectionInfo connectionInfo = getConnectionInfo();
callback.onConnectingToDatabase();
Class.forName(DRIVER_CLASS_NAME);
String mySQLConnectionString = String.format(MYSQL_CONNECTION_STRING_TEMPLATE,
connectionInfo.getHostname(),
connectionInfo.getDatabase(),
connectionInfo.getUsername(),
connectionInfo.getPassword());
conn = DriverManager.getConnection(mySQLConnectionString);
} catch (Exception ex) {
throw new DatabaseConnectionException(ex);
}
}
return conn;
}
private ConnectionInfo getConnectionInfo() throws IOException {
try (InputStream input = MySQLDatabase.class.getResourceAsStream(DATABASE_PROPERTIES_FILENAME)) {
callback.onLoadingProperties();
Properties prop = new Properties();
prop.load(input);
String hostname = prop.getProperty(KEY_HOSTNAME);
String database = prop.getProperty(KEY_DATABASE);
String username = prop.getProperty(KEY_USERNAME);
String password = prop.getProperty(KEY_PASSWORD);
ConnectionInfo connectionInfo = new ConnectionInfo(hostname, database, username, password);
return connectionInfo;
}
}
public static class ConsoleOutput implements Callback {
@Override
public void onLoadingProperties() {
System.out.println("Getting properties file...");
}
@Override
public void onConnectingToDatabase() {
System.out.println("Connecting to database...");
}
}
public static interface Callback {
void onLoadingProperties();
void onConnectingToDatabase();
}
public static class DatabaseConnectionException extends RuntimeException {
private static final long serialVersionUID = -1343640756318904341L;
public DatabaseConnectionException(Exception ex) {
super(ex);
}
}
}