/**
* 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 net.miginfocom.swing.MigLayout;
import org.orbisgis.commons.progress.ProgressMonitor;
import org.orbisgis.corejdbc.DataSourceService;
import org.orbisgis.framework.CoreWorkspaceImpl;
import org.orbisgis.frameworkapi.CoreWorkspace;
import org.orbisgis.sif.multiInputPanel.DirectoryComboBoxChoice;
import org.orbisgis.wkgui.icons.WKIcon;
import org.orbisgis.wkguiapi.ViewWorkspace;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Version;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
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;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.beans.EventHandler;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.h2gis.utilities.JDBCUrlParser;
import org.orbisgis.sif.components.CustomButton;
import static org.orbisgis.wkgui.gui.DatabaseSettingsPanel.DEFAULT_MESSAGE_H2;
/**
* GUI for Workspace selection.
*
* @author Nicolas Fortin
* @author Adam Gouge
*/
@org.osgi.service.component.annotations.Component
public class WorkspaceSelectionDialog extends JPanel {
private static final I18n I18N = I18nFactory.getI18n(WorkspaceSelectionDialog.class);
private static final Logger LOGGER = LoggerFactory.getLogger(WorkspaceSelectionDialog.class);
private DirectoryComboBoxChoice comboBox;
private JCheckBox defaultCheckBox;
private CoreWorkspaceImpl selectedWorkspace;
private JLabel errorLabel = new JLabel();
DataSourceService dataSourceService = new DataSourceService();
private Version bundleVersion;
private boolean isJDBCUrlValid = true;
public WorkspaceSelectionDialog() {
super(new MigLayout("wrap 1"));
}
/**
* @param dataSourceFactory DataSourceFactory instance
* @param serviceProperties Must contain DataSourceFactory.OSGI_JDBC_DRIVER_NAME entry.
*/
@Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE, policy = ReferencePolicy.DYNAMIC, policyOption =
ReferencePolicyOption.GREEDY)
public void addDataSourceFactory(DataSourceFactory dataSourceFactory, Map<String,String> serviceProperties) {
dataSourceService.addDataSourceFactory(dataSourceFactory, serviceProperties);
}
/**
* @param dataSourceFactory DataSourceFactory instance
* @param serviceProperties Must contain DataSourceFactory.OSGI_JDBC_DRIVER_NAME entry.
*/
public void removeDataSourceFactory(DataSourceFactory dataSourceFactory, Map<String,String> serviceProperties) {
dataSourceService.removeDataSourceFactory(dataSourceFactory, serviceProperties);
}
@Activate
public void activate(BundleContext bc) throws BundleException {
bundleVersion = bc.getBundle().getVersion();
new RegisterViewWorkspaceJob(this, bc).execute();
}
public ViewWorkspaceImpl askWorkspaceFolder(Window parentComponent, ProgressMonitor pm) {
CoreWorkspaceImpl coreWorkspace = new CoreWorkspaceImpl(bundleVersion.getMajor(), bundleVersion.getMinor(),
bundleVersion.getMicro(), bundleVersion.getQualifier(), new org.apache.felix.framework.Logger());
String errorMessage = "";
try {
do {
if (WorkspaceSelectionDialog.showWorkspaceFolderSelection(parentComponent, coreWorkspace, errorMessage)) {
/////////////////////
// Check connection
dataSourceService.setCoreWorkspace(coreWorkspace);
try {
dataSourceService.activate();
pm.setTaskName(I18N.tr("Connecting to the database.."));
try (Connection connection = dataSourceService.getConnection()) {
DatabaseMetaData meta = connection.getMetaData();
LOGGER.info(I18N.tr("Data source available {0} version {1}", meta.getDriverName(), meta.getDriverVersion()));
return new ViewWorkspaceImpl(coreWorkspace);
}
} catch (SQLException ex) {
errorMessage = ex.getLocalizedMessage();
}
} else {
// User cancel, stop OrbisGIS
return null;
}
} while (true);
} finally {
pm.endTask();
}
}
/**
* Init the workspace panel
*
* @param coreWorkspace
* @param errorMessage
*/
private void init(CoreWorkspaceImpl coreWorkspace, String errorMessage) {
selectedWorkspace = new CoreWorkspaceImpl(coreWorkspace.getVersionMajor(), coreWorkspace.getVersionMinor(),
coreWorkspace.getVersionRevision(), coreWorkspace.getVersionQualifier(), new org.apache.felix
.framework.Logger());
// Get the list of known workspaces
List<File> knownWorkspaces = coreWorkspace.readKnownWorkspacesPath();
// Remove the currently loaded workspace from the list
String currentWorkspacePath = coreWorkspace.getWorkspaceFolder();
if (currentWorkspacePath != null && !currentWorkspacePath.isEmpty()) {
knownWorkspaces.remove(new File(currentWorkspacePath));
}
// Initialize components
JLabel chooseLabel = new JLabel(I18N.tr("Choose the workspace folder"));
String defaultFontName = chooseLabel.getFont().getName();
Font largeFont = new Font(defaultFontName, Font.BOLD, 16);
Font smallFont = new Font(defaultFontName, Font.PLAIN, 10);
chooseLabel.setFont(largeFont);
JLabel subChooseLabel = new JLabel(
"<html><body><p style='width: 200px;'>" +
I18N.tr("Choose a previous OrbisGIS workspace or create a new one")
+ "</p></body></html>");
subChooseLabel.setFont(smallFont);
comboBox = new DirectoryComboBoxChoice(knownWorkspaces);
if (!knownWorkspaces.isEmpty()) {
// Select the default workspace on the combo box
comboBox.setValue(knownWorkspaces.get(0).getAbsolutePath());
}
ActionListener selectionDone = EventHandler.create(ActionListener.class, this, "onWorkspaceFolderChange");
comboBox.getComboBox().addActionListener(selectionDone);
defaultCheckBox = new JCheckBox(I18N.tr("Set as default?"));
JLabel subCheckBox = new JLabel("<html><body><p style='width: 200px;'>" +
I18N.tr("Setting this workspace as default will allow you to " +
"skip this dialog next time") + "</p></body></html>");
JButton deleteButton = new CustomButton(WKIcon.getIcon("remove"));
deleteButton.addActionListener(EventHandler.create(ActionListener.class, this, "onDeleteWorkspaceEntry"));
subCheckBox.setFont(smallFont);
// Add components
errorLabel.setForeground(Color.RED.darker());
add(errorLabel);
add(chooseLabel);
add(subChooseLabel);
add(comboBox.getComponent(), "split 2");
add(deleteButton, "gapleft 0");
add(Box.createGlue());
add(defaultCheckBox);
add(subCheckBox);
CustomButton customDataBase = new CustomButton(WKIcon.getIcon("database"));
customDataBase.setText(I18N.tr("Customize your database"));
customDataBase.setToolTipText(I18N.tr("Click to customize your database."));
customDataBase.addActionListener(
EventHandler.create(ActionListener.class, this, "onOpenDBPanel"));
add(customDataBase);
errorLabel.setText(errorMessage);
onWorkspaceFolderChange();
}
/**
* Check the JDBC url
* if invalid return false and the default JDBC connection reference
* if valid return true
*/
public void checkJDBCUrl() {
String jdbc_url = selectedWorkspace.getJDBCConnectionReference();
if (!jdbc_url.isEmpty()) {
try {
Properties jdbcProperties = JDBCUrlParser.parse(jdbc_url);
selectedWorkspace.setDatabaseName(jdbcProperties.getProperty(DataSourceFactory.JDBC_DATABASE_NAME));
errorLabel.setText("");
isJDBCUrlValid = true;
} catch (IllegalArgumentException ex) {
selectedWorkspace.setJDBCConnectionReference("");
isJDBCUrlValid = false;
errorLabel.setText(I18N.tr("The database parameters are invalid."));
}
}
else{
errorLabel.setText("The database parameters are invalid.");
}
}
/**
* Return true if the JDBC url is valid
* @return
*/
public boolean isJDBCUrlValid() {
return isJDBCUrlValid;
}
/**
* User click on delete button.
*/
public void onDeleteWorkspaceEntry() {
JComboBox combo = comboBox.getComboBox();
if(combo.getItemCount() != 0 && combo.getSelectedIndex() != -1) {
combo.removeItemAt(combo.getSelectedIndex());
}
}
/**
* The user click on add open button
*/
public void onOpenDBPanel() {
Window window = SwingUtilities.getWindowAncestor(this);
DatabaseSettingsPanel databaseSettingsPanel = new DatabaseSettingsPanel(window, selectedWorkspace);
databaseSettingsPanel.setConnectionName(new File(selectedWorkspace.getWorkspaceFolder()).getName());
String jdbc_url = selectedWorkspace.getJDBCConnectionReference();
Properties dbProperties = JDBCUrlParser.parse(jdbc_url);
databaseSettingsPanel.setDBName(dbProperties.getProperty(DataSourceFactory.JDBC_DATABASE_NAME));
String dbTypeName = DatabaseSettingsPanel.parseDbType(jdbc_url);
if (dbTypeName.equalsIgnoreCase("h2")) {
String netProt = dbProperties.getProperty(DataSourceFactory.JDBC_NETWORK_PROTOCOL);
if (netProt != null) {
databaseSettingsPanel.setDBType(DatabaseSettingsPanel.DB_TYPES.H2GIS_SERVER);
databaseSettingsPanel.setHost(dbProperties.getProperty(DataSourceFactory.JDBC_SERVER_NAME));
String portNum = dbProperties.getProperty(DataSourceFactory.JDBC_PORT_NUMBER);
databaseSettingsPanel.setPort(portNum != null ? portNum : DatabaseSettingsPanel.DEFAULT_H2_PORT);
} else {
databaseSettingsPanel.setDBType(DatabaseSettingsPanel.DB_TYPES.H2GIS_EMBEDDED);
databaseSettingsPanel.setPort(DEFAULT_MESSAGE_H2);
databaseSettingsPanel.setHost(DEFAULT_MESSAGE_H2);
}
} else if (dbTypeName.equalsIgnoreCase("postgresql")) {
databaseSettingsPanel.setDBType(DatabaseSettingsPanel.DB_TYPES.POSTGIS);
databaseSettingsPanel.setHost(dbProperties.getProperty(DataSourceFactory.JDBC_SERVER_NAME));
databaseSettingsPanel.setPort(dbProperties.getProperty(DataSourceFactory.JDBC_PORT_NUMBER));
}
databaseSettingsPanel.setUser(selectedWorkspace.getDataBaseUser());
databaseSettingsPanel.setHasPassword(selectedWorkspace.isRequirePassword());
databaseSettingsPanel.setAlwaysOnTop(true);
databaseSettingsPanel.setModal(true);
databaseSettingsPanel.setLocationRelativeTo(window);
databaseSettingsPanel.setVisible(true);
if (!databaseSettingsPanel.isCanceled()) {
// Read selected attributes
selectedWorkspace.setDataBaseUser(databaseSettingsPanel.getUser());
selectedWorkspace.setRequirePassword(databaseSettingsPanel.hasPassword());
selectedWorkspace.setJDBCConnectionReference(databaseSettingsPanel.getJdbcURI());
selectedWorkspace.setDatabaseName(databaseSettingsPanel.getDatabaseName());
isJDBCUrlValid=true;
errorLabel.setText("");
}
}
/**
* @return The workspace selection combo box
*/
public DirectoryComboBoxChoice getComboBox() {
return comboBox;
}
/**
* @return The default workspace check box
*/
public JCheckBox getDefaultCheckBox() {
return defaultCheckBox;
}
/**
* Shows a dialog to choose the workspace folder
*
* @param parent Parent component
* @param coreWorkspace Core workspace
* @param errorMessage
*
* @return True if the user validate workspace change
*/
public static boolean showWorkspaceFolderSelection(Window parent,
CoreWorkspaceImpl coreWorkspace, String errorMessage) {
String oldWorkspace = coreWorkspace.getWorkspaceFolder();
// Initialize a panel to contain the dialog
WorkspaceSelectionDialog panel = new WorkspaceSelectionDialog();
panel.init(coreWorkspace, errorMessage);
// Show the dialog and get the user's choice.
int userChoice = JOptionPane.showConfirmDialog(parent,
panel,
I18N.tr("Workspace Manager"),
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
WKIcon.getIcon("sidebar"));
// If the user clicked OK, then update the workspace.
if (userChoice == JOptionPane.OK_OPTION) {
String chosenWorkspacePath = panel.getComboBox().getValue();
if (!ViewWorkspaceImpl.isWorkspaceValid(new File(chosenWorkspacePath), coreWorkspace.getVersionMajor())) {
LOGGER.error(I18N.tr("The workspace folder version is invalid " +
"(!=OrbisGIS {0}), or the folder is not empty",
coreWorkspace.getVersionMajor()));
return false;
}
if(!panel.isJDBCUrlValid()){
LOGGER.error(I18N.tr("The database parameters are invalid."));
return false;
}
try {
if(panel.selectedWorkspace.isRequirePassword()) {
//The user must input the password
JPanel passwordPanel = new JPanel(new BorderLayout());
JPasswordField pass = new JPasswordField(10);
JLabel message = new JLabel(I18N.tr("<html>Database : {0}<br>User : {1}</html>",
panel.selectedWorkspace.getDatabaseName(),
panel.selectedWorkspace.getDataBaseUser()));
passwordPanel.add(pass, BorderLayout.CENTER);
passwordPanel.add(message, BorderLayout.NORTH);
if(JOptionPane.showConfirmDialog(panel, passwordPanel,
I18N.tr("Enter a password for the database"), JOptionPane.OK_CANCEL_OPTION,JOptionPane.QUESTION_MESSAGE,WKIcon.getIcon("database")) == JOptionPane.OK_OPTION) {
coreWorkspace.setDataBasePassword(new String(pass.getPassword()));
} else {
return false;
}
}
updateWorkspace(coreWorkspace, panel.getComboBox(), oldWorkspace, panel);
} catch (IOException ex) {
LOGGER.error(I18N.tr("Problem updating the workspace. ")
+ ex.getLocalizedMessage(), ex);
return false;
}
return true;
} else {
return false;
}
}
/**
* The user select another workspace folder.Update the JDBC uri
*/
public void onWorkspaceFolderChange() {
// Check if workspace folder is valid
if(ViewWorkspaceImpl.isWorkspaceValid(new File(comboBox.getValue()), selectedWorkspace.getVersionMajor())) {
selectedWorkspace.setWorkspaceFolder(comboBox.getValue());
errorLabel.setText("");
} else {
errorLabel.setText(I18N.tr("The selected folder is not a valid workspace"));
}
checkJDBCUrl();
}
/**
* Called when the user change/set the workspace folder of OrbisGIS.
* @param coreWorkspace Core workspace
* @param comboBox ComboBox with possible workspace directories
* @param oldWorkspacePath Path of the previous workspace
* @param wkDialog Workspace panel
* @throws IOException during certain workspace operations.
*/
private static void updateWorkspace(CoreWorkspaceImpl coreWorkspace,
DirectoryComboBoxChoice comboBox,
String oldWorkspacePath,WorkspaceSelectionDialog wkDialog) throws IOException {
// Set as default workspace if necessary
if (wkDialog.getDefaultCheckBox().isSelected()) {
coreWorkspace.setDefaultWorkspace(new File(wkDialog.getComboBox().getValue()));
} else {
coreWorkspace.setDefaultWorkspace(null);
}
// Save the workspace list, including the previous one
List<File> workspaces = comboBox.getValues();
File wkFile = new File(wkDialog.getComboBox().getValue());
workspaces.remove(wkFile);
if (oldWorkspacePath != null && !oldWorkspacePath.isEmpty()) {
workspaces.add(new File(oldWorkspacePath));
}
workspaces.add(0, wkFile);
coreWorkspace.writeKnownWorkspaces(workspaces);
// Initialize the workspace if empty or new
File[] files = wkFile.listFiles();
if (!wkFile.exists() || (files != null && files.length == 0)) {
ViewWorkspaceImpl.initWorkspaceFolder(wkFile, coreWorkspace.getVersionMajor(),
coreWorkspace.getVersionMinor(), coreWorkspace.getVersionRevision(),
coreWorkspace.getVersionQualifier());
}
// Write chosen jdbc attributes
wkDialog.selectedWorkspace.writeUriFile();
// Do this at the end because there is trigger on property change
coreWorkspace.setWorkspaceFolder(wkDialog.getComboBox().getValue());
}
public static class RegisterViewWorkspaceJob extends SwingWorker {
private WorkspaceSelectionDialog workspaceSelectionDialog;
private BundleContext bundleContext;
public RegisterViewWorkspaceJob(WorkspaceSelectionDialog workspaceSelectionDialog, BundleContext
bundleContext) {
this.workspaceSelectionDialog = workspaceSelectionDialog;
this.bundleContext = bundleContext;
}
@Override
protected Object doInBackground() throws Exception {
LoadingFrame loadingFrame = new LoadingFrame();
try {
loadingFrame.setVisible(true);
ProgressMonitor progressMonitor = loadingFrame.getProgressMonitor().startTask(2);
ViewWorkspaceImpl viewWorkspace = workspaceSelectionDialog.askWorkspaceFolder(loadingFrame, progressMonitor);
if (viewWorkspace != null) {
progressMonitor.setTaskName(I18N.tr("Loading OrbisGIS.."));
bundleContext.registerService(CoreWorkspace.class, viewWorkspace.getCoreWorkspace(), null);
bundleContext.registerService(ViewWorkspace.class, viewWorkspace, null);
progressMonitor.endTask();
} else {
bundleContext.getBundle(0).stop();
}
return null;
} finally {
loadingFrame.setVisible(false);
loadingFrame.dispose();
}
}
}
}