2018年11月22日 星期四

eclipse 啟動失敗 出現java.lang.ArrayIndexOutOfBoundsException

用了eclipse 這麼久,第一次遇到這個問題 記錄一下

在前次關閉eclipse都很正常的情況下,在啟動eclipse 竟然出現一個警訊 說無法啟用,要我去看LOG 檔 ,在eclipse\configuration\ 下可以看到log file 出現了以下的錯誤訊息:

.....
!ENTRY org.eclipse.osgi 4 0 2018-11-22 09:47:13.010
!MESSAGE Application error
!STACK 1
java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.util.Arrays.copyOfRange(Unknown Source)
at java.util.Arrays.copyOfRange(Unknown Source)
at org.eclipse.ui.internal.ide.ChooseWorkspaceDialog.lambda$7(ChooseWorkspaceDialog.java:380)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.collect(Unknown Source)
at org.eclipse.ui.internal.ide.ChooseWorkspaceDialog.createUniqueWorkspaceNameMap(ChooseWorkspaceDialog.java:382)
at org.eclipse.ui.internal.ide.ChooseWorkspaceDialog.createRecentWorkspacesComposite(ChooseWorkspaceDialog.java:325)
at org.eclipse.ui.internal.ide.ChooseWorkspaceDialog.createDialogArea(ChooseWorkspaceDialog.java:176)
at org.eclipse.jface.dialogs.TitleAreaDialog.createContents(TitleAreaDialog.java:158)
at org.eclipse.jface.window.Window.create(Window.java:426)
at org.eclipse.jface.dialogs.Dialog.create(Dialog.java:1095)
at org.eclipse.jface.window.Window.open(Window.java:783)
at org.eclipse.ui.internal.ide.ChooseWorkspaceDialog.prompt(ChooseWorkspaceDialog.java:114)
at org.eclipse.ui.internal.ide.application.IDEApplication.promptForWorkspace(IDEApplication.java:342)
at org.eclipse.ui.internal.ide.application.IDEApplication.checkInstanceLocation(IDEApplication.java:261)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:128)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610)
at org.eclipse.equinox.launcher.Main.run(Main.java:1519)



經上網查了一下,排除方式很容易,只要將eclipse/configuration/.settings中的所有文件刪除就可以正常開啟了,但刪除後,一些設定包含先前記下的workspace都會被重設,不過這比不能啟動來說,還算OK啦。

而有其它篇文章有寫到  刪除 eclipse/configuration/.settings/org.eclipse.ui.ide.prefs kot. 就可以,因我已先整個目錄都刪除了,沒機會實測,等下次再碰到時再確認看看。

參考資料:

2018年11月9日 星期五

JavaScript object 如何轉換成 JSON string


有時想要查看Json的內容,就直接output來看
但看到的結果就是object

這時就要用JSON.stringify() 轉換成 string
這個其實很常用,但常常忘了,就直接output
所以寫下來才不會浪費一些時間

作法如下:


var obj={"name":"binchen"};

console.log(obj);

console.log(JSON.stringify(obj));


產出會如下圖:
用console看是沒啥差,但用alert 

請點這裡看線上Demo

2018年11月6日 星期二

HttpClient 連結Https 出現javax.net.ssl.SSLHandshakeException 問題排除


最近在排除一個HTTPS API 連線的問題,當連線時會丟出以下錯誤

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.Alerts.getSSLException(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.recvAlert(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.writeRecord(Unknown Source)
    at sun.security.ssl.AppOutputStream.write(Unknown Source)
    at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
    at java.io.BufferedOutputStream.flush(Unknown Source)
    at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:506)
    at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)
    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)


這個在二三年前運行正常,看來應該是目前SSL各Server都修補SSLv3漏洞,目前都僅支援TLS,所以才變成不能使用
經查詢,蠻多人都有遇到此問題,大致問題點是無法進行憑證確認,找了很多方法其實都沒法排除,相關測過的如下,如想要看我的排除法,請直接看第4點即可:
1. 在JVM 啟動時加上  -Dhttps.protocols=TLSv1.2,TLSv1.1,TLSv1.0
首先,該異常發生的時機,是在Client與Server之間進行的Handshake的過程中,Client與Server之間的有效數據傳輸還沒有開始。
原因一:針對上述Handshake的過程,該異常往往發生在第4步,即Client得到服務器的數字證書時,向CA驗證證書有效性時。
Client在試圖向可信CA進行驗證時,發現Server引用的CA,沒有出現在Client的Trust Store中。
原因二:此外,該異常也可能是由於Client與Server所使用的SSL / TLS版本不一致服務器使用的TLS版本高,而Client支持的TLS版本低。
我試了之後沒反應 ,也有照著其它加上 -Djavax.net.debug=all  出來的錯誤訊息和該網友的差不多,但還是不能排除,有另一解法說更新JDK到1.8 但該系統沒全面測過,所以還是作罷。
參考資料:
2. 更新jar  檔
部分網友解釋:是因為JDK中的JCE的安全機制導致錯誤,要去oracle的官網下載對應的JCE包替換JDK中的JCE包。
jce所在地址:%JAVA_HOME%\ jre \ lib \ security裡的local_policy.jar,US_export_policy.jar
JDK7 http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
JDK8 http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
但是我更新後和該網友一樣沒有解決,而他排除的方式是在請求連接之前,加上
System.setProperty(“https.protocols”,“TLSv1.2,TLSv1.1,SSLv3”);
但我試了還是一樣不行
參考資料:
https://blog.csdn.net/gege87417376/article/details/77936507
3.匯入證書簽章
因為turst失敗,所以要將證書匯入到伺服器,先將Server的憑證取回,可利用IE 查看該網站的SSL ,再進行匯出轉匯入,該動作如下:
1.用IE打開需要連接的HTTPS網址,會彈出如下對話框:
2.單擊“查看證書”,在彈出的對話框中選擇“詳細信息”,然後再單擊“複製到文件”,根據提供的嚮導生成待訪問網頁的證書文件
3.嚮導第一步,歡迎界面,直接單擊“下一步”,
4.嚮導第二步,選擇導出的文件格式,默認,單擊“下一步”,
5.嚮導第三步,輸入導出的文件名,輸入後,單擊“下一步”,
6.嚮導第四步,單擊“完成”,完成嚮導
7.最後彈出一個對話框,顯示導出成功
8.用keytool的工具把剛才導出的證書倒入本地keystore.Keytool命令在<java主> \ BIN \下,打開命令行窗口,並到<java主> \ lib \ security中\目錄下,運行下面的命令:
keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
其中參數別名後跟的值是當前證書在密鑰庫中的唯一標識符,但是大小寫不區分;參數文件後跟的是剛才通過IE導出的證書所在的路徑和文件名;如果你想刪除剛才導入到密鑰庫的證書,可以用命令:
keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
但照做後還是一樣無法排除 ><。
參考資料:
4.最後排除的方式,為採用  httpclient跳過https請求的驗證的方式,實做驗證的方式然後自己加入讓他避掉原本網站要求
我們使用的是httpclient,其版本如下:

<dependency>
  <groupId>commons-httpclient</groupId>
  <artifactId>commons-httpclient</artifactId>
  <version>3.1</version>
</dependency>

接 下來要實作2個class,第一個是MyX509TrustManager(這個方法直接實現X509TrustManager,X509TrustManager在javax.net.ssl.X509TrustManager裡面),這串可以都不用更換即可使用。
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;

public class MyX509TrustManager implements X509TrustManager {
    /* (non-Javadoc)
    * @see javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String)
    */
    public void checkClientTrusted(X509Certificate[] arg0, String arg1)
            throws CertificateException {

    }
    /* (non-Javadoc)
     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String)
     */
    public void checkServerTrusted(X509Certificate[] arg0, String arg1)
            throws CertificateException {

    }
    /* (non-Javadoc)
     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
     */
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}
接下來實作MySecureProtocolSocketFactory ,這個我調整了getInstance("SSL"),原本他是採行SSL,但現行伺服端加密方法都因資安考量,只限TLSv1 以上的才可以trust,所以記得要改一下,不然是不能排除問題的,目前在網路上類似的參考案例都是 getInstance("SSL"),所以用了大多會無法排除,要特別注意。
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClientError;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;

public class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory {

    //這裡新增一個屬性,主要目的就是來獲取ssl跳過驗證
    private SSLContext sslContext = null;
    /**
     * Constructor for MySecureProtocolSocketFactory.
     */
    public MySecureProtocolSocketFactory() {
    }
    /**
     * 這個建立一個獲取SSLContext的方法,匯入MyX509TrustManager進行初始化
     * @return
     */
    private static SSLContext createEasySSLContext() {
        try {
            SSLContext context = SSLContext.getInstance("TLSv1");
            context.init(null, new TrustManager[] { new MyX509TrustManager() },
                    null);
            return context;
        } catch (Exception e) {
            throw new HttpClientError(e.toString());
        }
    }

    /**
     * 判斷獲取SSLContext
     * @return
     */
    private SSLContext getSSLContext() {
        if (this.sslContext == null) {
            this.sslContext = createEasySSLContext();
        }
        return this.sslContext;
    }
    //後面的方法基本上就是帶入相關引數就可以了
    /*
     * (non-Javadoc)
     *
     * @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String,
     *      int, java.net.InetAddress, int)
     */
    public Socket createSocket(String host, int port, InetAddress clientHost,int clientPort) throws IOException, UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(host, port,clientHost, clientPort);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.commons.httpclient.protocol.ProtocolSocketFactory#createSocket(java.lang.String,
     *      int, java.net.InetAddress, int,
     *      org.apache.commons.httpclient.params.HttpConnectionParams)
     */
    public Socket createSocket(final String host, final int port,final InetAddress localAddress, final int localPort,
                               final HttpConnectionParams params) throws IOException,UnknownHostException, ConnectTimeoutException {
        if (params == null) {
            throw new IllegalArgumentException("Parameters may not be null");
        }
        int timeout = params.getConnectionTimeout();
        if (timeout == 0) {
            return createSocket(host, port, localAddress, localPort);
        } else {
            return ControllerThreadSocketFactory.createSocket(this, host, port,localAddress, localPort, timeout);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see SecureProtocolSocketFactory#createSocket(java.lang.String,int)
     */
    public Socket createSocket(String host, int port) throws IOException,UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(host, port);
    }

    /*
     * (non-Javadoc)
     *
     * @see SecureProtocolSocketFactory#createSocket(java.net.Socket,java.lang.String,int,boolean)
     */
    public Socket createSocket(Socket socket, String host, int port,boolean autoClose) throws IOException, UnknownHostException {
        return getSSLContext().getSocketFactory().createSocket(socket, host,port, autoClose);
    }
}
最後,我們在使用前加上即可避掉驗證
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
/*
 * 利用HttpClient進行post請求的工具類
 */
public class HttpClientUtil {

    public static String doGet(String url) throws Exception {
        //宣告
        ProtocolSocketFactory fcty = new MySecureProtocolSocketFactory();
        //加入相關的https請求方式
        Protocol.registerProtocol("https", new Protocol("https", fcty, 443));
        //傳送請求即可
        org.apache.commons.httpclient.HttpClient httpclient = new org.apache.commons.httpclient.HttpClient();
        GetMethod httpget = new GetMethod(url);
        System.out.println("======url:" + url);
        try {
            httpclient.executeMethod(httpget);
            return httpget.getResponseBodyAsString();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new Exception(ex.getMessage());
        } finally {
            httpget.releaseConnection();
        }
    }

}
參考資料: