태그 보관물: 자바

파일 타입을 좀 더 정확하게 확인하도록 도와주는 자바 라이브러리

파일을 처리하는 애플리케이션을 개발하면서 많은 경우에 파일 처리는 확장자를 기준으로 파일의 타입을 결정하고 처리하게 된다. 예를 들면, 확장자가 .docx 이면 마이크로소프트 워드 파일이라는 것을 알 수 있고, 이 파일의 타입에 적합하게 처리(오픈 등)한다. 이 블로그의 파일 타입별 확장자 분류.. 포스팅을 확인해 보면, 비교적 자주 사용되는 파일 확장자와 그 확장자를 가지고 있는 파일이 어떤 타입의 파일인지 확인할 수 있다.

파일의 포맷을 인식하는 기본 방법이 확장자를 기준으로 타입을 구분해서 처리하는 방법이지만, 종종 파일의 타입을 알 수 없는 경우가 발생한다. 이런 경우는 다음과 같다.
– 수동으로 파일의 확장자를 수정한다.
– 특정 폴더에 이미지의 썸네일을 확장자 없이 저장(안드로이드에서 종종 볼 수 있다)한다.
– 각종 다운로드로 인해서 발생한다.

일반적으로 위 경우에는 파일을 처리하지 못하거나, 제대로 열 수 없는 애플리케이션을 호출하게 된다. 따라서 좀 더 정확하게 파일을 처리하기 위해서는 파일의 헤더를 분석해서 파일의 타입을 확인하고, 그 타입에 맞는 처리가 필요하다. 그래서, 이 글에서는 자바로 파일의 타입을 정확하게 확인할 수 있는 라이브러리를 확인해 보겠다.

파일의 타입을 확인할 수 있는 자바 라이브러리를 찾아보면 아래의 목록을 확인할 수 있다. 그리고 이 라이브러리는 안드로이드에서도 사용할 수 있다.

  1. SimpleMagic
    1. https://github.com/j256/simplemagic
    2. 의존 : 없음
  2. Java Mime Magic Library
    1. https://github.com/arimus/jmimemagic
    2. 의존 : Apache Common Logging 라이브러리
  3.  Apache Tika
    1. http://tika.apache.org/
    2. 의존 : 많음

이 글에서는 Apache Tika는 제외하고, 1.번과 2.번의 라이브러리를 확인해 보겠다. Apache Tika를 제외한 이유는 의존하는 라이브러리가 너무 많아서이다. 대상은 아래에 있는 5개 타입에 대해서 확인해 봤다.

docx = new java.io.File(".").getCanonicalPath()     + "/files/docx_file";
jpg = new java.io.File(".").getCanonicalPath() + "/files/jpg_file";
odt = new java.io.File(".").getCanonicalPath() + "/files/open_office_odt_file";
pdf = new java.io.File(".").getCanonicalPath() + "/files/pdf_file";
exe = new java.io.File(".").getCanonicalPath() + "/files/prgrep_exe_file";

위에서 파일의 확장자를 수동으로 변경했고, 이 파일에 대해서 포맷을 정확하게 인식하는 것을 테스트 했고, 결과는 다음과 같다.

– SimpleMagic 결과

0 ContentType : word
0 MimeType : application/vnd.openxmlformats-officedocument.wordprocessingml.document
0 File Extension 0 : docx
1 ContentType : jpeg
1 MimeType : image/jpeg
1 File Extension 0 : jpeg
1 File Extension 1 : jpg
1 File Extension 2 : jpe
2 ContentType : opendocument-text
2 MimeType : application/vnd.oasis.opendocument.text
2 File Extension 0 : odt
3 ContentType : pdf
3 MimeType : application/pdf
3 File Extension 0 : pdf
4 Unknown content-type

– Java Mime Magic Library 결과

0 File Ext : zip
0 File Mimetype : application/zip
1 File Ext : jpg
1 File Mimetype : image/jpeg
2 File Ext : zip
2 File Mimetype : application/zip
3 File Ext : pdf
3 File Mimetype : application/pdf
4 File Ext : ???
4 File Mimetype : ???

위 개별 라이브러리의 파일 타입 인식 결과로, 자바를 사용해서 파일의 포맷을 더 정확하게 확인하고 사용하려면, SimpleMagic 라이브러를 사용해서 파일의 타입을 확인하는게 좋겠다. 이 테스트를 위해서 사용한 소스는 다음과 같다.

import java.io.File;
import java.io.IOException;

import net.sf.jmimemagic.Magic;
import net.sf.jmimemagic.MagicMatch;

import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;

public class FileFormatCheckTest {

	static String[] files = null;

	static String docx = "";
	static String jpg = "";
	static String odt = "";
	static String pdf = "";
	static String exe = "";

	static {
		try {
			docx = new java.io.File(".").getCanonicalPath()	+ "/files/docx_file";
			jpg = new java.io.File(".").getCanonicalPath() + "/files/jpg_file";
			odt = new java.io.File(".").getCanonicalPath() + "/files/open_office_odt_file";
			pdf = new java.io.File(".").getCanonicalPath() + "/files/pdf_file";
			exe = new java.io.File(".").getCanonicalPath() + "/files/prgrep_exe_file";

			files = new String[] { docx, jpg, odt, pdf, exe };
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void testA() {
		ContentInfoUtil util = new ContentInfoUtil();
		try {
			ContentInfo info;

			for (int i = 0; i < files.length; i++) {
				info = util.findMatch(files[i]);
				if (info == null) {
					System.out.println(i + " Unknown content-type");
					continue;
				}

				System.out.println(i + " ContentType : " + info.getName());
				System.out.println(i + " MimeType : " + info.getMimeType());

				String[] extensions = info.getFileExtensions();
				if (extensions != null && extensions.length > 0) {
					for (int j = 0; j < extensions.length; j++) {
						System.out.println(i + " File Extension " + j + " : "
								+ extensions[j]);
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void testB() {
		MagicMatch match = null;

		try {
			for (int i = 0; i < files.length; i++) {
				match = Magic.getMagicMatch(new File(files[i]), true, false);

				if (match != null) {
					System.out.println(i + " File Ext  " + " : "
							+ match.getExtension());
					System.out.println(i + " File Mimetype  " + " : "
							+ match.getMimeType());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		// testA();
		testB();
	}

}

프로젝트 다운로드 : FileFormatCheckTest.zip

자바 CountDownLatch를 이용한 동시성 높이기..

각종 환경에서 성능을 높이기 위해서 취하는 방법이 멀티 프로세스와 멀티 쓰레드이다. 자바에서는 성능을 높이기 위해서 기본적으로 멀티 쓰레드의 형태를 취하고 있다. 물론, JNI를 사용해서 Native의 프로세스를 띄울 수 있기도 하다. 자바의 기본 패키지중에서 쓰레드를 사용해서 동시성을 극대화 시키는 각종 자료구조와 클래스들은 java.uti.concurrent 패키지를 확인해 보면 알 수 있다.

수학적인 계산을 제외한, 거의 모든 컴퓨터 성능이슈는 느린 I/O 처리를 어떻게 빠르게 처리하냐가 주된 관심사가 아닐까 한다. 

그래서, 많이 쓰이는 쓰레드의 형태를 살펴보면..
1. 잡을 개별 쓰레드로 위임한다.
2. 잡을 개별 쓰레드가 처리하고 결과를 취합해서, 취합된 결과를 기준으로 처리를 한다.

보통은, 위의 1.번으로 처리를 하는 경우가 많지만, 간혹, 2.번의 형태가 필요한 경우가 발생한다. 이 경우에 보통은 메인 쓰레드에서 잡 쓰레드를 생성하고, 자신은 wait하고 있다가 notify를 받는 구조를 많이 취하게 된다. 바로 이런 형태로 2.번의 상황을 처리할 수 있게 해 주는 클래스가 CountDownLatch이다.

자, 간단하게 예제를 살펴보자.

package net.sjava.example.countdownlatch;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
	static final int max = 10;
	/**
	 * 단일 쓰레드 테스트
	 */
	public static void testSingle() throws Exception {
		long start = System.currentTimeMillis();
		for (long i = 0; i < max; i++) {
			Thread.sleep(1000);
		}

		long elapsedTime = System.currentTimeMillis() - start;
		System.out.println("testSingle elapsed time -> " + elapsedTime);
	}

	/**
	 * CountDownLatch 테스트
	 */
	public static void testCountDownLatch() throws Exception {
		final CountDownLatch latch = new CountDownLatch(max);

		long start = System.currentTimeMillis();
		for (long i = 0; i < max; i++) {
			new Thread(new Worker(latch)).start();
		}

		latch.await();
		long elapsedTime = System.currentTimeMillis() - start;
		System.out.println("testCountDownLatch elapsed time -> " + elapsedTime);
	}

	/**
	 * Job 쓰레드
	 */
	static class Worker implements Runnable {
		private CountDownLatch latch;

		public Worker(CountDownLatch latch) {
			this.latch = latch;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException ex) {
				ex.printStackTrace();
			} finally {
				if (this.latch == null)
					return;

				latch.countDown();
			}
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		testSingle();
		testCountDownLatch();
	}
}

위의 코드의 결과는 아래와 같아서, 너무 극단적이긴 하지만, CountDownLatch의 사용을 위한 쉬운 예가 될 것이다.

testSingle elapsed time -> 10,000
testCountDownLatch elapsed time -> 1,004

자바 Restful 프레임웍 들

자바 기반으로 Restful한 서비스를 쉽게 만들게 하는 프레임웍(Framework)을 살펴보니, 아래의 프레임웍들을 많이 사용하는 것 같다.


Restlet is a lightweight REST framework for Java

Jersey is the open source (under dual CDDL+GPL license), production quality, JAX-RS  (JSR 311) Reference Implementation for building RESTful Web services. But, it is also more than the Reference Implementation. Jersey provides an API  so that developers may extend Jersey to suite their needs.

Apache CXF: CXF helps you build and develop services using frontend programming APIs, like JAX-WS and JAX-RS. These services can speak a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI.

– JBoss RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It strives to be standards based wherever possible, but to also innovate when appropriate.

System.currentTimeMillis() 값을 초/분/시간으로 변경하기

자바에서 시간을 처리하는 방법으로 Date클래스나 Calendar 클래스를 많이 사용한다. 이 외에도 많이 사용하는 것이 System 클래스에 있는 currentTimeMillis() 메서드이다. System.currentTimeMillis()는 현재 시각과 1970년 1월 1일 오전 0시와의 차이를 long 값으로 전달합니다. 이 메서드를 사용해서 전달 받은 값을 초(second), 분(minute), 시간(hour)으로 변환하는 코드이다.

private long curTime = System.currentTimeMillis();

System.out.println("second is "+ curTime / 1000);
System.out.println("minute is "+ curTime / (1000 * 60) );
System.out.println("hour is "+ curTime / (1000 * 60 * 60));


자바 가비지 컬렉션 뷰어(Garbage Collection Viewer)

자바의 가장 큰 장점은 애플리케이션이 사용하는 자원을 관리하지 않아도 자동으로 자원을 해제시키는 가비지 컬렉터의 존재이다. 자바로 개발하는 경우에는 가비지 컬렉션의 기본 지식을 알고 있는 것이 애플리케이션 개발 및 디버깅에 도움을 준다.

그래서 자바 애플리케이션의 메모리의 상태(가비지 컬렉션의 유무와 점유율 등)를 비주얼로 볼 수 있다면, 애플리케이션의 모니터링에 매우 도움이 될 수 있다. 그래서 가비지 컬렉션을 비주얼하게 보여주는 몇 가지 툴을 정리해 봤다.

* http://java.sun.com/performance/jvmstat/
* http://docs.hp.com/en/5991-6757/ch03s04.html
* http://java.sun.com/developer/technicalArticles/Programming/GCPortal/
* http://www.javaperformancetuning.com/tools/gcviewer/index.shtml