/**
* Created by IntelliJ IDEA.
* User: martlenn
* Date: 28-Jul-2009
* Time: 15:41:18
*/
package com.compomics.util.gui.utils;
import org.apache.log4j.Logger;
import com.compomics.util.enumeration.CompomicsTools;
import com.compomics.util.gui.JLabelAndComponentPanel;
import com.compomics.util.interfaces.Connectable;
import com.compomics.util.io.PropertiesManager;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;
import java.util.ArrayList;
/*
* CVS information:
*
* $Revision: 1.3 $
* $Date: 2009/07/30 10:20:39 $
*/
/**
* This class implements a dialog to gather all information concerning a DB connection.
*
* @author Lennart Martens
*/
public class ConnectionDialog extends JDialog {
// Class specific log4j logger for ConnectionDialog instances.
Logger logger = Logger.getLogger(ConnectionDialog.class);
/***
* ArrayList that holds all the preconfigured connections.
* Note that when this list holds only 0 or 1 elements,
* it changes the behaviour of the GUI.
*/
private ArrayList iConnections = new ArrayList();
private JComboBox cmbConfigurations = null;
private JTextField txtDriver = null;
private JTextField txtUrl = null;
private JTextField txtUser = null;
private JPasswordField txtPassword = null;
private JButton btnOK = null;
private JButton btnCancel = null;
private Connectable iTarget = null;
private String iPropsFile = null;
private String iLastInitiatedConfiguration;
/**
* This constructor takes as arguments the parent JFrame and
* a title for the dialog.
* It also constructs the GUI.
*
* @param aParent JFrame that is the parent of this JDialog.
* @param aTarget Connectable to which the connection will be passed
* once it is successfully established.
* @param aTitle String with the title for this dialog
* @param aPropsFile String with the filename for a propertiesfile with some connection
* parameters already filled out, notably 'url' and 'driver'. Can be 'null'
* for no defaults. If something goes wrong, no defaults are filled out.
*/
public ConnectionDialog(JFrame aParent, Connectable aTarget, String aTitle, String aPropsFile) {
super(aParent, aTitle, true);
this.iPropsFile = aPropsFile;
this.iTarget = aTarget;
this.showConnectionDialog();
}
/**
* This constructor takes as arguments the parent JFrame and
* a title for the dialog.
* It also constructs the GUI.
*
* @param aParent JFrame that is the parent of this JDialog.
* @param aTarget Connectable to which the connection will be passed
* once it is successfully established.
* @param aTitle String with the title for this dialog
* @param aConnectionProperties Properties instance with three variables (name, driver, url)
*/
public ConnectionDialog(JFrame aParent, Connectable aTarget, String aTitle, Properties aConnectionProperties) {
super(aParent, aTitle, true);
this.iTarget = aTarget;
parseConnectionProperties(aConnectionProperties);
this.showConnectionDialog();
}
/**
* This method actually shows the ConnectionDialog.
* It takes care of the GUI related stuff.
*/
private void showConnectionDialog() {
this.addWindowListener(new WindowAdapter() {
/**
* Invoked when a window is in the process of being closed.
* The close operation can be overridden at this point.
*/
public void windowClosing(WindowEvent e) {
cancelTriggered();
}
});
// Load predefined configuration connection parameters, if any.
this.tryToLoadParams();
// Create GUI.
this.constructScreen();
if(getParent().getLocation().getX() <= 0 || getParent().getLocation().getY() <= 0) {
this.setLocation(100, 100);
} else {
this.setLocation((int)getParent().getLocation().getX()+100, (int)getParent().getLocation().getY()+100);
}
this.pack();
this.setResizable(false);
}
/**
* This method will initialize and lay-out all components.
*/
private void constructScreen() {
txtDriver = new JTextField(25);
txtDriver.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
txtUrl.requestFocus();
} else {
super.keyTyped(e);
}
}
});
txtUrl = new JTextField(25);
txtUrl.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
txtUser.requestFocus();
} else {
super.keyTyped(e);
}
}
});
txtUser = new JTextField(25);
txtUser.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
txtPassword.requestFocus();
} else {
super.keyTyped(e);
}
}
});
txtPassword = new JPasswordField(25);
txtPassword.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
connectTriggered();
} else {
super.keyTyped(e);
}
}
});
// If there are less than 2 predefined connection parameters,
// there is no reason to show the selection combobox.
JLabelAndComponentPanel jpanTop = null;
if(iConnections.size() < 2) {
jpanTop = new JLabelAndComponentPanel( new JLabel[] { new JLabel("Database driver"), new JLabel("Database URL"), new JLabel("Username"), new JLabel("Password")},
new JTextField[]{txtDriver, txtUrl, txtUser, txtPassword});
// Here we have to set the defaults.
if(iConnections.size() > 0) {
InnerConfigParams params = (InnerConfigParams) iConnections.get(0);
initConfiguration(params);
}
} else {
cmbConfigurations = new JComboBox(iConnections.toArray());
cmbConfigurations.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
Object temp = cmbConfigurations.getSelectedItem();
if(temp != null) {
InnerConfigParams params = (InnerConfigParams) temp;
initConfiguration(params);
}
}
});
initConfiguration((InnerConfigParams) iConnections.get(0));
jpanTop = new JLabelAndComponentPanel( new JLabel[] { new JLabel("Predefined connections"), new JLabel("Database driver"), new JLabel("Database URL"), new JLabel("Username"), new JLabel("Password")},
new JComponent[]{cmbConfigurations, txtDriver, txtUrl, txtUser, txtPassword});
}
jpanTop.setBorder(BorderFactory.createTitledBorder("Connection settings"));
btnOK = new JButton("Connect");
btnOK.setMnemonic(KeyEvent.VK_O);
btnOK.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
connectTriggered();
}
});
btnOK.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
connectTriggered();
}
}
});
btnCancel = new JButton("Cancel");
btnCancel.setMnemonic(KeyEvent.VK_C);
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
cancelTriggered();
}
});
btnCancel.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been typed.
* This event occurs when a key press is followed by a key release.
*/
public void keyTyped(KeyEvent e) {
if(e.getKeyChar() == KeyEvent.VK_ENTER) {
cancelTriggered();
}
}
});
JPanel jpanButtons = new JPanel();
jpanButtons.setLayout(new BoxLayout(jpanButtons, BoxLayout.X_AXIS));
jpanButtons.add(Box.createHorizontalGlue());
jpanButtons.add(btnOK);
jpanButtons.add(Box.createRigidArea(new Dimension(15, btnOK.getHeight())));
jpanButtons.add(btnCancel);
jpanButtons.add(Box.createRigidArea(new Dimension(10, btnOK.getHeight())));
JPanel jpanTotal = new JPanel();
jpanTotal.setLayout(new BoxLayout(jpanTotal, BoxLayout.Y_AXIS));
jpanTotal.add(jpanTop);
jpanTotal.add(Box.createRigidArea(new Dimension(jpanTop.getWidth(), 10)));
jpanTotal.add(jpanButtons);
this.getContentPane().add(jpanTotal, BorderLayout.CENTER);
}
/**
* This method initializes the predefined connection parameters 'driver'
* and 'url', if they are found in the specified InnerConfigParams.
*
* @param aParams InnerConfigParams with the predefined configuration
* parameters to initialize.
*/
private void initConfiguration(InnerConfigParams aParams) {
iLastInitiatedConfiguration = aParams.getName();
if(aParams.getDriver() != null) {
txtDriver.setText(aParams.getDriver().trim());
}
if(aParams.getUrl() != null) {
txtUrl.setText(aParams.getUrl().trim());
}
if(aParams.getUser() != null) {
txtUser.setText(aParams.getUser().trim());
}
}
/**
* This method is called when the user attempts to connect.
*/
private void connectTriggered() {
String driverClass = txtDriver.getText().trim();
String url = txtUrl.getText().trim();
String user = txtUser.getText().trim();
String password = new String(txtPassword.getPassword()).trim();
if(driverClass.equals("")) {
JOptionPane.showMessageDialog(this, "Driver class needs to be specified!", "No driver specified!", JOptionPane.ERROR_MESSAGE);
txtDriver.requestFocus();
return;
}
if(url.equals("")) {
JOptionPane.showMessageDialog(this, "Database URL needs to be specified!", "No URL specified!", JOptionPane.ERROR_MESSAGE);
txtUrl.requestFocus();
return;
}
String errorString = null;
Connection lConn = null;
try {
this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
Driver d = (Driver)Class.forName(driverClass).newInstance();
Properties lProps = new Properties();
if(user != null) {
lProps.put("user", user);
}
if(password != null) {
lProps.put("password", password);
}
lConn = d.connect(url, lProps);
if(lConn == null) {
errorString = "Could not connect to the database. Either your driver is incorrect for this database, or your URL is malformed.";
}
} catch(ClassNotFoundException cnfe) {
errorString = "Driver class was not found! (" + cnfe.getMessage() + ")";
} catch(IllegalAccessException iae) {
errorString = "Could not access default constructor on driver class! (" + iae.getMessage() + ")";
} catch(InstantiationException ie) {
errorString = "Could not create instance of driver class! (" + ie.getMessage() + ")";
} catch(SQLException sqle) {
errorString = "Database refused connection! (" + sqle.getMessage() + ")";
}
this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
if(errorString != null) {
JOptionPane.showMessageDialog(this, new String[]{"Unable to make the connection to '" + url + "' using '" + driverClass + "'!", errorString, "\n"}, "Unable to connect!", JOptionPane.ERROR_MESSAGE);
return;
} else {
// Success!
// First, fut the last used values into a properties file and store for next time.
Properties lLastUsedProperties = new Properties();
if(iLastInitiatedConfiguration.toLowerCase().equals("default")){
lLastUsedProperties.put("user", user);
lLastUsedProperties.put("driver", driverClass);
lLastUsedProperties.put("url", url);
}else{
lLastUsedProperties.put("user_" + iLastInitiatedConfiguration, user);
lLastUsedProperties.put("driver_" + iLastInitiatedConfiguration, driverClass);
lLastUsedProperties.put("url_" + iLastInitiatedConfiguration, url);
}
PropertiesManager.getInstance().updateProperties(CompomicsTools.MSLIMS, "ms-lims.properties", lLastUsedProperties);
// Now continue into ms_lims.
JOptionPane.showMessageDialog(this, new String[]{"Connection to '" + url + "' established!", "\n"}, "Connection established!", JOptionPane.INFORMATION_MESSAGE);
iTarget.passConnection(lConn, url.substring(url.lastIndexOf(":")+1));
this.setVisible(false);
this.dispose();
}
}
/**
* This method is called when the user presses cancel.
*/
private void cancelTriggered() {
this.setVisible(false);
this.dispose();
iTarget.passConnection(null, "");
}
/**
* This method attempts to load connection parameters from
* a properties file in the classpath.
* If this file is not found, nothing happens.
* If it is found, the parameters found will be filled out.
*/
private void tryToLoadParams() {
if(iPropsFile != null) {
try {
Properties p = new Properties();
InputStream is = ClassLoader.getSystemResourceAsStream(iPropsFile);
if(is == null) {
is = this.getClass().getClassLoader().getResourceAsStream(iPropsFile);
if(is == null) {
// Leave it at that.
return;
}
logger.info("local classloader.");
}
p.load(is);
parseConnectionProperties(p);
is.close();
} catch(Exception e) {
// Do nothing.
logger.error(e.getMessage(), e);
}
}
}
/**
* This method reads the predefined connection properties from a Properties instance.
*
* @param aConnectionProperties Properties instance with the information for the connection(s)
*/
private void parseConnectionProperties(final Properties aConnectionProperties) {
// Two options here - old-fashioned, single predefined configuration,
// or hot new multiple predefined configurations.
// Distinction is the presence of the 'CONFIGURATION' key in the
// latter case.
if( (aConnectionProperties.getProperty("CONFIGURATION") == null || aConnectionProperties.getProperty("CONFIGURATION").trim().length() == 0)
&&
(aConnectionProperties.getProperty("configuration") == null || aConnectionProperties.getProperty("configuration").trim().length() == 0)
) {
// In this case, we expect only a sinlge predefined configuration,
// which will be stored under the 'DEFAULT' key in the hashmap.
String dbDriver = aConnectionProperties.getProperty("driver");
if(dbDriver == null) {
dbDriver = aConnectionProperties.getProperty("DRIVER");
}
String dbUrl = aConnectionProperties.getProperty("url");
if(dbUrl == null) {
dbUrl = aConnectionProperties.getProperty("URL");
}
String dbUser = aConnectionProperties.getProperty("USER");
if(dbUser == null) {
dbUser = aConnectionProperties.getProperty("user");
}
iConnections.add(new InnerConfigParams(dbUser, "DEFAULT", dbDriver, dbUrl));
} else {
String configurationString = aConnectionProperties.getProperty("CONFIGURATION");
if(configurationString == null) {
configurationString = aConnectionProperties.getProperty("configuration");
}
String[] configurations = configurationString.split(",");
for (int i = 0; i < configurations.length; i++) {
String lConfiguration = configurations[i].trim();
String dbDriver = aConnectionProperties.getProperty("DRIVER_" + lConfiguration);
if(dbDriver == null) {
dbDriver = aConnectionProperties.getProperty("driver_" + lConfiguration);
}
String dbUrl = aConnectionProperties.getProperty("url_" + lConfiguration);
if(dbUrl == null) {
dbUrl = aConnectionProperties.getProperty("URL_" + lConfiguration);
}
String dbUser = aConnectionProperties.getProperty("USER_" + lConfiguration);
if (dbUser == null) {
dbUser = aConnectionProperties.getProperty("user_" + lConfiguration);
}
iConnections.add(new InnerConfigParams(dbUser, lConfiguration, dbDriver, dbUrl));
}
}
}
/**
* This class represents a wrapper object for connection properties
*/
private class InnerConfigParams {
private String iUser = null;
private String iName = null;
private String iDriver = null;
private String iUrl = null;
/**
* Constructor to create an InnerConfigParams object.
*
* @param aName String with the connection name
* @param aDriver String with the driver classname
* @param aUrl String with the DB URL
*/
private InnerConfigParams(String aName, String aDriver, String aUrl) {
iName = aName;
iDriver = aDriver;
iUrl = aUrl;
}
/**
* Constructor to create an InnerConfigParams object.
*
* @param aUser String with the username
* @param aName String with the connection name
* @param aDriver String with the driver classname
* @param aUrl String with the DB URL
*/
private InnerConfigParams(String aUser, String aName, String aDriver, String aUrl) {
this(aName, aDriver, aUrl);
iUser = aUser;
}
public String getUser() {
return iUser;
}
public void setUser(String aUser) {
iUser = aUser;
}
public String getDriver() {
return iDriver;
}
public void setDriver(String aDriver) {
iDriver = aDriver;
}
public String getUrl() {
return iUrl;
}
public void setUrl(String aUrl) {
iUrl = aUrl;
}
public String toString() {
return iName;
}
public String getName() {
return iName;
}
public void setName(final String aName) {
iName = aName;
}
}
}