2012년 11월 12일 월요일

String split() 사용

1. 일반적으로 생각하는 split() 함수 사용

 > 아래와 같이 String 을 split() 함수를 이용해 나눌 수 있다.

String line = "a,b,c,d,e,f";
String[] result = line.split(",");

결과:
result ==> [a, b, c, d, e, f]


2. delimiter 사이에 빈 문자 처리

 > 아래와 같은 경우 빈 string 도 결과 배열에 포함한다.

String line = "a,,c,d,e,f";
String[] result = line.split(",");

결과:
result ==> [a, , c, d, e, f]

 > 아래와 같은 경우 빈 string 을 결과 배열에 포함하지 않는다.

String line = "a,,c,d,e,f";
String[] result = line.split("(,+)");

결과:
result ==> [a, c, d, e, f]

설명:
정규 표현 (,+) 의 의미는 , 이 하나 이상을 표현한다.
결국 연달아 , 이 반복되는 경우 하나의 delimiter 로 인식한다.

Regex 참고: http://moonlighting.tistory.com/52

2012년 10월 4일 목요일

Array 정렬

Arrays.sort() 함수를 이용해 배열을 정렬할 수 있다.

Comparator 를 구현하여 정렬 방식을 조절할 수 있다.

예) 파일 목록을 디렉토리, 파일 순으로 정렬하면서 알파벳 순으로 정렬하는 코드

File[] files = file.listFiles();

Arrays.sort(files, new Comparator<File>() {
public int compare(File f1, File f2) {
boolean d1 = f1.isDirectory();
boolean d2 = f2.isDirectory();
if (d1 && !d2) {
return -1;
} else if (!d1 && d2) {
return 1;
} else {
return f1.getName().toUpperCase().compareTo(f2.getName().toUpperCase());
}
}
});

정리) compare() 함수에 -1 을 리턴하면 앞의 값을 앞으로 1 을 리턴하면 뒤의 값을 앞으로 0 을 리턴하면 변경 없이 정렬이 된다.

2012년 9월 13일 목요일

Pool 예제

솔직히 Pool 에 대해 제가 정확히 이해했는지 자신 없습니다. 아래 내용은 제 사견입니다.

Pool 은 미리 사용하려는 양 만큼의 버퍼를 할당해 놓고 객체를 사용하고 사용한 객체는 다시 버퍼에 넣고 재활용하는 방식의 방법입니다.

이런 방식의 장점으로는 버퍼를 재활용함으로 인해 객체 할당 및 제거에 드는 시간을 없앨 수 있다는 점이 있겠습니다.

Pool.java
 - queue 가 두개 필요합니다. 유휴 버퍼와 사용 가능 버퍼로 나눕니다.
 - 유휴 버퍼는 미리 빈 객체들로 원하는 갯수 만큼 채워 넣습니다.
 - 사용 가능 버퍼에는 유휴 버퍼에서 얻은 사용 객체로 하나씩 채워지게 됩니다.
 - 사용 가능 버퍼를 다 사용하고 나면 다시 유휴 버퍼로 옮겨 넣음으로써 나중에 다시 사용 가능하게 됩니다.

소스 코드 :
package pool.test;

import java.util.concurrent.LinkedBlockingQueue;

/**
 *
 * @param <T>
 */
public class Pool<T> {
    
    private LinkedBlockingQueue<T> free = new LinkedBlockingQueue<T>();
    private LinkedBlockingQueue<T> work = new LinkedBlockingQueue<T>();
    
    /**
     * 
     * @param creator
     * @param count
     */
    public Pool(PoolObjectCreator<T> creator, int count) {
        for (int i = 0;i < count; i++) {
            free.add(creator.create());
        }
    }
    
    /**
     * 
     * @return
     */
    public final T acquire() {
        return free.size() > 0 ? free.remove() : null;
    }
    
    /**
     * 
     * @param item
     */
    public final void enqueue(T item) {
        work.add(item);
    }
    
    /**
     * 
     * @return
     */
    public final T dequeue() {
        return work.size() > 0 ? work.remove() : null;
    }
    
    /**
     * 
     * @param item
     */
    public final void release(T item) {
        free.add(item);
    }
    
    /**
     *
     * @param <T>
     */
    public static interface PoolObjectCreator<T> {
        public T create();
    }
}

아래 내용은 Pool 사용 예제 입니다.
 - Producer 스레드에서는 1초 마다 pool 에 데이터를 넣습니다.
 - Consumer 스레드에서는 pool 에서 데이터를 받아서 화면에 값을 출력합니다.

Main.java :
package pool.test;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 *
 */
public class Main {
    
    private Consumer consumer;
    private Producer producer;
    
    /**
     * 
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        
        Main main = new Main();
        main.start();
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        reader.readLine();
        
        main.stop();
        
        System.out.println("Done");
    }
    
    /**
     * 
     */
    public void start() {
        
        Pool<Container> pool = new Pool<Container>(new Pool.PoolObjectCreator<Container>() {
            @Override
            public Container create() {
                return new Container();
            }
        } , 10);
        
        
        producer = new Producer(pool);
        consumer = new Consumer(pool);
        
        producer.start();
        consumer.start();
    }
    
    /**
     * 
     */
    public void stop() {
        
        producer.interrupt();
        consumer.interrupt();
        
    }
    
    /**
     * 
     */
    public class Container {
        public int x;
    }
    
    
    
    /**
     *
     */
    public class Producer extends Thread {
        
        private Pool<Container> pool;
        
        public Producer(Pool<Container> pool) {
            this.pool = pool;
        }

        @Override
        public void run() {
            super.run();
            
            try {
                
                System.out.println("Producer :: start");
                
                int count = 0;
                
                Container container = null;
                while (!Thread.interrupted()) {
                    
                    if ((container = pool.acquire()) != null) {
                        
                        container.x = count++;
                        
                        pool.enqueue(container);
                        
                        Thread.sleep(1000);
                        
                    } else {
                        Thread.sleep(10);
                    }
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("Producer :: done");
            }
        }
    }
    
    
    /**
     *
     */
    public class Consumer extends Thread {
        
        private Pool<Container> pool;
        
        public Consumer(Pool<Container> pool) {
            this.pool = pool;
        }

        @Override
        public void run() {
            super.run();
            
            try {
                
                System.out.println("Consumer :: start");
                
                Container container = null;
                while (!Thread.interrupted()) {
                    
                    if ((container = pool.dequeue()) != null) {
                        
                        System.out.printf("x : %d\n", container.x);
                        
                        pool.release(container);
                        
                    } else {
                        Thread.sleep(10);
                    }
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("Consumer :: done");
            }
        }
    }
    
}


SocketChannel 이용한 non-blocking server/client 예제

예제는 SocketChannel 을 이용한 non-blocking 서버와 클라이언트 입니다.
서버는 여러개의 클라이언트 접속을 처리합니다.
클라이언트는 서버에 메시지를 전달할 수 있고 서버는 전달받은 메시지를 화면에 출력합니다.

실행 예)

- 서버측 -

Server :: started
Server :: waiting for accept
Server :: accepted - client [java.nio.channels.SocketChannel[connected local=/127.0.0.1:9891 remote=/127.0.0.1:61334]]
Client :: started
From Client : Hello
From Client : Hello World

- 클라이언트 측 -

Client :: started
Client :: connected
From Server : Welcome
Hello
[write :: text : Hello / len : 5]
Hello World
[write :: text : Hello World / len : 11]

폴더 구조:


소스 코드:


Abortable.java : 간단하게 종료를 나타내는 클래스를 만들었습니다.
package com.tcpsample;

public class Abortable {
    public boolean done = false;
    
    public Abortable() {
        init();
    }
    
    public void init() {
        done = false;
    }
    
    public boolean isDone() {
        return done;
    }
}

Server.java
- ServerThread 클래스는 accept 처리하는 스레드입니다.
- ClientHandlerThread 클래스는 접속한 client 를 처리하는 스레드입니다.
package com.tcpsample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 *
 */
public class Server {
    
    public static final int PORT_NUMBER = 9891;
    
    private Abortable abortable;
    private ServerThread serverThread;
    
    /**
     * 
     */
    public Server() {
        abortable = new Abortable();
    }
    
    /**
     * 
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        
        
        Server server = new Server();
        server.start();
        
        Thread.sleep(500);
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        reader.readLine();
        
        server.stop();

        System.out.println("BYE");
    }
    
    /**
     * start server
     */
    public void start() {
        
        abortable.init();
        
        if (serverThread == null || !serverThread.isAlive()) {
            serverThread = new ServerThread(abortable);
            serverThread.start();
        }
    }
    
    /**
     * stop server
     */
    public void stop() {
        
        abortable.done = true;
        
        if (serverThread != null && serverThread.isAlive()) {
            serverThread.interrupt();
        }
        
    }
    
    /**
     *
     */
    public class ServerThread extends Thread {
        
        private Abortable abortable;
        private List<Thread> clientList = new ArrayList<Thread>();
        
        public ServerThread(Abortable abortable) {
            this.abortable = abortable;
        }

        @Override
        public void run() {
            super.run();
            
            ServerSocketChannel server = null;
            Selector selector = null;
            
            try {
                
                System.out.println("Server :: started");
                
                server = ServerSocketChannel.open();
                server.socket().bind(new InetSocketAddress("", PORT_NUMBER));
                server.configureBlocking(false);
                
                selector = Selector.open();
                server.register(selector, SelectionKey.OP_ACCEPT);
                
                System.out.println("Server :: waiting for accept");
                
                while (!Thread.interrupted() && !abortable.isDone()) {
                    selector.select(3000);
                    
                    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                    while (iter.hasNext()) {
                        
                        SelectionKey key = iter.next();
                        if (key.isAcceptable()) {
                            
                            SocketChannel client = server.accept();
                            
                            if (client != null) {
                                System.out.printf("Server :: accepted - client [%s]\n", client);
                                Thread t = new ClientHandlerThread(abortable, client);
                                t.start();
                                clientList.add(t);
                            }
                        }
                    }
                }
                
            } catch (Exception e) {
                
                e.printStackTrace();
                
            } finally {
                
                for (Thread t : clientList) {
                    
                    if (t != null && t.isAlive())
                        t.interrupt();
                    
                    try {
                        t.join(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                if (server != null) {
                    
                    try {
                        server.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println("Server :: done");
                
            }
        }
    }

    /**
     *
     */
    public class ClientHandlerThread extends Thread {
        
        private Abortable abortable;
        private SocketChannel client;
        
        public ClientHandlerThread(Abortable abortable, SocketChannel client) {
            this.abortable = abortable;
            this.client = client;
        }

        @Override
        public void run() {
            super.run();
            
            Selector selector = null;
            Charset cs = Charset.forName("UTF-8");
            
            boolean done = false;
            
            try {
                
                System.out.println("Client :: started");
                
                client.configureBlocking(false);
                selector = Selector.open();
                
                client.register(selector, SelectionKey.OP_READ);
                
                // send welcome message
                client.write(ByteBuffer.wrap(new String("Welcome").getBytes()));
                
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                
                while (!Thread.interrupted() && !abortable.isDone() && !done) {
                    selector.select(3000);
                    
                    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                    while (!abortable.isDone() && iter.hasNext() && !done) {
                        SelectionKey key = iter.next();
                        if (key.isReadable()) {
                            int len = client.read(buffer);
                            if (len < 0) {
                                done = true;
                                break;
                            } else if (len == 0) {
                                continue;
                            }
                            buffer.flip();
                            
                            CharBuffer cb = cs.decode(buffer);
                            
                            System.out.printf("From Client : ");
                            while (cb.hasRemaining()) {
                                System.out.printf("%c", cb.get());
                            }
                            System.out.println();
                            
                            buffer.compact();
                        }
                    }
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                
                if (client != null) {
                    try {
                        client.socket().close();
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println("Client :: bye");
            }
        }
    }
}

Client.java
- ClientThread 클래스는 서버에 접속하고 통신하는 스레드입니다.
package com.tcpsample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
 *
 */
public class Client {
    
    private Abortable abortable = new Abortable();
    private ClientThread clientThread;
    
    /**
     * 
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        
        Client client = new Client();
        client.start("127.0.0.1", Server.PORT_NUMBER);
        
        Thread.sleep(500);

        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        
        while (true) {
            String line = reader.readLine();
            
            if (line.equals("quit"))
                break;
            
            try {
                client.sayToServer(line);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
            
        }
        
        client.stop();
         
        System.out.println("BYE");
    }
    
    /**
     * start client
     * 
     * @param host
     * @param port
     */
    public void start(String host, int port) {
        
        abortable.init();
        
        
        if (clientThread == null || !clientThread.isAlive()) {
            clientThread = new ClientThread(abortable, host, port);
            clientThread.start();
        }
    }
    
    /**
     * stop client
     */
    public void stop() {
        
        abortable.done = true;
        
        if (clientThread != null && clientThread.isAlive()) {
            clientThread.interrupt();
        }
        
    }
    
    /**
     * 
     * @param text
     * @throws IOException
     */
    public void sayToServer(String text) throws IOException {
        clientThread.sayToServer(text);
    }
    
    /**
     *
     */
    public class ClientThread extends Thread {
        
        private Abortable abortable;
        private String host;
        private int port;
        private SocketChannel client;
        
        /**
         * 
         * @param abortable
         * @param host
         * @param port
         */
        public ClientThread(Abortable abortable, String host, int port) {
            this.abortable = abortable;
            this.host = host;
            this.port = port;
        }

        /**
         * 
         * @param text
         * @throws IOException 
         */
        public void sayToServer(String text) throws IOException {
            int len = client.write(ByteBuffer.wrap(text.getBytes()));
            System.out.printf("[write :: text : %s / len : %d]\n", text, len);
        }

        @Override
        public void run() {
            super.run();
            
            boolean done = false;
            Selector selector = null;
            Charset cs = Charset.forName("UTF-8");
            
            try {
                
                System.out.println("Client :: started");
                
                client = SocketChannel.open();
                client.configureBlocking(false);
                client.connect(new InetSocketAddress(host, port));
                
                selector = Selector.open();
                client.register(selector, SelectionKey.OP_READ);
                
                while (!Thread.interrupted() && !abortable.isDone() && !client.finishConnect()) {
                    Thread.sleep(10);
                }
                
                System.out.println("Client :: connected");
                
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                
                while (!Thread.interrupted() && !abortable.isDone() && !done) {
                    
                    selector.select(3000);
                    
                    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
                    while (!Thread.interrupted() && !abortable.isDone() && !done && iter.hasNext()) {
                        SelectionKey key = iter.next();
                        if (key.isReadable()) {
                            int len = client.read(buffer);
                            if (len < 0) {
                                System.out.println("Client :: server closed");
                                done = true;
                                break;
                            } else if (len == 0) {
                                continue;
                            }
                            buffer.flip();
                            
                            CharBuffer cb = cs.decode(buffer);
                            
                            System.out.printf("From Server : ");
                            while (cb.hasRemaining()) {
                                System.out.printf("%c", cb.get());
                            }
                            System.out.println();
                            
                            buffer.compact();
                        }
                    }
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                
                if (client != null) {
                    try {
                        client.socket().close();
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println("Client :: done");
            }
            
        }
    }
}


2012년 6월 20일 수요일

enum 사용

간단한 선언
예)
public enum Types {
 TYPE1, TYPE2, TYPE3;
}

String 값을 대입
선언 예)
public enum Types {
 TYPE1("Type 1"),
 TYPE2("Type 2"),
 TYPE3("Type 3");

 private String name;
 Types(String name) {
  this.name = name;
 }

 @Override
 public String toString() {
  return name;
 }
}

사용 예)
package com.enumtest;

public class EnumTest {
 public static void main(String[] args) throws Exception {
  Types type = Types.TYPE1;
  System.out.println(type);
 }
}

결과:
Type 1

2012년 6월 4일 월요일

Ant 사용

소개


Command line 으로 java 프로젝트를 빌드하기에는 패키지명까지 일일이 적어야 하는 번거로움이 있기 때문에 빌드를 도와주는 빌드 시스템을 주로 이용하게 되며 그 중 ant 빌드 시스템 사용법을 기술


1. Ant 설치

  1. 다운로드 : http://ant.apache.org/bindownload.cgi
  2. 원하는 경로에 압축을 해제
  3. $ANT_HOME/bin 경로를 PATH 환경 변수에 추가

2. 소스 코드 및 build.xml 작성

예제 project : hello

최종 폴더 구조
hello/
 + src/
   + com/sample/hello/
     + Hello.java
 + build.xml
 + dist/
   + com/sample/hello/
     + Hello.class

Hello.java
package com.sample.hello;

public class Hello {
 public static void main(String[] args) throws Exception {
  System.out.println("Hello!");
 }
}

build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project name="hello" default="compile">

 <property name="build.dir" location="dist" />

 <target name="compile">
  <mkdir dir="${build.dir}" />
  <javac destdir="${build.dir}" source="1.6" target="1.6" debug="true"
   deprecation="false" optimize="false" failonerror="true">
   <src path="src" />
  </javac>
 </target>
</project>

설명
  • project - root 노드
    • default 속성 - ant 수행시 기본으로 수행할 target 지정
  • property - variable 지정
  • target - 작업 수행 단위
  • mkdir - 폴더 생성
  • javac - java compile
  • src - 소스 경로 지정

3. compile 하기

$ pwd
/path/to/hello
$ ant compile

4. 실행하기

$ pwd
/path/to/hello
$ java -cp dist com.sample.hello.Hello
Hello!

!) Ant 로 실행하기
아래 코드를 project 태그 안에 추가한다.
<target name="run">
 <java classname="com.sample.hello.Hello">
  <classpath>
   <pathelement location="${build.dir}" />
  </classpath>
 </java>
</target>

실행
$ ant run
Buildfile: /path/to/hello/build.xml

run:
[java] Hello!

BUILD SUCCESSFUL
Total time: 0 seconds


Timestamp
tstamp 태그를 이용한다.
참고: http://ant.apache.org/manual/Tasks/tstamp.html
예)
<tstamp>
    <format property="TODAY_UK" pattern="d-MMMM-yyyy" locale="en,UK" />
</tstamp>
또는
<tstamp>
    <format property="today" pattern="yyyy-MM-dd_HHmmss" />
</tstamp>

locale 을 지정하지 않으면 시스템 시간대가 사용된다.

특정 폴더의 파일 목록 출력하기
참고: http://stackoverflow.com/questions/1456852/how-can-i-print-a-fileset-to-a-file-one-file-name-per-line
예)
<fileset id="my-file-list" dir="."></fileset>
    <pathconvert pathsep="${line.separator}" property="files" refid="my-file-list"/>
<echo message="${files}"/>

폴더 만들기
참고: http://ant.apache.org/manual/Tasks/mkdir.html
<mkdir dir="${path}" />

파일 복사하기
copy 태그를 이용한다.
예)
<copy todir="${to-dir}">
    <fileset dir="${from-dir}">
    </fileset>

2012년 3월 19일 월요일

HTTPS 통신

참고 - http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html

프로젝트명: HTTPSPractice


파일 구성:

 - HttpsClientWithCustomCert.java - 사용자가 만든 인증서를 이용한 서버 인증
 - HttpsClientWithDefaultCACert.java - 공인 인증된 인증서로 서버 인증
 - HttpsClientWithoutValidation.java - 인증 없이 서버 접속
 - SimpleHttpsServer.java - 사용자가 만든 인증키를 이용하는 간단한 HTTPS 서버
 - keystore.jks - 서버(SimpleHttpsServer) 에서 사용하는 인증키 저장소
 - truststore.jks - 클라이언트(HttpsClientWithCustomCert) 에서 서버 인증시 사용하는 인증서 저장소


다음과 같은 순서로 설명
1. HttpsURLConnection 을 이용한 접속 (인증 과정 없음)
2. 공인 인증된 인증서로 서버 인증
3. 사용자가 만든 인증서로 서버 인증


1. HttpsURLConnection 을 이용한 접속 (인증 과정 없음)

인증 과정을 결정하는 부분은 SSLContext.init() 함수이다.
아래 소스에는 init() 함수에 모두 null 을 집어 넣음으로써 인증 과정을 무시하고 서버의 내용물을 받는다.

소스:
package com.test.https.practice;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;


/**
 * 
 */
public class HttpsClientWithoutValidation {
 
 /**
  * 
  * @param urlString
  * @throws IOException
  * @throws NoSuchAlgorithmException
  * @throws KeyManagementException
  */
 public void getHttps(String urlString) throws IOException, NoSuchAlgorithmException, KeyManagementException {
  
  // Get HTTPS URL connection
  URL url = new URL(urlString);  
  HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
  
  // Set Hostname verification
  conn.setHostnameVerifier(new HostnameVerifier() {
   @Override
   public boolean verify(String hostname, SSLSession session) {
    // Ignore host name verification. It always returns true.
    return true;
   }
   
  });
  
  // SSL setting
  SSLContext context = SSLContext.getInstance("TLS");
  context.init(null, null, null);  // No validation for now
  conn.setSSLSocketFactory(context.getSocketFactory());
  
  // Connect to host
  conn.connect();
  conn.setInstanceFollowRedirects(true);
  
  // Print response from host
  InputStream in = conn.getInputStream();
  BufferedReader reader = new BufferedReader(new InputStreamReader(in));
  String line = null;
  while ((line = reader.readLine()) != null) {
   System.out.printf("%s\n", line);
  }
  
  reader.close();
 }
 
 /**
  * 
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  HttpsClientWithoutValidation test = new HttpsClientWithoutValidation();
  test.getHttps("https://www.google.com");
 }
}

결과:


2. 공인 인증된 인증서로 서버 인증

SSLContext.init() 함수의 두번째 인자에 X509TrustManager 를 선언하여 집어 넣었다.
그리고 JRE 를 설치하면 기본적으로 [JRE 경로]/lib/security/cacerts 라는 파일명의 공인 인증된 인증서 저장소 파일이 있다.
해당 인증서 저장소를 이용하여 서버의 인증서를 검사하게 하였다.

소스:
package com.test.https.practice;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

/**
 *
 */
public class HttpsClientWithDefaultCACert {
 
 /**
  * 
  * @param urlString
  * @throws IOException
  * @throws NoSuchAlgorithmException
  * @throws KeyManagementException
  */
 public void getHttps(String urlString) throws IOException, NoSuchAlgorithmException, KeyManagementException {
  
  // Get HTTPS URL connection
  URL url = new URL(urlString);  
  HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
  
  // Set Hostname verification
  conn.setHostnameVerifier(new HostnameVerifier() {
   @Override
   public boolean verify(String hostname, SSLSession session) {
    // Ignore host name verification. It always returns true.
    return true;
   }
   
  });
  
  // SSL setting
  SSLContext context = SSLContext.getInstance("TLS");
  context.init(null, new TrustManager[] { new X509TrustManager() {

   @Override
   public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    // client certification check
   }

   @Override
   public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    
    // Server certification check
    
    try {
     
     // Get trust store
     KeyStore trustStore = KeyStore.getInstance("JKS");
     String cacertPath = System.getProperty("java.home") + "/lib/security/cacerts"; // Trust store path should be different by system platform.
     trustStore.load(new FileInputStream(cacertPath), "changeit".toCharArray()); // Use default certification validation
     
     // Get Trust Manager
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
     tmf.init(trustStore);
     TrustManager[] tms = tmf.getTrustManagers();
     ((X509TrustManager)tms[0]).checkServerTrusted(chain, authType);
     
    } catch (KeyStoreException e) {
     e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
    
   }

   @Override
   public X509Certificate[] getAcceptedIssuers() {
    return null;
   }
  } }, null);
  conn.setSSLSocketFactory(context.getSocketFactory());
  
  // Connect to host
  conn.connect();
  conn.setInstanceFollowRedirects(true);
  
  // Print response from host
  InputStream in = conn.getInputStream();
  BufferedReader reader = new BufferedReader(new InputStreamReader(in));
  String line = null;
  while ((line = reader.readLine()) != null) {
   System.out.printf("%s\n", line);
  }
  
  reader.close();
 }
 
 /**
  * 
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  HttpsClientWithDefaultCACert test = new HttpsClientWithDefaultCACert();
  test.getHttps("https://www.google.com");
 }
}

결과:


3. 사용자가 만든 인증서로 서버 인증

우선 인증키 저장소와 인증서 저장소를 만들어 보자.
Portecle 라는 툴을 사용했다.

3.1. 인증키 저장소 만들기(keystore.jks)
3.2. 인증키에서 인증서 추출
3.3. 인증서 저장소 만들기(truststore.jks)

서버는 지정된 인증키 저장소를 이용해 클라이언트에 자신을 인증하는 키를 보내게 된다. 인증과정이 성공하면 클라이언트에 html 내용물을 전달하고 종료한다.
클라이언트는 지정된 인증서 저장소를 이용해 서버를 인증한다. 인증과정이 성공하면 서버로 부터 html 내용물을 전달받는다.

서버 소스:
참고 - http://www.java2s.com/Tutorial/Java/0490__Security/SSLContextandKeymanager.htm
package com.test.https.practice;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

public class SimpleHttpsServer {
 
 public void run(int port) throws NoSuchAlgorithmException, KeyManagementException, IOException, KeyStoreException, CertificateException, UnrecoverableKeyException {
  
  // create ssl context
  SSLContext context = SSLContext.getInstance("TLS");
  
  // set key store
  KeyStore keyStore = KeyStore.getInstance("JKS");
  keyStore.load(new FileInputStream("keystore.jks"), "changeit".toCharArray());
  KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
  kmf.init(keyStore, "changeit".toCharArray());
  context.init(kmf.getKeyManagers(), null, null);
  
  // create ssl socket
  SSLServerSocketFactory factory = context.getServerSocketFactory();
  SSLServerSocket socket = (SSLServerSocket)factory.createServerSocket(port);
  SSLSocket client = (SSLSocket)socket.accept();
  InputStream in = client.getInputStream();
  OutputStream out = client.getOutputStream();
  
  // read from client
  BufferedReader reader = new BufferedReader(new InputStreamReader(in));
  reader.readLine();
  
  // write to client
  BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
  writer.write("HTTP/1.0 200 OK");
  writer.newLine();
  writer.write("Content-Type: text/html");
  writer.newLine();
  writer.newLine();
  writer.write("<html><head><title>Hello</title></head><body>Hellow!</body></html>");
  writer.flush();
  
  // close
  writer.close();
  reader.close();
  client.close();
 }
 
 /**
  * 
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  SimpleHttpsServer server = new SimpleHttpsServer();
  server.run(9999);
 }
}


클라이언트 소스:
package com.test.https.practice;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class HttpsClientWithCustomCert {
 
 /**
  * 
  * @param urlString
  * @throws IOException
  * @throws NoSuchAlgorithmException
  * @throws KeyManagementException
  */
 public void getHttps(String urlString) throws IOException, NoSuchAlgorithmException, KeyManagementException {
  
  // Get HTTPS URL connection
  URL url = new URL(urlString);  
  HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
  
  // Set Hostname verification
  conn.setHostnameVerifier(new HostnameVerifier() {
   @Override
   public boolean verify(String hostname, SSLSession session) {
    // Ignore host name verification. It always returns true.
    return true;
   }
   
  });
  
  // SSL setting
  SSLContext context = SSLContext.getInstance("TLS");
  context.init(null, new TrustManager[] { new X509TrustManager() {

   @Override
   public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    // client certification check
   }

   @Override
   public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    
    // Server certification check
    
    try {
     
     // Get trust store
     KeyStore trustStore = KeyStore.getInstance("JKS");
     trustStore.load(new FileInputStream("truststore.jks"), "changeit".toCharArray()); // Use default certification validation
     
     // Get Trust Manager
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
     tmf.init(trustStore);
     TrustManager[] tms = tmf.getTrustManagers();
     ((X509TrustManager)tms[0]).checkServerTrusted(chain, authType);
     
    } catch (KeyStoreException e) {
     e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
    
   }

   @Override
   public X509Certificate[] getAcceptedIssuers() {
    return null;
   }
  } }, null);
  conn.setSSLSocketFactory(context.getSocketFactory());
  
  // Connect to host
  conn.connect();
  conn.setInstanceFollowRedirects(true);
  
  // Print response from host
  InputStream in = conn.getInputStream();
  BufferedReader reader = new BufferedReader(new InputStreamReader(in));
  String line = null;
  while ((line = reader.readLine()) != null) {
   System.out.printf("%s\n", line);
  }
  
  reader.close();
 }
 
 /**
  * 
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  HttpsClientWithCustomCert test = new HttpsClientWithCustomCert();
  test.getHttps("https://127.0.0.1:9999");
 }
}

결과:

stdin 입력

import java.io.*;

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine();

System.out.println(line);


2012년 1월 14일 토요일

XML 파싱하기

참고 - http://www.w3.org/TR/REC-xml/
(Extensible Markup Language (XML) 1.0 (Fifth Edition))


XML 파서의 종류는 SAX 파서와 DOM 파서가 있다.

SAX 파서와 DOM 파서의 차이점:

참고 - http://www.ibiblio.org/xml/books/xmljava/chapters/ch09s11.html
(Choosing between SAX and DOM)

 > 요약: 어떤 파서를 사용할지는 아래와 같은 상황을 고려하여 선택하는 편이 좋다.

SAX 파서를 사용하는 상황
  • 파싱하려는 XML 크기가 너무 클 때
  • 파싱하려는 XML 중 필요한 정보만 취합하고자 할 때
  • 실시간으로 파싱을 처리하고자 할때

DOM 파서를 사용하는 상황
  • XML 문서안에 넓게 분포된 자료들 한번에 취합하고자 할 때
  • 파싱하려는 XML 문서 구조가 매우 복잡할 때
  • XML 문서를 수정할 때
  • 파싱한 XML 정보를 여러 함수들이 사용하고자 할 때

파서 사용법:

예) RSS 파일을 파싱하는 예

NY times 사이트의 RSS 링크 중 하나를 선택하여 nyfeed.xml 이라는 파일명으로 저장하였다.
RSS 항목들 중 item 항목에 대한 title 과 link 만 추려서 출력한다.


프로젝트 구조는 아래와 같다.

실행 결과는 아래와 같다.

DOM 파서

SAX 파서

nyfeed.xml
아래 링크로 접속하여 소스 보기 한 후 내용을 복사하여 프로젝트 폴더에 nyfeed.xml 파일로 저장
http://feeds.nytimes.com/nyt/rss/World

XMLParseTest.java
같은 일을 하는 함수 두개를 만들었다. domParseTest() 와 saxParseTest() 함수이다.

1. nyfeed.xml 파일을 읽어 파싱한다.
2. item 항목의 title 과 link 내용을 출력한다.

package xml.parse;

import java.io.File;

import javax.xml.parsers.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * XMLParserTest
 */
public class XMLParserTest {
 
 private final String XML_FILE_PATH = "nyfeed.xml";

 /**
  * 
  * @param args
  * @throws Exception
  */
 public static void main(String[] args) throws Exception {
  XMLParserTest xpt = new XMLParserTest();
  xpt.domParseTest();
  xpt.saxParseTest();
 }
 
 /**
  * 
  * @throws Exception
  */
 public void domParseTest() throws Exception {

  System.out.println("==============================");
  System.out.println("domParseTest()");
  System.out.println("==============================");
  
  File xmlFile = new File(XML_FILE_PATH);
  
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  DocumentBuilder db = dbf.newDocumentBuilder();
  Document doc = db.parse(xmlFile);
  
  doc.getDocumentElement().normalize();
  
  System.out.printf("Root element:%s\n", doc.getDocumentElement().getNodeName());
  NodeList itemNodeList = doc.getElementsByTagName("item");
  
  for (int s = 0; s < itemNodeList.getLength(); s++) {

   Node itemNode = itemNodeList.item(s);

   if (itemNode.getNodeType() == Node.ELEMENT_NODE) {

    Element itemElement = (Element)itemNode;
    
    NodeList titleNodeList = itemElement.getElementsByTagName("title");
    Element titleElement = (Element)titleNodeList.item(0);
    NodeList childTitleNodeList = titleElement.getChildNodes();
    System.out.printf("[title : %s]\n", ((Node)childTitleNodeList.item(0)).getNodeValue());
    
    NodeList linkNodeList = itemElement.getElementsByTagName("link");
    Element linkElement = (Element) linkNodeList.item(0);
    NodeList childLinkNodeList = linkElement.getChildNodes();
    System.out.printf("[link : %s]\n", ((Node)childLinkNodeList.item(0)).getNodeValue());
   }

  }
 }
 
 /**
  * 
  * @throws Exception
  */
 public void saxParseTest() throws Exception {
  
  System.out.println("==============================");
  System.out.println("saxParseTest()");
  System.out.println("==============================");
  
  File xmlFile = new File(XML_FILE_PATH);
  
  SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
  DefaultHandler dh = new DefaultHandler() {
   
   private boolean firstElement = true;
   private boolean inItem = false;
   private boolean inTitle = false;
   private boolean inLink = false;
   private StringBuilder characterSB;
   
   @Override
   public void startDocument() throws SAXException {
    System.out.println("startDocument");
    super.startDocument();
   }

   @Override
   public void endDocument() throws SAXException {
    System.out.println("endDocument");
    super.endDocument();
   }

   @Override
   public void startElement(String uri, String localName,
     String qName, Attributes attributes) throws SAXException {
    
    if (firstElement) {
     System.out.printf("Root element:%s\n", qName);
     firstElement = false;
    }
    
    if (qName.equals("item")) {
     inItem = true;
    } else if (qName.equals("title")) {
     inTitle = true;
    } else if (qName.equals("link")) {
     inLink = true;
    }
    
    if (inItem && (inTitle || inLink)) {
     characterSB = new StringBuilder();
    }
    
    super.startElement(uri, localName, qName, attributes);
   }

   @Override
   public void characters(char[] ch, int start, int length)
     throws SAXException {
    
    if (characterSB != null) {
     characterSB.append(handleCharacters(ch, start, length));
    }
    
    super.characters(ch, start, length);
   }

   @Override
   public void endElement(String uri, String localName, String qName)
     throws SAXException {
    
    if (characterSB != null) {
     if (inItem && inTitle) {
      System.out.printf("[title : %s]\n", characterSB.toString());
     } else if (inItem && inLink) {
      System.out.printf("[link : %s]\n", characterSB.toString());
     }
     characterSB = null;
    }
    
    if (qName.equals("item")) {
     inItem = false;
    } else if (qName.equals("title")) {
     inTitle = false;
    } else if (qName.equals("link")) {
     inLink = false;
    }
    
    super.endElement(uri, localName, qName);
   }
   

   /**
    * 
    * @param ch
    * @param start
    * @param end
    * @return
    */
   public String handleCharacters(char[] ch, int start, int length) {
    
    StringBuilder sb = new StringBuilder();
    for (int i = start; i < start + length; i++) {
     sb.append(ch[i]);
    }
    return sb.toString();
   }
  };
  parser.parse(xmlFile, dh);
 }
}


팁)
SAX 파싱 작업 중 더 이상 작업 할 필요가 없을 때는 SAXException 을 날려서 중단하면 된다.
참고 - http://www.ibm.com/developerworks/xml/library/x-tipsaxstop/
(Tip: Stop a SAX parser when you have enough data)

throw new SAXException("Enough!");

2012년 1월 11일 수요일

현재 작업 경로 구하기

1. File 클래스 생성자에 '.' 을 입력하면 현재 작업 경로를 얻을 수 있다.

File cwd = new File(".");
System.out.println(cwd.getAbsoluteFile());

2. System property 에 현재 작업 경로가 저장되어 있다.

System.getProperty("user.dir");

참고 - http://www.roseindia.net/java/example/java/io/GetParentDir.shtml

주의)
첫번째 방법으로 파일을 만들게 되면 parent 를 얻을 수 없는 문제가 있다.
두번째 방법을 이용해서 파일을 만들면 parent 를 얻을 수 있다.