2015-05-06 35 views
0

我已經嘗試通過Google和Stackoverflow搜索解決方案。關於這個問題有很多話題,但沒有任何幫助我。Java - 使用JSch的ssh-tunneling到mysqldatabase

import java.util.logging.Level; 
import java.util.logging.Logger; 

public class GTEVDatabaseConnection { 

private Connection connection = null; 
private static final int DATABASE_PORT = 3306, 
     LOCAL_PORT = 1234, 
     SSH_PORT = 22; 
private static final String DRIVER = "com.mysql.jdbc.Driver", 
     DATABASE = "myDatabase", 
     DATABASE_USER = "dbuser", 
     DATABASE_PASSWORD = "dbpasswd", 
     DATABASE_HOST = "example.de.mysql", 
     SSH_HOST = "ssh.example.de", 
     LOCAL_DATABASE_URL = "jdbc:mysql://localhost:" + LOCAL_PORT + "/" + DATABASE; 

public GTEVDatabaseConnection(String sshUsername, String sshPassword) throws SQLException { 
    Session sshSession = null; 
    try { 
     sshSession = new JSch().getSession(sshUsername, SSH_HOST, SSH_PORT); 
     sshSession.setPassword(sshPassword); 
     sshSession.setConfig("StrictHostKeyChecking", "no"); 
     sshSession.connect(); 
     sshSession.setPortForwardingL(LOCAL_PORT, DATABASE_HOST, DATABASE_PORT); 
     //sshSession.setPortForwardingR(DATABASE_PORT, "localhost", LOCAL_PORT); 

     Class.forName(DRIVER); 
     this.connection =  DriverManager.getConnection(LOCAL_DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD); //TODO com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure 
    } catch (ClassNotFoundException | JSchException ex) { 
Logger.getLogger(GTEVDatabaseConnection.class.getName()).log(Level.SEVERE,null, ex); 
     } finally { 
      if(this.connection != null){ 
       connection.close(); 
      } 
      if(sshSession != null && sshSession.isConnected()){ 
       sshSession.disconnect(); 
      } 
     } 
    } 

    public ResultSet execQuery(String query) throws SQLException { 
     return this.connection.createStatement().executeQuery(query); 
    } 
} 

的代碼應該做到底:
你應該能夠創建GTEVDatabaseConnection的對象。構造函數的參數是ssh-login的用戶名和密碼。您需要此登錄才能連接到我的MySql數據庫,因爲您只能通過ssh訪問它。
因此,當一個新對象被創建時,它會通過jsch.getSession(..)創建一個新的Session,並將PortForwarding連接到本地主機。
當我嘗試執行DriverManager.getConnection(...)時發生錯誤。 以下是錯誤:

SCHWERWIEGEND: null 
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure 

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:422) 
at com.mysql.jdbc.Util.handleNewInstance(Util.java:389) 
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1038) 
at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:627) 
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1013) 
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2239) 
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2270) 
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2069) 
at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:794) 
at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:44) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:422) 
at com.mysql.jdbc.Util.handleNewInstance(Util.java:389) 
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:399) 
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:325) 
at java.sql.DriverManager.getConnection(DriverManager.java:664) 
at java.sql.DriverManager.getConnection(DriverManager.java:247) 
at connection.GTEVDatabaseConnection.<init>(GTEVDatabaseConnection.java:44) 
at gui.DatabaseConnector.createConnection(DatabaseConnector.java:102) 
at gui.DatabaseConnector.lambda$start$0(DatabaseConnector.java:50) 
at gui.DatabaseConnector$$Lambda$67/2106702206.handle(Unknown Source) 
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) 
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) 
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) 
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) 
at javafx.event.Event.fireEvent(Event.java:198) 
at javafx.scene.Node.fireEvent(Node.java:8390) 
at javafx.scene.control.Button.fire(Button.java:185) 
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182) 
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96) 
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89) 
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) 
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) 
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) 
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) 
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) 
at javafx.event.Event.fireEvent(Event.java:198) 
at javafx.scene.Scene$MouseHandler.process(Scene.java:3758) 
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3486) 
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762) 
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2495) 
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:350) 
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275) 
at java.security.AccessController.doPrivileged(Native Method) 
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$350(GlassViewEventHandler.java:385) 
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$140/973574965.get(Unknown Source) 
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:404) 
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:384) 
at com.sun.glass.ui.View.handleMouseEvent(View.java:555) 
at com.sun.glass.ui.View.notifyMouse(View.java:927) 
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
at com.sun.glass.ui.win.WinApplication.lambda$null$145(WinApplication.java:101) 
at com.sun.glass.ui.win.WinApplication$$Lambda$36/1963387170.run(Unknown Source) 
at java.lang.Thread.run(Thread.java:745) 
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost. 
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2914) 
at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:559) 
... 68 more 

謝謝您的幫助。詢問更多細節,當我錯過了一些東西。 問候跟蹤器

+0

[這可能會幫助你(http://stackoverflow.com/questions/2983248/com-mysql-jdbc-exceptions-jdbc4-communicationsexception -communications-link-fai) –

+0

您建立連接並轉發端口的代碼看起來是正確的 - 您確定在服務器上有正確的數據庫端口嗎?如果MySQL服務器關閉或在其他端口上運行,則會出現此類錯誤消息。 建立連接後,您可以暫停程序的執行,並嘗試從命令行運行'telnet localhost 1234'?這將告訴你是否可以訪問數據庫服務器。 –

+0

另外,請確保您可以將DATABASE_HOST從本地機器解析爲IP。 –

回答

1

我發現了錯誤。服務器的防火牆阻塞了ssh-tunneling。所以我創建了以下代碼,這是可行的。

package connection; 
import com.jcraft.jsch.Channel; 
import com.jcraft.jsch.ChannelExec; 
import com.jcraft.jsch.JSch; 
import com.jcraft.jsch.JSchException; 
import com.jcraft.jsch.Session; 
import java.io.IOException; 
import java.io.InputStream; 
import java.sql.SQLException; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

public class GTEVDatabaseConnection { 

private final Channel channel; 
private static final int SSH_PORT = 22; 
private static final String DATABASE = "databasename", 
     DATABASE_USER = "databaseuser", 
     DATABASE_PASSWORD = "databasepasswd", 
     DATABASE_HOST = "databasehost", 
     SSH_HOST = "sshhost"; 

public GTEVDatabaseConnection(String sshUsername, String sshPassword) { 
    try { 
     Session sshSession = createSshSession(sshUsername, sshPassword); 
     this.channel = sshSession.openChannel("exec"); 
     ((ChannelExec) channel).setErrStream(System.err); 
    } catch (JSchException ex) { 
     Logger.getLogger(GTEVDatabaseConnection.class.getName()).log(Level.SEVERE, null, ex); 
     throw new SQLException("username or password didn´t work"); 
    } 
} 

/** 
* F&uuml;hrt eine SQL-Abfrage aus und liefert eine Tabelle der Ergebnisse 
* @param query Der SQL-Code 
* @return Tabelle der Ergebnisse. Erste Dimension = Zeile; zweite Dimension = Spalte 
* @throws SQLException Tritt auf, wenn der SQL-Befehl eine Fehler ausl&ouml;ste. (Fehler wird per System.err.println(...) ausgegeben) 
*/ 
public ArrayList<ArrayList<String>> execQuery(String query) throws SQLException { 
    ArrayList<ArrayList<String>> formattedResult = null; 
    try { 
     ((ChannelExec) channel).setCommand("mysql -u" + DATABASE_USER + " -p" + DATABASE_PASSWORD + " -h" + DATABASE_HOST + " -e'" + query + "' " + DATABASE); 
     InputStream in = channel.getInputStream(); 
     channel.connect(); 
     //TODO check whether sqlcode is correct 

     String result = readResult(in); 
     if(result == null){ 
      throw new SQLException("Invalid SQL-Code"); 
     } 
     formattedResult = new ArrayList<>(); 
     for(String row: result.split("\n")){ 
      ArrayList<String> fields = new ArrayList<>(); 
      fields.addAll(Arrays.asList(row.split("\t"))); 
      formattedResult.add(fields); 
     } 

     channel.disconnect(); 
    } catch (JSchException | IOException ex) { 
     Logger.getLogger(GTEVDatabaseConnection.class.getName()).log(Level.SEVERE, null, ex); 
    } 

    return formattedResult; 
} 

private String readResult(InputStream in) throws IOException { 
    if (in.available() <= 0) { 
     return null; 
    } 

    StringBuilder output = new StringBuilder(); 
    int nextByte; 
    do { 
     nextByte = in.read(); 
     output.append((char) nextByte); 
    } while (nextByte != -1); 

    return output.toString(); 
} 

private Session createSshSession(String sshUsername, String sshPassword) throws JSchException { 
    Session session = new JSch().getSession(sshUsername, SSH_HOST, SSH_PORT); 
    session.setPassword(sshPassword); 
    session.setConfig("StrictHostKeyChecking", "no"); 
    session.connect(); 
    return session; 
} 
} 

現在程序通過ssh連接,執行mysql-command並讀出結果。

它可能不太專業或「好」,因爲我不能使用ResultSet來迭代我的結果,但它的工作原理。


我試過不同的東西,以便通過telnet連接。

  • 當我沒有運行程序時,連接錯誤如預期。
  • 當我運行程序並暫停它以執行telnet localhost 1234時,cmd被清除並且沒有響應。
  • 當我嘗試鍵入內容時,cmd-prompt再次出現。所以我只認爲說看到一隻連接...
+0

理想的SO線程由一個問題和*一個*答案組成。請使用編輯功能來改善您的答案,而不是在一個線程中多次發帖。 – APC