Popular Posts
Activate IIS GIZP compress 1. Set metabase editable for editing. 2. Add a new extension for gzip. 3. Edit the file c:\windows\system32\inetsrv\MetaBase.xml Add file... Sublime Text hotkeys 編輯 Ctrl + X 刪除行 Ctrl + Enter 插入下一行 Ctrl + Shift + Enter 插入前一行 Ctrl + Shift + ↑ 往上移動一行 C... Tomcat GET encoding edit server.xml : <Connector     URIEncoding="UTF-8"     connectionTimeout="20000"     port="8080"     prot...
Stats
JUnit with Spring : orderly test method

When using junit with spring framework for test, to sort execute order of method, will use FixMethodOrder class. It is a little inconvenience because you need to name your test method in correct way.



Execute test method ordered by method name
package com.prhythm.test;

import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Created by nanashi07 on 15/6/20.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/application-context.xml")
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderedTest {

    @Test
    public void test1() {
        System.out.println("test1 executed");
    }

    @Test
    public void test2() {
        System.out.println("test2 executed");
    }

    @Test
    public void test3() {
        System.out.println("test3 executed");
    }

    @Test
    public void test4() {
        System.out.println("test4 executed");
    }
}
Result:
test1 executed
test2 executed
test3 executed
test4 executed


Now there is an another way to do it.



Create a order annotation for customize order
package org.springframework.test.context.junit4;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Created by nanashi07 on 15/6/20.
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    int value();
}
Create a junit runner extends SpringJUnit4ClassRunner
package org.springframework.test.context.junit4;

import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.springframework.test.context.TestContextManager;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
 
public class SpringJUnit4ClassOrderedRunner extends SpringJUnit4ClassRunner {

    /**
     * Constructs a new {@code SpringJUnit4ClassRunner} and initializes a
     * {@link TestContextManager} to provide Spring testing functionality to
     * standard JUnit tests.
     *
     * @param clazz the test class to be run
     * @see #createTestContextManager(Class)
     */
    public SpringJUnit4ClassOrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        Collections.sort(list, new Comparator<FrameworkMethod>() {
            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null)
                    return -1;

                return o1.value() - o2.value();
            }
        });
        return list;
    }
}
Run test as customized order
package com.prhythm.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.Order;
import org.springframework.test.context.junit4.SpringJUnit4ClassOrderedRunner;

/**
 * Created by nanashi07 on 15/6/20.
 */
@RunWith(SpringJUnit4ClassOrderedRunner.class)
@ContextConfiguration("classpath:spring/application-context.xml")
public class OrderedTest {

    @Test
    @Order(value = 4)
    public void test1() {
        System.out.println("test1 executed");
    }

    @Test
    @Order(value = 3)
    public void test2() {
        System.out.println("test2 executed");
    }

    @Test
    @Order(value = 2)
    public void test3() {
        System.out.println("test3 executed");
    }

    @Test
    @Order(value = 1)
    public void test4() {
        System.out.println("test4 executed");
    }
}
Result:
test4 executed
test3 executed
test2 executed
test1 executed
Tired of Hibernate? Try JDBI in your code

JDBI Quick sample



ICategoryDAO.java : create a data access interface (implement is not required)
package com.prhythm.erotic.task.data.dao;

import com.prhythm.erotic.entity.source.Category;
import com.prhythm.erotic.task.data.mapper.CategoryMapper;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
import org.skife.jdbi.v2.sqlobject.mixins.Transactional;

import java.util.List;

/**
 * Created by nanashi07 on 15/6/14.
 */
@RegisterMapper(CategoryMapper.class)
public interface ICategoryDAO extends Transactional<ICategoryDAO> {

    @SqlUpdate("insert into Category (Source, Url, Category, SubCategory, Enabled) values (:source, :url, :category, :subCategory, :enabled)")
    int insert(@BindBean Category category);

    @SqlUpdate("update Category set Source = :source, Category = :category, SubCategory = :subCategory, Enabled = :enabled where Url = :url")
    int update(@BindBean Category category);

    @SqlUpdate("update Category set Enabled = :enabled")
    int setEnabled(@Bind("enabled") boolean enabled);

    @SqlQuery("select * from Category where Url = :url")
    Category findCategory(@Bind("url") String url);

    @SqlQuery("select * from Category")
    List<Category> getCategories();

    void close();
}

CategoryMapper.java : create a object mapper for convertion
package com.prhythm.erotic.task.data.mapper;

import com.prhythm.erotic.entity.source.Category;
import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.tweak.ResultSetMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Created by nanashi07 on 15/6/14.
 */
public class CategoryMapper extends MetaDataSupport implements ResultSetMapper<Category> {

    @Override
    public Category map(int index, ResultSet r, StatementContext ctx) throws SQLException {
        Category category = new Category();
        if (hasColumn(r, "source")) category.setSource(r.getString("source"));
        if (hasColumn(r, "url")) category.setUrl(r.getString("url"));
        if (hasColumn(r, "category")) category.setCategory(r.getString("category"));
        if (hasColumn(r, "subCategory")) category.setSubCategory(r.getString("subCategory"));
        if (hasColumn(r, "enabled")) category.setEnabled(r.getBoolean("enabled"));
        return category;
    }

}

ICategoryService.java : create a data access service interface
package com.prhythm.erotic.task.data;

import com.prhythm.erotic.entity.source.Category;

import java.util.Collection;
import java.util.List;

/**
 * Created by nanashi07 on 15/6/14.
 */
public interface ICategoryService {

    /**
     * Retrive all categories
     *
     * @return
     */
    List<Category> getCategories();

    /**
     * Update categories
     *
     * @param categories
     * @return
     */
    int updateCategories(Collection<Category> categories);
}

CategoryServiceImpl.java : Implement the data access interface
package com.prhythm.erotic.task.data.impl;

import com.prhythm.erotic.entity.source.Category;
import com.prhythm.erotic.logging.LogHandler;
import com.prhythm.erotic.task.data.ICategoryService;
import com.prhythm.erotic.task.data.dao.ICategoryDAO;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IDBI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;

/**
 * Created by nanashi07 on 15/6/14.
 */
@Service
public class CategoryServiceImpl implements ICategoryService {

    @Autowired
    IDBI dbi;

    @Override
    public List<Category> getCategories() {
        Handle h = dbi.open();
        ICategoryDAO dao = h.attach(ICategoryDAO.class);

        List<Category> categories = dao.getCategories();

        dao.close();
        h.close();

        return categories;
    }

    @Override
    public int updateCategories(Collection<Category> categories) {
        int count = 0;

        Handle handle = dbi.open();

        try {
            updateCategories(handle, categories);
            handle.commit();
        } catch (Exception e) {
            LogHandler.error(e);
            handle.rollback();
        } finally {
            handle.close();
        }

        return count;
    }

    int updateCategories(Handle handle, Collection<Category> categories) {
        int count = 0;

        ICategoryDAO dao = handle.attach(ICategoryDAO.class);

        // Disable all items
        dao.setEnabled(false);

        for (Category c : categories) {
            Category found = dao.findCategory(c.getUrl());
            if (found == null) {
                count += dao.insert(c);
            } else {
                count += dao.update(c);
            }
        }

        return count;
    }
}

CategoryServiceTester.java : test usage
package com.prhythm.erotic.test.dao;

import com.google.common.base.Stopwatch;
import com.prhythm.erotic.entity.source.Category;
import com.prhythm.erotic.logging.LogHandler;
import com.prhythm.erotic.task.data.ICategoryService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skife.jdbi.v2.IDBI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Arrays;
import java.util.List;

/**
 * Created by nanashi07 on 15/6/14.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/application-context.xml")
public class CategoryServiceTester {

    @Autowired
    ICategoryService categoryService;

    @Test
    public void testGetCategories() {
        Stopwatch stopwatch = Stopwatch.createStarted();
        List<Category> categories = categoryService.getCategories();
        stopwatch.stop();
        LogHandler.info("%d items loaded in %s", categories.size(), stopwatch);
    }

    @Test
    public void testUpdateCategories() {
        // Get current items
        List<Category> categories = categoryService.getCategories();
        // Append 10 new items
        for (int i = 0; i < 10; i++) {
            Category c = new Category();
            c.setSource("Prhythm");
            c.setUrl("http://app.prhtyhm.com/sample/" + UUID.randomUUID());
            c.setCategory("Blog");
            c.setEnabled(true);
            categories.add(c);
        }

        Stopwatch stopwatch = Stopwatch.createStarted();
        int count = categoryService.updateCategories(categories);
        stopwatch.stop();
        LogHandler.info("%d items updated in %s", count, stopwatch);
    }

}

datasourc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${com.prhythm.erotic.datasource.driver}"/>
        <property name="url" value="${com.prhythm.erotic.datasource.temp.url}"/>
        <property name="username" value="${com.prhythm.erotic.datasource.user}"/>
        <property name="password" value="${com.prhythm.erotic.datasource.password}"/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager"/>

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean id="txDataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
        <constructor-arg ref="dataSource"/>
    </bean>

    <bean class="org.skife.jdbi.v2.DBI" depends-on="txManager">
        <constructor-arg ref="txDataSource"/>
        <property name="SQLLog">
            <bean class="com.prhythm.erotic.task.logging.SQLLogAppender"/>
        </property>
    </bean>

</beans>
javax.net.ssl.SSLHandshakeException: Connection closed by peer in Android 5.0 Lollipop

Recently, there is a error occurs when access website via ssl connection like below although it worked fine several days ago.

// Enable SSL connection
HttpsURLConnection.setDefaultSSLSocketFactory(new MySocketFactory1());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
});

URL address = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) address.openConnection();
InputStream in = urlConnection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[128];
int len = 0;
while ((len = in.read(buffer)) > 0) {
    baos.write(buffer, 0, len);
}
baos.close();
in.close();
MySocketFactory1.java
package com.prhythm.google.myapplication2.app;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by nanashi07 on 15/5/23.
 */
public class MySocketFactory1 extends SSLSocketFactory {

    SSLContext context;
    SSLSocketFactory factory;

    public MySocketFactory1() {
        try {
            init();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    void init() throws KeyManagementException, NoSuchAlgorithmException {
        context = SSLContext.getInstance("SSL");
        context.init(null, new X509TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        factory = context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return factory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return factory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return factory.createSocket(s, host, port, autoClose);
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return factory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return factory.createSocket(host, port, localHost, localPort);
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return factory.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return factory.createSocket(address, port, localAddress, localPort);
    }
}
javax.net.ssl.SSLHandshakeException: Connection closed by peer
05-24 07:54:11.626    1697-1716/com.prhythm.google.myapplication2.app E/Test﹕ error
    javax.net.ssl.SSLHandshakeException: Connection closed by peer
            at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
            at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:302)
            at com.android.okhttp.Connection.upgradeToTls(Connection.java:197)
            at com.android.okhttp.Connection.connect(Connection.java:151)
            at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:276)
            at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:211)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:373)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:323)
            at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:190)
            at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
            at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:25)
            at com.prhythm.google.myapplication2.app.MainActivity$1.doInBackground(MainActivity.java:41)
            at com.prhythm.google.myapplication2.app.MainActivity$1.doInBackground(MainActivity.java:26)
            at android.os.AsyncTask$2.call(AsyncTask.java:288)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)

After track and search from some technical articles, I found this TLS/SSL Default Configuration Changes, finally. Base on this, I've made some changes for my SSLSocketFactory to add cipher suites setting. Now it works fine again.

MySocketFactory2.java
package com.prhythm.google.myapplication2.app;

import com.prhythm.core.generic.util.Cube;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Set;

/**
 * Created by nanashi07 on 15/5/23.
 */
public class MySocketFactory2 extends SSLSocketFactory {

    SSLContext context;
    SSLSocketFactory factory;
    String type;

    public MySocketFactory2() {
        try {
            init();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    void init() throws KeyManagementException, NoSuchAlgorithmException {
        context = SSLContext.getInstance(type = Cube.from("SSL", "TLS").random());
        context.init(null, new TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                System.out.printf("[CLIENT] chain = %s, authType = %s%n", Arrays.toString(chain), authType);
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                System.out.printf("[SERVER] chain = %s, authType = %s%n", Arrays.toString(chain), authType);
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        }}, new SecureRandom());
        factory = context.getSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return getCipherSuites().toArray(String.class);
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return getCipherSuites().toArray(String.class);
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return configCipherSuites(factory.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return configCipherSuites(factory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return configCipherSuites(factory.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return configCipherSuites(factory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return configCipherSuites(factory.createSocket(address, port, localAddress, localPort));
    }

    Cube<String> getCipherSuites() {
        return Cube.from("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
                "SSL_DHE_DSS_WITH_DES_CBC_SHA",
                "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
                "SSL_DHE_RSA_WITH_DES_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                "SSL_DH_anon_WITH_DES_CBC_SHA",
                "SSL_DH_anon_WITH_RC4_128_MD5",
                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
                "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
                "SSL_RSA_WITH_DES_CBC_SHA",
                "SSL_RSA_WITH_NULL_MD5",
                "SSL_RSA_WITH_NULL_SHA",
                "SSL_RSA_WITH_RC4_128_MD5",
                "SSL_RSA_WITH_RC4_128_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
                "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA256",
                "TLS_DH_anon_WITH_AES_128_GCM_SHA256",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA256",
                "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
                "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
                "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDHE_RSA_WITH_NULL_SHA",
                "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
                "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDH_ECDSA_WITH_NULL_SHA",
                "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
                "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
                "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_ECDH_RSA_WITH_NULL_SHA",
                "TLS_ECDH_RSA_WITH_RC4_128_SHA",
                "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
                "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
                "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
                "TLS_ECDH_anon_WITH_NULL_SHA",
                "TLS_ECDH_anon_WITH_RC4_128_SHA",
                "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
                "TLS_FALLBACK_SCSV",
                "TLS_PSK_WITH_3DES_EDE_CBC_SHA",
                "TLS_PSK_WITH_AES_128_CBC_SHA",
                "TLS_PSK_WITH_AES_256_CBC_SHA",
                "TLS_PSK_WITH_RC4_128_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA",
                "TLS_RSA_WITH_AES_128_CBC_SHA256",
                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                "TLS_RSA_WITH_AES_256_CBC_SHA",
                "TLS_RSA_WITH_AES_256_CBC_SHA256",
                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                "TLS_RSA_WITH_NULL_SHA256")
                .where(new Cube.Selection<String>() {
                    @Override
                    public boolean predicate(String item, int index) {
                        return item.startsWith(type);
                    }
                });
    }

    Socket configCipherSuites(Socket socket) {
        if (!(socket instanceof SSLSocket)) return socket;
        SSLSocket sslsocket = (SSLSocket) socket;
        // 處理不合的cipher suite
        Set<String> suites = getCipherSuites().toSet();
        while (suites.size() > 0) {
            try {
                sslsocket.setEnabledCipherSuites(suites.toArray(new String[suites.size()]));
                break;
            } catch (Throwable e) {
                String message = e.getMessage();
                for (String cipher : suites) {
                    if (message.toLowerCase().contains(cipher.toLowerCase())) {
                        suites.remove(cipher);
                        System.out.printf("Cipher suite %s has been removed.%n", cipher);
                        break;
                    }
                }
            }
        }
        return socket;
    }
}
Gradle: error: unmappable character for encoding

Sometimes error occurs on gradle build cause of file encoding




Solved: add gradle vm option as below:

-Dfile.encoding=utf-8
Gradle: generate java source folder
task createSourceFolder {
    sourceSets*.java.srcDirs*.each { it.mkdirs() }
    sourceSets*.resources.srcDirs*.each { it.mkdirs() }
}
Dynamic add classpath path
public void appendClasspath(String path) throws NoSuchMethodException, MalformedURLException, InvocationTargetException, IllegalAccessException {
    Path externalResourcesFolder = Paths.get(path);
    ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
    if (sysLoader instanceof URLClassLoader) {
        Class<URLClassLoader> sysLoaderClass = URLClassLoader.class;

        // Use reflection to invoke the private addURL method
        Method method = sysLoaderClass.getDeclaredMethod("addURL", URL.class);
        method.setAccessible(true);
        method.invoke(sysLoader, externalResourcesFolder.toUri().toURL());

        System.out.printf("classpath: %s loaded%n", path);
    }
}
ASP.NET MVC 5 Attribute Routing
Enable attribute route
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // Enable attribute route
        RouteTable.Routes.MapMvcAttributeRoutes();

        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}
or
RouteConfig.cs
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            
        // Enable attribute route
        routes.MapMvcAttributeRoutes();

        AreaRegistration.RegisterAllAreas();

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}
Direct route
public class BookController : Controller {

    // Map to 'http://prhythm.com.tw/Pet/10'
    [Route("Pet/{id}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
Optional paramter
public class BookController : Controller {

    // Optional parameter id
    // Map to 'http://prhythm.com.tw/Pet'
    //     or 'http://prhythm.com.tw/Pet/5'
    [Route("Pet/{id?}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
Default parameter value
public class BookController : Controller {
    
    // Default value of id
    // Map to 'http://prhythm.com.tw/Pet'
    //     or 'http://prhythm.com.tw/Pet/10'
    [Route("Pet/{id=10}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
Paramter constrains
public class BookController : Controller {
    
    // Paramters constrains
    [Route("Pet/{id:int}")]
    public ActionResult Pet(int? id) {
        return View();
    }

}
ConstraintDescriptionExample
alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha}
bool Matches a Boolean value. {x:bool}
datetime Matches a DateTime value. {x:datetime}
decimal Matches a decimal value. {x:decimal}
double Matches a 64-bit floating-point value. {x:double}
float Matches a 32-bit floating-point value. {x:float}
guid Matches a GUID value. {x:guid}
int Matches a 32-bit integer value. {x:int}
length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
{x:length(1,20)}
long Matches a 64-bit integer value. {x:long}
max Matches an integer with a maximum value. {x:max(10)}
maxlength Matches a string with a maximum length. {x:maxlength(10)}
min Matches an integer with a minimum value. {x:min(10)}
minlength Matches a string with a minimum length. {x:minlength(10)}
range Matches an integer within a range of values. {x:range(10,50)}
regex Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)}
Route prefix
[RoutePrefix("Booking")]
public class BookController : Controller {

    // Map to 'http://prhythm.com.tw/Booking'
    //     or 'http://prhythm.com.tw/Booking/Pet'
    [Route]    
    [Route("Pet")]
    public ActionResult Pet() {
        return View();
    }

    // Map to 'http://prhythm.com.tw/Booking/Edit'
    [Route("Edit")
    public ActionResult Edit() {
        return View();
    }
}
Override route prefix
[RoutePrefix("Booking")]
public class BookController : Controller {

    // Override route prefix
    // Map to 'http://prhythm.com.tw/Pet'
    [Route("~/Pet")]
    public ActionResult Pet() {
        return View();
    }
}
Default route
[RoutePrefix("Booking")]
[Route("action=pet")]
public class BookController : Controller {

    // Map to 'http://prhythm.com.tw/Booking'
    //     or 'http://prhythm.com.tw/Booking/Pet'
    public ActionResult Pet() {
        return View();
    }

    // Map to 'http://prhythm.com.tw/Booking/Edit'
    public ActionResult Edit() {
        return View();
    }
}
Route area
[RouteArea("Backend")]
[RoutePrefix("News")]
public class NewsController : Controller {

    // Map to 'http://prhythm.com.tw/backend/news'
    public ActionResult Index() {
        return View();
    }

}
[RouteArea("Backend", AreaPrefix = "Admin")]
[RoutePrefix("News")]
public class NewsController : Controller {

    // Map to 'http://prhythm.com.tw/admin/news'
    public ActionResult Index() {
        return View();
    }

}

Reference http://sampathloku.blogspot.tw/2013/11/attribute-routing-with-aspnet-mvc-5.html

Reference http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

Asynchronous and deferred JavaScript execution explained
  • Normal execution <script>
    This is the default behavior of the <script> element. Parsing of the HTML code pauses while the script is executing. For slow servers and heavy scripts this means that displaying the webpage will be delayed.
  • Deferred execution <script defer>
    Simply put: delaying script execution until the HTML parser has finished. A positive effect of this attribute is that the DOM will be available for your script. However, since not every browser supports defer yet, don’t rely on it!
  • Asynchronous execution <script async>
    Don’t care when the script will be available? Asynchronous is the best of both worlds: HTML parsing may continue and the script will be executed as soon as it’s ready. I’d recommend this for scripts such as Google Analytics.

Reference : http://peter.sh/experiments/asynchronous-and-deferred-javascript-execution-explained/

Reference : http://www.html5rocks.com/en/tutorials/speed/script-loading/

Integration config for spring & hibernate
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.prhythm.spring.web.controller"/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="url" value="jdbc:hsqldb:mem:test"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.prhythm.spring.web.model"/>
        <!--<property name="annotatedClasses">-->
            <!--<list>-->
                <!--<value>com.prhythm.spring.web.model.Employee</value>-->
            <!--</list>-->
        <!--</property>-->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

</beans>