diff --git a/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.form b/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.form index 67e6ca091..6d37fbe90 100644 --- a/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.form +++ b/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.form @@ -449,6 +449,20 @@ + + + + + + + + + + + + + + diff --git a/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.java b/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.java index 768bc058b..23bf41ffd 100644 --- a/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.java +++ b/src/main/gui/net/sf/jailer/ui/DbConnectionDetailsEditor.java @@ -27,7 +27,11 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.net.URI; +import java.net.URL; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -35,8 +39,14 @@ import javax.swing.Icon; import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.Timer; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import net.sf.jailer.ui.DbConnectionDialog.ConnectionInfo; +import net.sf.jailer.ui.util.ConcurrentTaskControl; +import net.sf.jailer.ui.util.HttpDownload; +import net.sf.jailer.util.CsvFile; +import net.sf.jailer.util.CsvFile.Line; /** * "Connect with DB" dialog. @@ -104,6 +114,7 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { } private final Window parent; + private List driverlist; /** Creates new form DbConnectionDialog * @param forNew @@ -128,6 +139,32 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { dbUrl, user }; + try { + CsvFile drivers = new CsvFile(Environment.newWorkingFolderFile("driverlist.csv")); + driverlist = new ArrayList(drivers.getLines()); + } catch (Throwable t) { + driverlist = null; + } + + Arrays.asList(jar1, jar2, jar3, jar4, dbUrl).forEach(f -> f.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent e) { + check(); + } + @Override + public void insertUpdate(DocumentEvent e) { + check(); + } + @Override + public void changedUpdate(DocumentEvent e) { + check(); + } + private void check() { + List driverURLs = retrieveDriverURLs(driverlist); + downloadButton.setEnabled(driverURLs != null + && Arrays.asList(jar1, jar2, jar3, jar4).stream().allMatch(f -> f.getText().trim().isEmpty())); + } + })); if (needsTest) { testConnectionButton.setVisible(false); } else { @@ -212,8 +249,41 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { addWindowListener(new WindowAdapter() { @Override - public void windowOpened(WindowEvent e) { + public void windowOpened(WindowEvent evt) { alias.grabFocus(); + UIUtil.invokeLater(4, () -> { + List driverURLs = retrieveDriverURLs(driverlist); + if (forNew && driverURLs != null && downloadButton.isEnabled() && driverURLs.stream().allMatch( + url -> { + try { + return new File(Environment.newFile(HttpDownload.DOWNLOADFOLDER), HttpDownload.toFileName(new URL(url))).exists(); + } catch (Exception e) { + return false; + } + })) { + UIUtil.invokeLater(() -> { + List files = driverURLs.stream().map(url -> { + try { + return new File(Environment.newFile(HttpDownload.DOWNLOADFOLDER), HttpDownload.toFileName(new URL(url))).getAbsolutePath(); + } catch (Exception e) { + return ""; + } + }).collect(Collectors.toList()); + if (files.size() > 0) { + jar1.setText(files.get(0)); + } + if (files.size() > 1) { + jar2.setText(files.get(1)); + } + if (files.size() > 2) { + jar3.setText(files.get(2)); + } + if (files.size() > 3) { + jar4.setText(files.get(3)); + } + }); + } + }); } @Override public void windowClosed(WindowEvent e) { @@ -231,6 +301,17 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { UIUtil.initPeer(); } + protected List retrieveDriverURLs(List driverlist) { + if (driverlist != null) { + String url = dbUrl.getText().trim().replaceAll("^(\\w+:\\w+:).*", "$1"); + Line line = driverlist.stream().filter(l -> l.cells.get(1).startsWith(url)).findFirst().orElse(null); + if (line != null && !line.cells.get(4).isEmpty()) { + return new ArrayList(Arrays.asList(line.cells.get(4).split("\\s+"))); + } + } + return null; + } + /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is @@ -280,6 +361,7 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { importCBButton = new javax.swing.JButton(); feedbackLabel = new javax.swing.JLabel(); jSeparator1 = new javax.swing.JSeparator(); + downloadButton = new javax.swing.JButton(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle("Database Connection"); @@ -624,6 +706,20 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { gridBagConstraints.insets = new java.awt.Insets(0, 2, 16, 0); jPanel1.add(jSeparator1, gridBagConstraints); + downloadButton.setText("Download Driver"); + downloadButton.setEnabled(false); + downloadButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + downloadButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 44; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; + jPanel1.add(downloadButton, gridBagConstraints); + getContentPane().add(jPanel1, "card2"); pack(); @@ -753,6 +849,86 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { Toolkit.getDefaultToolkit().beep(); }//GEN-LAST:event_importCBButtonActionPerformed + private void downloadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_downloadButtonActionPerformed + List driverURLs = retrieveDriverURLs(driverlist); + if (driverURLs != null) { + final List files; + final Object LOCK = new Object(); + synchronized (LOCK) { + files = new ArrayList(); + } + + AtomicBoolean ok = new AtomicBoolean(true); + + @SuppressWarnings("serial") + final ConcurrentTaskControl concurrentTaskControl = new ConcurrentTaskControl( + this, "Downloading Driver") { + + @Override + protected void onError(Throwable error) { + UIUtil.showException(this, "Error", error); + ok.set(false); + closeWindow(); + } + + @Override + protected void onCancellation() { + ok.set(false); + closeWindow(); + } + }; + + ConcurrentTaskControl.openInModalDialog(this, concurrentTaskControl, + new ConcurrentTaskControl.Task() { + @Override + public void run() throws Throwable { + long[] total = { 0 }; + driverURLs.forEach(url -> { + try { + String result = HttpDownload.get(url, vol -> { + UIUtil.invokeLater(() -> { + total[0] += vol; + concurrentTaskControl.master.infoLabel.setText("Downloading... (" + total[0] / 1024 + "k)"); + }); + }); + if (result.length() == 0) { + throw new RuntimeException("cannot download \"" + url + "\""); + } + synchronized (LOCK) { + files.add(result.toString()); + } + } catch (Throwable t) { + throw new RuntimeException("cannot download \"" + url + "\"", t); + } + }); + + UIUtil.invokeLater(new Runnable() { + @Override + public void run() { + if (ok.get()) { + synchronized (LOCK) { + if (files.size() > 0) { + jar1.setText(files.get(0)); + } + if (files.size() > 1) { + jar2.setText(files.get(1)); + } + if (files.size() > 2) { + jar3.setText(files.get(2)); + } + if (files.size() > 3) { + jar4.setText(files.get(3)); + } + } + } + concurrentTaskControl.closeWindow(); + } + }); + } + }, "Downloading Driver"); + } + }//GEN-LAST:event_downloadButtonActionPerformed + protected void onSelect() { } @@ -760,6 +936,7 @@ public class DbConnectionDetailsEditor extends javax.swing.JDialog { public javax.swing.JTextField alias; private javax.swing.JButton cancelButton; public javax.swing.JTextField dbUrl; + private javax.swing.JButton downloadButton; public javax.swing.JTextField driverClass; private javax.swing.JButton exportCBButton; private javax.swing.JLabel feedbackLabel; diff --git a/src/main/gui/net/sf/jailer/ui/util/HttpDownload.java b/src/main/gui/net/sf/jailer/ui/util/HttpDownload.java new file mode 100644 index 000000000..a707c0eac --- /dev/null +++ b/src/main/gui/net/sf/jailer/ui/util/HttpDownload.java @@ -0,0 +1,128 @@ +/* + * Copyright 2007 - 2020 Ralf Wisser. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.sf.jailer.ui.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.function.Consumer; + +import net.sf.jailer.ui.Environment; + +public class HttpDownload { + + public static final String DOWNLOADFOLDER = "download"; + + public static String get(final String url, Consumer volConsumer) throws Throwable { + Throwable t = null; + StringBuilder result = new StringBuilder(); + try { + if (get(url, result, volConsumer) == 200) { + return result.toString(); + } + } catch (Throwable err) { + t = err; + } + try { + result = new StringBuilder(); + Process p = Runtime.getRuntime().exec("java -classpath " + Environment.newWorkingFolderFile("jailer.jar").getPath() + " -Djava.net.useSystemProxies=true " + HttpDownload.class.getName() + " " + url); + BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + + while ((line = input.readLine()) != null) { + result.append(line); + } + + input.close(); + } catch (Throwable err) { + err.printStackTrace(); + if (t != null) { + throw t; + } + } + return result.toString(); + } + + public static int get(final String url, final StringBuilder result, Consumer volConsumer) throws MalformedURLException, IOException { + URL theUrl; + theUrl = new URL(url); + long t0 = System.currentTimeMillis(); + URLConnection con = theUrl.openConnection(); + ((HttpURLConnection) con).setInstanceFollowRedirects(true); + InputStream in = con.getInputStream(); + int rc = ((HttpURLConnection) con).getResponseCode(); + if (rc == 200) { + File dir = Environment.newFile(DOWNLOADFOLDER); + dir.mkdir(); + String name = toFileName(theUrl); + File tmpFile = new File(dir, name + "." + System.currentTimeMillis()); + File file = new File(dir, name); + if (!file.exists()) { + OutputStream out = new FileOutputStream(tmpFile); + int c; + long total = 0; + long vol = 0; + while ((c = in.read()) != -1) { + out.write((char) c); + ++total; + ++vol; + long t1 = System.currentTimeMillis(); + if (t1 - t0 > 1000) { + t0 = t1; + if (volConsumer != null) { + volConsumer.accept(vol); + } + vol = 0; + } + } + out.close(); + if (!tmpFile.renameTo(file)) { + throw new RuntimeException("can't rename \"" + tmpFile.getAbsolutePath() + "\""); + }; + if (total < 1024 * 100L) { + return 0; + } + } + in.close(); + result.append(new File(DOWNLOADFOLDER, name).getAbsolutePath()); + } + return rc; + } + + public static String toFileName(URL theUrl) { + String name = theUrl.getPath().replaceAll("^.*/([^/]+)$", "$1"); + return name; + } + + public static void main(String args[]) { + final StringBuilder result = new StringBuilder(); + try { + get(args[0], result, null); + System.out.println(result); + } catch (Throwable e) { + e.printStackTrace(); + } + } + +}