/** * OrbisGIS is a java GIS application dedicated to research in GIScience. * OrbisGIS is developed by the GIS group of the DECIDE team of the * Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>. * * The GIS group of the DECIDE team is located at : * * Laboratoire Lab-STICC – CNRS UMR 6285 * Equipe DECIDE * UNIVERSITÉ DE BRETAGNE-SUD * Institut Universitaire de Technologie de Vannes * 8, Rue Montaigne - BP 561 56017 Vannes Cedex * * OrbisGIS is distributed under GPL 3 license. * * Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488) * Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285) * * This file is part of OrbisGIS. * * OrbisGIS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * OrbisGIS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * OrbisGIS. If not, see <http://www.gnu.org/licenses/>. * * For more information, please consult: <http://www.orbisgis.org/> * or contact directly: * info_at_ orbisgis.org */ package org.orbisgis.wkgui.gui; import java.awt.Window; import java.awt.event.ActionListener; import java.beans.EventHandler; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.xml.bind.DatatypeConverter; import net.miginfocom.swing.MigLayout; import org.h2gis.utilities.JDBCUrlParser; import org.orbisgis.frameworkapi.CoreWorkspace; import org.orbisgis.sif.common.MenuCommonFunctions; import org.orbisgis.sif.components.CustomButton; import org.orbisgis.wkgui.icons.WKIcon; import org.osgi.service.jdbc.DataSourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18n; import org.xnap.commons.i18n.I18nFactory; /** * This class is used to manage the database connections used by OrbisGIS. * * @author Erwan Bocher * @author Nicolas Fortin */ public class DatabaseSettingsPanel extends JDialog { private static final String DB_PROPERTIES_FILE = "db_connexions.properties"; private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseSettingsPanel.class); protected static final I18n I18N = I18nFactory.getI18n(DatabaseSettingsPanel.class); private Properties dbProperties = new Properties(); private JTextField connectionName; private String urlValue; private JTextField dbHost; private JTextField dbPort; private JTextField dbName; private JTextField userValue; private JCheckBox requirePassword; private JComboBox<Object> connectionsComboBox; private JComboBox<DB_TYPES> dbTypes; boolean canceled = false; private CoreWorkspace defaultCoreWorkspace; public static String DEFAULT_H2_PORT="8082"; public static String DEFAULT_MESSAGE_H2=I18N.tr("Not required"); private static final String URL_STARTS = "jdbc:"; public enum DB_TYPES { H2GIS_EMBEDDED, H2GIS_SERVER, POSTGIS; } public DatabaseSettingsPanel(CoreWorkspace defaultCoreWorkspace) { super(); this.defaultCoreWorkspace = defaultCoreWorkspace; init(); } public DatabaseSettingsPanel(Window owner, CoreWorkspace defaultCoreWorkspace) { super(owner); this.defaultCoreWorkspace = defaultCoreWorkspace; init(); } /** * Create the panel */ private void init() { loadDBProperties(); Object[] dbKeys = dbProperties.keySet().toArray(); JPanel mainPanel = new JPanel(new MigLayout()); JLabel cbLabel = new JLabel(I18N.tr("Saved connections")); connectionsComboBox = new JComboBox<Object>(dbKeys); connectionsComboBox.addActionListener(EventHandler.create(ActionListener.class, this, "onUserSelectionChange")); connectionsComboBox.setSelectedIndex(-1); mainPanel.add(cbLabel); mainPanel.add(connectionsComboBox, "width 200!"); CustomButton removeBt = new CustomButton(WKIcon.getIcon("remove")); removeBt.setToolTipText(I18N.tr("Remove the connection parameters")); removeBt.addActionListener(EventHandler.create(ActionListener.class, this, "onRemove")); CustomButton refreshBt = new CustomButton(WKIcon.getIcon("refresh")); refreshBt.setToolTipText(I18N.tr("Refresh the parameters")); refreshBt.addActionListener(EventHandler.create(ActionListener.class, this, "onUserSelectionChange")); mainPanel.add(refreshBt); mainPanel.add(removeBt, "wrap"); JLabel labelName = new JLabel(I18N.tr("Connection name")); connectionName = new JTextField(); mainPanel.add(labelName); mainPanel.add(connectionName, "width 200!"); CustomButton saveBt = new CustomButton(WKIcon.getIcon("save")); saveBt.setToolTipText(I18N.tr("Save the connection parameters")); saveBt.addActionListener(EventHandler.create(ActionListener.class, this, "onSave")); mainPanel.add(saveBt, "wrap"); JLabel labeldbType = new JLabel(I18N.tr("Database")); dbTypes = new JComboBox<DB_TYPES>(DB_TYPES.values()); dbTypes.addActionListener(EventHandler.create(ActionListener.class, this, "onDBTypeChange")); mainPanel.add(labeldbType); mainPanel.add(dbTypes, "span, grow, wrap"); JLabel labelHost = new JLabel(I18N.tr("Host")); dbHost = new JTextField(); mainPanel.add(labelHost); mainPanel.add(dbHost, "span, grow, wrap"); JLabel labelPort = new JLabel(I18N.tr("Port")); dbPort = new JTextField(); mainPanel.add(labelPort); mainPanel.add(dbPort, "span, grow, wrap"); JLabel labeldbName = new JLabel(I18N.tr("Database name")); dbName = new JTextField(); mainPanel.add(labeldbName); mainPanel.add(dbName, "span, grow, wrap"); JLabel userLabel = new JLabel(I18N.tr("User name")); userValue = new JTextField(); mainPanel.add(userLabel); mainPanel.add(userValue, "span 1, grow, wrap"); JLabel pswLabel = new JLabel(I18N.tr("Require password")); requirePassword = new JCheckBox(); mainPanel.add(pswLabel); mainPanel.add(requirePassword, "span 1, grow, wrap"); JButton okBt = new JButton(I18N.tr("&Ok")); MenuCommonFunctions.setMnemonic(okBt); okBt.addActionListener(EventHandler.create(ActionListener.class, this, "onOk")); okBt.setDefaultCapable(true); mainPanel.add(okBt, "span 3"); JButton cancelBt = new JButton(I18N.tr("&Cancel")); MenuCommonFunctions.setMnemonic(cancelBt); cancelBt.addActionListener(EventHandler.create(ActionListener.class, this, "onCancel")); cancelBt.setDefaultCapable(true); mainPanel.add(cancelBt, "span 3"); getContentPane().add(mainPanel); setTitle(I18N.tr("Database parameters")); onUserSelectionChange(); pack(); setResizable(false); } /** * Click on the cancel button */ public void onCancel() { canceled = true; setVisible(false); } /** * @return True if the user cancel */ public boolean isCanceled() { return canceled; } /** * Click on the Ok button */ public void onOk() { if (checkParameters()) { urlValue = buildJDBCUrl(); saveProperties(); setVisible(false); } } /** * Check if the parameters are well filled. */ private boolean checkParameters() { boolean isParametersOk =true; if (connectionName.getText().isEmpty()) { JOptionPane.showMessageDialog(rootPane, I18N.tr("Please specify a connexion name.")); isParametersOk=false; } else if (dbName.getText().isEmpty()) { JOptionPane.showMessageDialog(rootPane, I18N.tr("The name of the database cannot be null.")); isParametersOk=false; } else if (userValue.getText().isEmpty()) { JOptionPane.showMessageDialog(rootPane, I18N.tr("The user name cannot be null.")); isParametersOk=false; } //Check the DB type return isParametersOk; } private static List<String> decodeStrings(String encodedStrings) { StringTokenizer tk = new StringTokenizer(encodedStrings, "|"); List<String> strings = new ArrayList<>(tk.countTokens()); while(tk.hasMoreTokens()) { String var = tk.nextToken() ; strings.add(new String(DatatypeConverter.parseBase64Binary(var))); } return strings; } private static String encodeStrings(String... vars) { StringBuilder sb = new StringBuilder(); for(String var : vars) { if(sb.length() != 0) { sb.append("|"); } sb.append(DatatypeConverter.printBase64Binary(var.getBytes())); } return sb.toString(); } /** * Click on the Ok button. */ public void onSave() { if (checkParameters()) { String nameValue = connectionName.getText(); if (!dbProperties.containsKey(nameValue)) { // Encode attributes in Base64 in order to be able to use separator char without worries urlValue = buildJDBCUrl(); dbProperties.setProperty(nameValue, encodeStrings(urlValue,userValue.getText(), Boolean.toString(requirePassword.isSelected()))); connectionsComboBox.addItem(nameValue); connectionsComboBox.setSelectedItem(nameValue); saveProperties(); onUserSelectionChange(); } } } /** * Create the JDBC url from swing components * * @return */ private String buildJDBCUrl(){ StringBuilder sb = new StringBuilder("jdbc:"); DB_TYPES dbType = (DB_TYPES) dbTypes.getSelectedItem(); switch (dbType) { case H2GIS_EMBEDDED: sb.append("h2:").append(dbName.getText()); break; case H2GIS_SERVER: sb.append("h2:tcp://").append(dbHost.getText()); if(dbPort.getText()!=null){ sb.append(":").append(dbPort.getText()); } sb.append("/").append(dbName.getText()); break; case POSTGIS: sb.append("postgresql://").append(dbHost.getText()); if(dbPort.getText()!=null){ sb.append(":").append(dbPort.getText()); } sb.append("/").append(dbName.getText()); break; default: break; } return sb.toString(); } /** * Click on the Ok button. */ public void onRemove() { String valueConnection = connectionName.getText(); if(dbProperties.containsKey(valueConnection)){ dbProperties.remove(valueConnection); connectionsComboBox.removeItem(valueConnection); saveProperties(); onUserSelectionChange(); } } /** * Load the connection properties file. */ private void loadDBProperties() { try { File propertiesFile = new File(defaultCoreWorkspace.getApplicationFolder() + File.separator + DB_PROPERTIES_FILE); if (propertiesFile.exists()) { dbProperties.load(new FileInputStream(propertiesFile)); } } catch (IOException e) { LOGGER.error(e.getLocalizedMessage(), e); } } /** * Save the connection properties file. */ public void saveProperties() { try { dbProperties.store(new FileOutputStream(defaultCoreWorkspace.getApplicationFolder() + File.separator + DB_PROPERTIES_FILE), I18N.tr("Saved with the OrbisGIS database panel")); } catch (IOException ex) { LOGGER.error(ex.getLocalizedMessage(), ex); } } /** * Change the populate the components. */ public void onUserSelectionChange() { boolean isCmbEmpty = connectionsComboBox.getItemCount() == 0; if (!isCmbEmpty && connectionsComboBox.getSelectedItem() != null) { String value = connectionsComboBox.getSelectedItem().toString(); String data = dbProperties.getProperty(value); connectionName.setText(value); List<String> config = decodeStrings(data); if (config.size() == 3) { urlValue = config.get(0); Properties jdcProperties = JDBCUrlParser.parse(urlValue); dbName.setText(jdcProperties.getProperty(DataSourceFactory.JDBC_DATABASE_NAME)); String dbTypeName = parseDbType(urlValue); if (dbTypeName.equalsIgnoreCase("h2")) { String netProt = jdcProperties.getProperty(DataSourceFactory.JDBC_NETWORK_PROTOCOL); if (netProt != null) { dbTypes.setSelectedItem(DatabaseSettingsPanel.DB_TYPES.H2GIS_SERVER); dbHost.setText(jdcProperties.getProperty(DataSourceFactory.JDBC_SERVER_NAME)); String portNum = jdcProperties.getProperty(DataSourceFactory.JDBC_PORT_NUMBER); dbPort.setText(portNum != null ? portNum : DatabaseSettingsPanel.DEFAULT_H2_PORT); } else { dbTypes.setSelectedItem(DatabaseSettingsPanel.DB_TYPES.H2GIS_EMBEDDED); dbHost.setText(DEFAULT_MESSAGE_H2); dbPort.setText(DEFAULT_MESSAGE_H2); } } else if (dbTypeName.equalsIgnoreCase("postgresql")) { dbTypes.setSelectedItem(DatabaseSettingsPanel.DB_TYPES.POSTGIS); dbHost.setText(jdcProperties.getProperty(DataSourceFactory.JDBC_SERVER_NAME)); dbPort.setText(jdcProperties.getProperty(DataSourceFactory.JDBC_PORT_NUMBER)); } userValue.setText(config.get(1)); requirePassword.setSelected(Boolean.parseBoolean(config.get(2))); } } } /** * If the dbtype change some components must be disable */ public void onDBTypeChange(){ DB_TYPES dbType = (DB_TYPES) dbTypes.getSelectedItem(); if(dbType.equals(DB_TYPES.H2GIS_EMBEDDED)){ dbHost.setEnabled(false); dbPort.setEnabled(false); } else{ dbHost.setEnabled(true); dbPort.setEnabled(true); if (dbHost.getText() == null) { dbHost.setText("Required"); } if (dbPort.getText() == null) { dbPort.setText("Required"); } } } /** * @return Password field */ public boolean hasPassword() { return requirePassword.isSelected(); } /** * @return URI field */ public String getJdbcURI() { return urlValue; } /** * @return User field */ public String getUser() { return userValue.getText(); } /** * Set a new database user name. * @param dataBaseUser User identifier */ public void setUser(String dataBaseUser) { userValue.setText(dataBaseUser); } /** * @return Database name */ public String getDatabaseName() { return dbName.getText(); } /** * Set this connection require password. * @param hasPassword True if this connection require a password. */ public void setHasPassword(Boolean hasPassword) { requirePassword.setSelected(hasPassword); } /** * Set the connection identifier * @param connectionName Connection identifier */ public void setConnectionName(String connectionName) { this.connectionName.setText(connectionName); } /** * Set the name of the database * @param dbName */ public void setDBName(String dbName) { this.dbName.setText(dbName); } /** * Select the DBType name * @param dbType */ public void setDBType(DB_TYPES dbType) { dbTypes.setSelectedItem(dbType); } /** * Set the host value * @param hostValue */ public void setHost(String hostValue) { dbHost.setText(hostValue); } /** * Set the port number * @param portValue */ public void setPort(String portValue) { dbPort.setText(portValue); } /** * Return the database type name base on the JDBC url * @param jdbcUrl * @return */ public static String parseDbType(String jdbcUrl) { if (!jdbcUrl.startsWith(URL_STARTS)) { throw new IllegalArgumentException("JDBC Url must start with " + URL_STARTS); } String driverAndURI = jdbcUrl.substring(URL_STARTS.length()); String driver = driverAndURI.substring(0, driverAndURI.indexOf(':')); if (driver != null) { return driver; } throw new IllegalArgumentException("JDBC Url must start with " + URL_STARTS); } }