Popular Posts
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... 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.... Enable SSL connection for Jsoup import org.jsoup.Connection; import org.jsoup.Jsoup; import javax.net.ssl.*; import java.io.IOException; import java.security.KeyManagement...
Blog Archive
Stats
Compile source via JavaCompiler
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;

/**
 * Created by nanashi07 on 15/12/13.
 */
public class CompileOnFly {

    public static void main(String args[]) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);
        out.println("package com.prhythm.output;");
        out.println();
        out.println("public class OutputImpl implements IOutput {");
        out.println();
        out.println("   public void printFor(String message) {");
        out.println("       System.out.printf(\"Print from %s : %s%n\", getClass().getName(), message);");
        out.println("   }");
        out.println();
        out.println("}");
        out.close();
        JavaFileObject file = new TextJavaObject("com.prhythm.output.OutputImpl", writer.toString());

        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
        JavaCompiler.CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);

        boolean success = task.call();
        for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
            System.out.printf("Code: %s%n", diagnostic.getCode());
            System.out.printf("Kind: %s%n", diagnostic.getKind());
            System.out.printf("Position: %s%n", diagnostic.getPosition());
            System.out.printf("StartPosition: %s%n", diagnostic.getStartPosition());
            System.out.printf("EndPosition: %s%n", diagnostic.getEndPosition());
            System.out.printf("Source: %s%n", diagnostic.getSource());
            System.out.printf("Message: %s%n", diagnostic.getMessage(null));
        }

        if (success) {
            try {
                URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{new File("").toURI().toURL()});
                Class<?> outClass = Class.forName("com.prhythm.output.OutputImpl", true, classLoader);
                Object outInstance = outClass.newInstance();
                outClass.getDeclaredMethod("printFor", new Class[]{String.class}).invoke(outInstance, "Bruce");
            } catch (ClassNotFoundException e) {
                System.err.println("Class not found: " + e);
            } catch (NoSuchMethodException e) {
                System.err.println("No such method: " + e);
            } catch (IllegalAccessException e) {
                System.err.println("Illegal access: " + e);
            } catch (InvocationTargetException e) {
                System.err.println("Invocation target: " + e);
            } catch (InstantiationException e) {
                System.err.println("Initialize instance: " + e);
            }
        }
    }
}

class TextJavaObject extends SimpleJavaFileObject {
    final String code;

    TextJavaObject(String name, String code) {
        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}

Output:

Print from com.prhythm.output.OutputImpl : Bruce
Annotation processor on Intellij IDEA
Project structure
+ ---- annotation- processor
| + ---- annotation- lib
|   + ---- build.gradle
|   + ---- src
|     + ---- main
|     | + ---- java
|     |   + ---- com
|     |     + ---- prhythm
|     |       + ---- annotations
|     |         + ---- PrintUsage.java
|     |         + ---- PrintUsageProcessor.java
|     | + ---- resources
|         + ---- META-INF
|           + ---- services
|             + ---- javax.annotation.processing.Processor
| + ---- foo
|   + ---- build.gradle
|   + ---- src
|     + ---- main
|       + ---- java
|         + ---- com
|           + ---- prhythm
|             + ---- foo
|               + ---- Bar.java
| + ---- build.gradle
| + ---- settings.gradle

PrintUsage.java
package com.prhythm.annotations;

/**
 * Print usage
 * Created by nanashi07 on 15/12/2.
 */
public @interface PrintUsage {
}

PrintUsageProcessor.java
package com.prhythm.annotations;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

/**
 * Processor of {@link PrintUsage}
 * Created by nanashi07 on 15/12/2.
 */
@SupportedAnnotationTypes("com.prhythm.annotations.PrintUsage")
public class PrintUsageProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();
        for (TypeElement typeElement : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                String msg = String.format("Use %s on %s#%s", typeElement, element.getEnclosingElement(), element);
                messager.printMessage(Diagnostic.Kind.NOTE, msg);
            }
        }
        return true;
    }
}

javax.annotation.processing.Processor
com.prhythm.annotations.PrintUsageProcessor

Bar.java
package com.prhythm.foo;

import com.prhythm.annotations.PrintUsage;

/**
 * Test class
 * Created by nanashi07 on 15/12/2.
 */
public class Bar {

    @PrintUsage
    public void move(String name) {

    }

    @PrintUsage
    public long time() {
        return System.currentTimeMillis();
    }

}

build.gradle (foo)
group 'com.prhythm'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.6

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile project(':annotation-lib')
}

Enable annotation processor on preference




Build with gradle

gradle clean build

result


References:



Type Memo

Note: ==================================================
Note: TypeElement: com.prhythm.annotations.PrintUsage
Note: TypeElement.getClass(): class com.sun.tools.javac.code.Symbol$ClassSymbol
Note: TypeElement.getSimpleName(): PrintUsage
Note: TypeElement.getEnclosedElements(): 
Note: TypeElement.getEnclosingElement(): com.prhythm.annotations
Note: TypeElement.getInterfaces(): java.lang.annotation.Annotation
Note: TypeElement.getNestingKind(): TOP_LEVEL
Note: TypeElement.getQualifiedName(): com.prhythm.annotations.PrintUsage
Note: TypeElement.getSuperclass(): none
Note: TypeElement.getTypeParameters(): 
Note: TypeElement.getAnnotationMirrors(): 
Note: TypeElement.asType(): com.prhythm.annotations.PrintUsage
Note: TypeElement.getKind(): ANNOTATION_TYPE
Note: TypeElement.getModifiers(): [public, abstract]
Note: ----------------------------------------------------------
Note: Element: move(java.lang.String)
Note: Element.getModifiers(): [public]
Note: Element.getClass(): class com.sun.tools.javac.code.Symbol$MethodSymbol
Note: Element.getKind(): METHOD
Note: Element.getAnnotationMirrors(): @com.prhythm.annotations.PrintUsage
Note: Element.getSimpleName(): move
Note: Element.asType(): (java.lang.String)void
Note: Element.getEnclosedElements(): 
Note: Element.getEnclosingElement(): com.prhythm.foo.Bar
Note: ----------------------------------------------------------
Note: Element: time()
Note: Element.getModifiers(): [public]
Note: Element.getClass(): class com.sun.tools.javac.code.Symbol$MethodSymbol
Note: Element.getKind(): METHOD
Note: Element.getAnnotationMirrors(): @com.prhythm.annotations.PrintUsage
Note: Element.getSimpleName(): time
Note: Element.asType(): ()long
Note: Element.getEnclosedElements(): 
Note: Element.getEnclosingElement(): com.prhythm.foo.Bar
Note: ==================================================
Note: TypeElement: com.prhythm.annotations.Mark
Note: TypeElement.getClass(): class com.sun.tools.javac.code.Symbol$ClassSymbol
Note: TypeElement.getSimpleName(): Mark
Note: TypeElement.getEnclosedElements(): value(),names()
Note: TypeElement.getEnclosingElement(): com.prhythm.annotations
Note: TypeElement.getInterfaces(): java.lang.annotation.Annotation
Note: TypeElement.getNestingKind(): TOP_LEVEL
Note: TypeElement.getQualifiedName(): com.prhythm.annotations.Mark
Note: TypeElement.getSuperclass(): none
Note: TypeElement.getTypeParameters(): 
Note: TypeElement.getAnnotationMirrors(): 
Note: TypeElement.asType(): com.prhythm.annotations.Mark
Note: TypeElement.getKind(): ANNOTATION_TYPE
Note: TypeElement.getModifiers(): [public, abstract]
Note: ----------------------------------------------------------
Note: Element: com.prhythm.foo.Yahoo
Note: Element.getModifiers(): [public, abstract]
Note: Element.getClass(): class com.sun.tools.javac.code.Symbol$ClassSymbol
Note: Element.getKind(): INTERFACE
Note: Element.getAnnotationMirrors(): @com.prhythm.annotations.Mark("2323")
Note: Element.getSimpleName(): Yahoo
Note: Element.asType(): com.prhythm.foo.Yahoo
Note: Element.getEnclosedElements(): home(java.lang.String)
Note: Element.getEnclosingElement(): com.prhythm.foo
Note: ----------------------------------------------------------
Note: Element: home(java.lang.String)
Note: Element.getModifiers(): [public, abstract]
Note: Element.getClass(): class com.sun.tools.javac.code.Symbol$MethodSymbol
Note: Element.getKind(): METHOD
Note: Element.getAnnotationMirrors(): @com.prhythm.annotations.Mark("110"),@com.prhythm.core.generic.http.annotation.Get("https://tw.yahoo.com")
Note: Element.getSimpleName(): home
Note: Element.asType(): (java.lang.String)java.lang.String
Note: Element.getEnclosedElements(): 
Note: Element.getEnclosingElement(): com.prhythm.foo.Yahoo
MEMO: Url rewrite on default site of IIS

Application Request Rounting is required. Install using web platform installer.

Enable Application Request Rounting.

Set server proxy setting.

Set Enable proxy checked and Enable disk cache unchecked.

Binding host name on default site. Using www.mvc.com as example.

Rewrite www.mvc.com to http://localhost:8080/mvc

Add rewrite rule on default site.

Add rule using blank rule template.

Set rewrite rule for www.mvc.com

The result of www.mvc.com

Add rewrite rule for other iis site on localhost. First, create a new web site, and add an host reference www.mysite.me.

Set rewrite rule for www.mysite.com

Add host binding of default site

The result of www.mysite.com

Display soap message in axis client

client-config.wsdd : should be placed in the root of classpath

<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig" xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

    <handler name="log" type="java:org.apache.axis.handlers.LogHandler">
        <!-- If true, output SOAP messages in the console ; otherwise, output in a file. -->
        <parameter name="LogHandler.writeToConsole" value="false" />
        <!-- Specifies the name of the output file when LogHandler.writeToConsole is false -->
        <parameter name="LogHandler.fileName" value="axis.log" />
    </handler>

    <globalConfiguration>
        <parameter name="disablePrettyXML" value="false" />
        <requestFlow>
            <handler type="log" />
        </requestFlow>
        <responseFlow>
            <handler type="log" />
        </responseFlow>
    </globalConfiguration>

    <transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender" />
    <transport name="local" pivot="java:org.apache.axis.transport.local.LocalSender" />
    <transport name="java" pivot="java:org.apache.axis.transport.java.JavaSender" />
</deployment>
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);
    }
}